diff --git a/.clang-format b/.clang-format index 93ba0f7b0c187..f0eb7d31df8e3 100644 --- a/.clang-format +++ b/.clang-format @@ -54,3 +54,7 @@ UseTab: Never # Do not format protobuf files Language: Proto DisableFormat: true +--- +# Do not format JSON configuration files +Language: Json +DisableFormat: true diff --git a/.clang-format-ignore b/.clang-format-ignore new file mode 100644 index 0000000000000..a6c57f5fb2ffb --- /dev/null +++ b/.clang-format-ignore @@ -0,0 +1 @@ +*.json diff --git a/.clang-tidy b/.clang-tidy index da768906bcc32..080c35ba31df3 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,3 +1,42 @@ +--- +Checks: + - -* + - modernize-avoid-bind + - modernize-deprecated-headers + - modernize-make-shared + - modernize-raw-string-literal + - modernize-redundant-void-arg + - modernize-replace-auto-ptr + - modernize-replace-random-shuffle + - modernize-shrink-to-fit + - modernize-unary-static-assert + - modernize-use-equals-default + - modernize-use-noexcept + - modernize-use-nullptr + - modernize-use-override + - modernize-use-transparent-functors + - modernize-use-uncaught-exceptions + - readability-braces-around-statements + - -clang-diagnostic-vla-cxx-extension CheckOptions: - - key: CheckPathRegex - value: '.*/O2/.*' + # Naming conventions + readability-identifier-naming.ClassCase: CamelCase + readability-identifier-naming.ClassMemberPrefix: m + readability-identifier-naming.ConceptCase: CamelCase + readability-identifier-naming.ConstexprVariableCase: CamelCase + readability-identifier-naming.EnumCase: CamelCase + readability-identifier-naming.EnumConstantCase: CamelCase + readability-identifier-naming.EnumConstantIgnoredRegexp: "^k?[A-Z][a-zA-Z0-9_]*$" # Allow "k" prefix and non-trailing underscores in PDG names. + readability-identifier-naming.FunctionCase: camelBack + readability-identifier-naming.MacroDefinitionCase: UPPER_CASE + readability-identifier-naming.MacroDefinitionIgnoredRegexp: "^[A-Z][A-Z0-9_]*_$" # Allow the trailing underscore in header guards. + readability-identifier-naming.MemberCase: camelBack + readability-identifier-naming.NamespaceCase: lower_case + readability-identifier-naming.ParameterCase: camelBack + readability-identifier-naming.StructCase: CamelCase + readability-identifier-naming.TemplateParameterCase: CamelCase + readability-identifier-naming.TypeAliasCase: CamelCase + readability-identifier-naming.TypedefCase: CamelCase + readability-identifier-naming.TypeTemplateParameterCase: CamelCase + readability-identifier-naming.VariableCase: camelBack +... diff --git a/.cmake-format.py b/.cmake-format.py index 9827eecd329c4..ae092bc09f363 100644 --- a/.cmake-format.py +++ b/.cmake-format.py @@ -66,12 +66,6 @@ "HEADERS": '*', } }, - "o2_target_man_page": { - "kwargs": { - "NAME": '+', - "SECTION": '*', - } - }, "add_root_dictionary": { "kwargs": { "LINKDEF": '+', diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000000..30ad6d8f005b3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +--- +# Dependabot configuration +# Reference: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/async-auto-label.yml b/.github/workflows/async-auto-label.yml new file mode 100644 index 0000000000000..b0a17c7faba26 --- /dev/null +++ b/.github/workflows/async-auto-label.yml @@ -0,0 +1,17 @@ +--- +name: Apply requested async label + +'on': + issue_comment: + types: + - created + - edited + +permissions: {} + +jobs: + apply_async_labels: + name: Apply requested async label + uses: alisw/ali-bot/.github/workflows/async-auto-label.yml@master + permissions: + pull-requests: write # to update labels diff --git a/.github/workflows/clean-test.yml b/.github/workflows/clean-test.yml index a48b6993522eb..b149ae86b991d 100644 --- a/.github/workflows/clean-test.yml +++ b/.github/workflows/clean-test.yml @@ -19,35 +19,28 @@ name: Clean PR checks # Warning: the check_* keys are magic and must consist of the string # "check_" followed by the applicable check name exactly. The # "description" field is only the human-readable label for the input. - 'check_build/O2/o2': - description: build/O2/o2 - type: boolean - default: true - 'check_build/AliceO2/O2/o2/macOS': - description: build/AliceO2/O2/o2/macOS - type: boolean - default: true 'check_build/AliceO2/O2/o2/macOS-arm': description: build/AliceO2/O2/o2/macOS-arm type: boolean default: true - 'check_build/O2/fullCI': + 'check_build/O2/fullCI_slc9': description: build/O2/fullCI type: boolean default: true - 'check_build/O2/o2-cs8': - description: build/O2/o2-cs8 + 'check_build/O2/o2-dataflow-slc9': + description: build/O2/o2-dataflow-slc9 type: boolean default: true - 'check_build/O2/o2-dataflow': - description: build/O2/o2-dataflow + 'check_build/O2/o2/aarch64': + description: build/O2/o2/aarch64 type: boolean default: true - 'check_build/O2/o2-dataflow-cs8': - description: build/O2/o2-dataflow-cs8 + 'check_build/O2/o2_slc9': + description: build/O2/o2_slc9 type: boolean default: true + permissions: {} jobs: diff --git a/.github/workflows/code-transformations.yml b/.github/workflows/code-transformations.yml deleted file mode 100644 index 4b5e55fcc2941..0000000000000 --- a/.github/workflows/code-transformations.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Current refactorings - -on: [pull_request_target] -env: - RATIONALE: "Adapt to new FairLogger API" - REFACTORING: "s|LOGP[(]ERROR|LOGP(error|g;s|LOGP[(]INFO|LOGP(info|g;s|LOGP[(]WARNING|LOGP(warning|g;s|LOGP[(]WARN|LOGP(warn|g;s|LOGP[(]DEBUG|LOGP(debug|;s|LOG[(]ERROR|LOG(error|g;s|LOG[(]INFO|LOG(info|g;s|LOG[(]WARNING|LOG(warning|g;s|LOG[(]WARN|LOG(warn|g;s|LOG[(]DEBUG|LOG(debug|g" - -jobs: - build: - # We need at least 20.04 to install clang-format-11. - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - persist-credentials: false - # We need the history of the dev branch all the way back to where the PR - # diverged. We're fetching everything here, as we don't know how many - # commits back that point is. - fetch-depth: 0 - - - name: Run refactoring - id: run_refactoring - env: - ALIBUILD_GITHUB_TOKEN: ${{secrets.ALIBUILD_GITHUB_TOKEN}} - run: | - set -x - # We need to fetch the other commit. - git fetch origin ${{ github.event.pull_request.base.ref }} \ - pull/${{ github.event.pull_request.number }}/head:${{ github.event.pull_request.head.ref }} - - # We create a new branch which we will use for the eventual PR. - git config --global user.email "alibuild@cern.ch" - git config --global user.name "ALICE Action Bot" - git checkout -b alibot-refactor-${{ github.event.pull_request.number }} ${{ github.event.pull_request.head.sha }} - - # github.event.pull_request.base.sha is the latest commit on the branch - # the PR will be merged into, NOT the commit this PR derives from! For - # that, we need to find the latest common ancestor between the PR and - # the branch we are merging into. - BASE_COMMIT=$(git merge-base HEAD ${{ github.event.pull_request.base.sha }}) - echo "Running refactoring against branch ${{ github.event.pull_request.base.ref }}, with hash ${{ github.event.pull_request.base.sha }}" - COMMIT_FILES=$(git diff --diff-filter d --name-only $BASE_COMMIT) - if [ -z "$COMMIT_FILES" ]; then - echo "No files to check" >&2 - echo clean=true >> "$GITHUB_OUTPUT" - exit 0 - fi - perl -p -i -e "${{ env.REFACTORING }}" $COMMIT_FILES - - if git diff --exit-code; then - echo "Refactoring not needed." - git push --set-upstream https://alibuild:$ALIBUILD_GITHUB_TOKEN@github.com/alibuild/AliceO2.git :alibot-refactoring-${{ github.event.pull_request.number }} -f || true - echo clean=true >> "$GITHUB_OUTPUT" - else - git commit -m "${{ env.RATIONALE }}" -a - git show | cat - git fetch https://github.com/AliceO2Group/AliceO2.git pull/${{ github.event.pull_request.number }}/head - git push --set-upstream https://alibuild:$ALIBUILD_GITHUB_TOKEN@github.com/alibuild/AliceO2.git HEAD:refs/heads/alibot-refactoring-${{ github.event.pull_request.number }} -f - echo clean=false >> "$GITHUB_OUTPUT" - fi - - - name: pull-request - uses: alisw/pull-request@master - with: - source_branch: 'alibuild:alibot-refactoring-${{ github.event.pull_request.number }}' - destination_branch: '${{ github.event.pull_request.head.label }}' - github_token: ${{ secrets.ALIBUILD_GITHUB_TOKEN }} - pr_title: "Please consider the refactoring changes to AliceO2Group/AliceO2#${{ github.event.pull_request.number }}" - pr_body: | - AliceO2Group/AliceO2#${{ github.event.pull_request.number }}" cannot be merged as is. - You should either modify your code according to what is done in this PR, or directly merge this PR in yours. - The rationale for this change is: - ${{ env.RATIONALE }} - - continue-on-error: true # We do not create PRs if the branch is not there. - - - name: Exit with error if the PR is not clean - run: | - case ${{ steps.run_refactoring.outputs.clean }} in - true) echo "PR clean" ; exit 0 ;; - false) echo "PR not clean" ; exit 1 ;; - esac diff --git a/.github/workflows/datamodel-doc.yml b/.github/workflows/datamodel-doc.yml index 8b07cbed9d54b..1dd54790bff76 100644 --- a/.github/workflows/datamodel-doc.yml +++ b/.github/workflows/datamodel-doc.yml @@ -10,20 +10,20 @@ jobs: steps: - name: Checkout O2 - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: path: O2 persist-credentials: false - name: Checkout O2Physics - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: repository: AliceO2Group/O2Physics path: O2Physics persist-credentials: false - name: Checkout documentation - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: repository: AliceO2Group/analysis-framework path: analysis-framework @@ -40,7 +40,7 @@ jobs: git checkout -B auto-datamodel-doc - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v7 with: python-version: 3.x @@ -48,6 +48,7 @@ jobs: run: | python3 -m pip install --user -U numpy nltk python3 -m nltk.downloader -d ~/nltk_data punkt + python3 -m nltk.downloader -d ~/nltk_data punkt_tab - name: Generate documentation run: exec bash -exo pipefail O2/scripts/datamodel-doc/update-datamodel.sh @@ -59,6 +60,7 @@ jobs: run: | # git diff --quiet exits with 1 if any tracked files have changed, and # with 0 otherwise. + set -e if git diff --quiet; then exit # Nothing has changed, so no need to send a PR. fi @@ -68,9 +70,10 @@ jobs: git push -f origin auto-datamodel-doc # Send pull request - # We need to use "hub" ourselves because alisw/pull-request gets + # We need to use "gh" ourselves because alisw/pull-request gets # confused when multiple repos are checked out. - hub pull-request -b AliceO2Group:master -h alibuild:auto-datamodel-doc \ - --no-edit --no-maintainer-edits -m 'Automatic data model update' \ - -m "This update to the data model documentation was automatically created from tonight's O2 dev branch." || - : # If the PR already exists, hub fails, but we've just force-pushed, so we don't need a new PR. + GH_TOKEN="$GITHUB_TOKEN" gh pr create -R AliceO2Group/analysis-framework -B master \ + --no-maintainer-edit -t 'Automatic data model update' -b "This update \ + to the data model documentation was automatically created from \ + tonight's O2 dev branch." || true + # If the PR already exists, hub fails, but we've just force-pushed, so we don't need a new PR. diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 38da67c793799..b1dbaf122b342 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -13,7 +13,7 @@ jobs: run: | sudo apt-get update -y sudo apt-get install -y doxygen doxygen-doc doxygen-latex doxygen-gui graphviz cmake - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 with: ref: "dev" persist-credentials: false diff --git a/.github/workflows/first-timer.yml b/.github/workflows/first-timer.yml index 20b7ee6a070a8..54334d109bd49 100644 --- a/.github/workflows/first-timer.yml +++ b/.github/workflows/first-timer.yml @@ -8,7 +8,7 @@ jobs: nag_first_timer: runs-on: ubuntu-latest steps: - - uses: actions/first-interaction@v1 + - uses: actions/first-interaction@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} pr-message: 'This seems to be your first PR. You will need a positive review in order for tests to start.' diff --git a/.github/workflows/pr-security-approval.yml b/.github/workflows/pr-security-approval.yml new file mode 100644 index 0000000000000..9ae70fdb9933d --- /dev/null +++ b/.github/workflows/pr-security-approval.yml @@ -0,0 +1,18 @@ +--- +name: Security approval + +'on': + pull_request_review: + types: + - edited + - submitted + +permissions: {} + +jobs: + clean: + name: Security approval + uses: alisw/ali-bot/.github/workflows/pr-security-approval.yml@master + permissions: + pull-requests: read # to get last commit for PR + statuses: write # for set-github-status diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 894ff2e0bb49b..2f692527ea5ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: branch=$(echo ${{ github.event.inputs.tag }}-patches | tr . - | sed -e's/-[0-9]*-patches$/-patches/') EOF id: decide_release_branch - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 with: ref: "dev" - name: Tag branch (or create one before tagging if does not exists) diff --git a/.github/workflows/reports.yml b/.github/workflows/reports.yml index 9045b8b8c8031..444ca1002f6ff 100644 --- a/.github/workflows/reports.yml +++ b/.github/workflows/reports.yml @@ -17,12 +17,12 @@ jobs: if: github.repository == 'AliceO2Group/AliceO2' steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.7 - uses: actions/setup-python@v1 + - uses: actions/checkout@v5 + - name: Set up Python 3.10 + uses: actions/setup-python@v7 with: - python-version: 3.7 - - uses: actions/cache@v2 + python-version: '3.10' + - uses: actions/cache@v5 name: Configure pip caching with: path: ~/.cache/pip @@ -81,7 +81,7 @@ jobs: run: | set -x mkdir -p doc/data - # We create new files once per month, mostly so that + # We create new files once per month, mostly so that # we can keep the query results small. It does not # matter if we get results from different months, # as what matters is how we merge them. @@ -96,7 +96,6 @@ jobs: # being published LAST_RELEASE="${{ github.event.inputs.LAST_RELEASE_DATE }}" MERGED_AFTER=${LAST_RELEASE:-$(date -v -14d +%Y-%m-%d)} - # Here we convert all the json files to per subsystem # logs, using the MERGED_AFTER date to further filter them. # Notice we can have duplicates in each file, @@ -106,7 +105,7 @@ jobs: for f in doc/data/*_prs.json; do for x in Algorithm Analysis Common DataFormats Detectors EventVisualisation Examples Framework Generators Steer Testing Utilities; do cat $f | jq ".repository.pullRequests.edges[].node | select(.files.edges[].node.path | test(\"$x\")) | del(.files) | select(.state == \"MERGED\" and .mergedAt >= \"${MERGED_AFTER}\")" > /tmp/${x}_prs.json - if [ ! X`jq -s length /tmp/${x}_prs.json` = X0 ]; then + if [ ! X`jq -s length /tmp/${x}_prs.json` = X0 ]; then cat /tmp/${x}_prs.json | jq -r '"- [#\(.number)](https://github.com/AliceO2Group/AliceO2/pull/\(.number)) \(.mergedAt | split("T")[0]): \(.title) by [@\(.author.login)](https://github.com/\(.author.login))"' | sort -u >> /tmp/${x}_prs.md fi done @@ -127,11 +126,6 @@ jobs: git config --global user.name "GitHub Action Bot" git commit -m "Updated README" -a || echo "No changes to commit" git push origin HEAD:changelog -f - GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \ - hub pull-request -f -b dev -h changelog \ - --no-edit --no-maintainer-edits \ - 'Auto-generated changelog' \ - -m 'The following changelog has been automatically generated.' || - # If the PR already exists, the force-push will have updated it. - # It's fine if this step fails. - true + # If the PR already exists, the force-push will have updated it. + # It's fine if this step fails. + GH_TOKEN=${{ secrets.GITHUB_TOKEN }} gh pr create -B dev -H changelog -t 'Auto-generated changelog' -b 'The following changelog has been automatically generated.' || true diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 1f1387d4868ae..44e9072aabf6f 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,7 +7,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v1 + - uses: actions/stale@v11 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-pr-message: 'This PR did not have any update in the last 30 days. Is it still needed? Unless further action in will be closed in 5 days.' diff --git a/.gitignore b/.gitignore index a489daad80115..3142e97cd74b5 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ compile_commands.json .vscode .ycm_extra_conf.py Session.vim +CMakeLists.txt.user # Datafiles gphysi.dat @@ -81,8 +82,17 @@ bazel-* # direnv .envrc +# git wrappers +.sl + # LSP support on macOS with vim .clangd DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h dpl-config.json O2.code-workspace + +# Python bytecode +*.pyc + +# Claude code +.claude/settings.local.json diff --git a/.skills/build-software-stack/SKILL.md b/.skills/build-software-stack/SKILL.md new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/.skills/build-software-stack/SKILL.md @@ -0,0 +1 @@ + diff --git a/.skills/create-a-new-file/SKILL.md b/.skills/create-a-new-file/SKILL.md new file mode 100644 index 0000000000000..a7af739d49894 --- /dev/null +++ b/.skills/create-a-new-file/SKILL.md @@ -0,0 +1,20 @@ +--- +name: create-a-new-file +description: describes how to create a new file +--- + +## Copyright statements + +The copyright statement for ALICE / O2 is found in ./o2-copyright-statement.md. It should be at the beginning of +the new file using the proper commenting syntax for the given programming language. For example in C++ it should be commented via +multiline comments: + +``` C++ +// Copyright 2019- CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// ... +``` + +The only part which needs to be adapted by you is the `` which you need to replace with the actual current year. + + diff --git a/.skills/create-a-new-file/o2-copyright-statement.md b/.skills/create-a-new-file/o2-copyright-statement.md new file mode 100644 index 0000000000000..0bc4b092e6ba6 --- /dev/null +++ b/.skills/create-a-new-file/o2-copyright-statement.md @@ -0,0 +1,11 @@ +// Copyright 2019- CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + diff --git a/Algorithm/CMakeLists.txt b/Algorithm/CMakeLists.txt index b245562c7cc93..6deed0fb8614b 100644 --- a/Algorithm/CMakeLists.txt +++ b/Algorithm/CMakeLists.txt @@ -11,39 +11,12 @@ o2_add_header_only_library(Algorithm INTERFACE_LINK_LIBRARIES O2::Headers) -o2_target_man_page(Algorithm NAME Algorithm SECTION 3) -o2_target_man_page(Algorithm NAME algorithm_parser SECTION 3) - -o2_add_test(o2formatparser - SOURCES test/o2formatparser.cxx - COMPONENT_NAME Algorithm - PUBLIC_LINK_LIBRARIES O2::Algorithm - LABELS algorithm) - o2_add_test(headerstack SOURCES test/headerstack.cxx COMPONENT_NAME Algorithm PUBLIC_LINK_LIBRARIES O2::Algorithm LABELS algorithm) -o2_add_test(parser - SOURCES test/parser.cxx - COMPONENT_NAME Algorithm - PUBLIC_LINK_LIBRARIES O2::Algorithm - LABELS algorithm) - -o2_add_test(tableview - SOURCES test/tableview.cxx - COMPONENT_NAME Algorithm - PUBLIC_LINK_LIBRARIES O2::Algorithm - LABELS algorithm) - -o2_add_test(pageparser - SOURCES test/pageparser.cxx - COMPONENT_NAME Algorithm - PUBLIC_LINK_LIBRARIES O2::Algorithm - LABELS algorithm) - o2_add_test(mpl_tools SOURCES test/test_mpl_tools.cxx COMPONENT_NAME Algorithm diff --git a/Algorithm/doc/Algorithm.3.in b/Algorithm/doc/Algorithm.3.in deleted file mode 100644 index eaf618ee68da2..0000000000000 --- a/Algorithm/doc/Algorithm.3.in +++ /dev/null @@ -1,12 +0,0 @@ -.\" Alice O2 manpage for module Algorithm -.TH "AliceO2" 3 "17 Jan 2017" "1.0" "Algorithm man page" - -.SH NAME -AliceO2 - module -.B Algorithm - -.SH DESCRIPTION -A collection of generic algorithms for Alice O2 - -.SH SEE ALSO -algorithm_parser(3) diff --git a/Algorithm/doc/algorithm_parser.3.in b/Algorithm/doc/algorithm_parser.3.in deleted file mode 100644 index 98f45df279669..0000000000000 --- a/Algorithm/doc/algorithm_parser.3.in +++ /dev/null @@ -1,135 +0,0 @@ -.\" Alice O2 manpage for parser algorithms -.TH "AliceO2" 3 "17 Jan 2017" "1.0" "Algorithm Parser man page" - -.SH NAME -AliceO2 - module -.B Algorithm -- data parsers - -.SH SYNOPSIS -.B ForwardParser< -.I SomeHeaderType -, -.I SomeTrailerType -.B > - -.B ReverseParser< -.I SomeHeaderType -, -.I SomeTrailerType -.B > - -.SS Public types -.TP 2 -// a compound of header, data, and trailer -.B struct FrameInfo { - using PtrT = const PayloadType*; - const HeaderType* header = nullptr; - const TrailerType* trailer = nullptr; - PtrT payload = nullptr; - size_t length = 0; - -.B }; - -.TP 2 -.B using CheckHeaderFct = std::function; -alias for callback checking the header, return true if the object is a valid header -.TP 2 -.B using CheckTrailerFct = std::function; -alias for callback checking the trailer -.TP 2 -.B using GetFrameSizeFct = std::function; -alias for callback to get the complete frame size including header, trailer and the data -.TP 2 -.B using InsertFct = std::function; -function callback to insert/handle one frame into, sequentially called for all frames if the whole block has a valid format - -.SS Public member functions -.TP 2 -.B template -.B int parse(const InputType* \fIbuffer\fB, size_t \fIbufferSize\fB, CheckHeaderFct \fIcheckHeader\fB, CheckTrailerFct \fIcheckTrailer\fB, GetFrameSizeFct \fIgetFrameSize\fB, InsertFct \fIinsert\fB) - -.SS Public member variables -.TP 2 -.B static const size_t headOffset = typesize::size; -the length offset due to header -.TP 2 -.B static const size_t tailOffset = typesize::size; -the length offset due to trailer -.TP 2 -.B static const size_t totalOffset = headOffset + tailOffset; -total length offset due to header and trailer - -.SH DESCRIPTION -Template utilities for parsing of data sequences. Each entry in the sequence consist of a header, variable payload, and optionally a trailer. The three parts are collected in the FrameInfo structure for every entry. - -Callback functions for checking header and trailer integrity, getting length of the current frame and handling of a frame. - -.SS ForwardParser -The size is expected to be part of the header, parsing starts at beginning of buffer. -Trailer type can be void, which is also the default template parameter. That -allows to define a frame consisting of only header and data. - -.SS ReverseParser -The size is expected to be part of the trailer, the parsing is thus in reverse direction. Also the insert callback is called with the entries starting form the end of the buffer. -An easy extension can be to reverse the order of the inserts, meaning that the entries are read from the beginning. - -.SH EXAMPLES -.SS ReverseParser example -.EX -using SomeParser = ReverseParser; -SomeParser parser; -std::vector frames; -parser.parse(ptr, size, - [] (const typename SomeParser::HeaderType& h) { - // check the header - return true; - }, - [] (const typename SomeParser::TrailerType& t) { - // check the trailer - return true; - }, - [] (const typename SomeParser::TrailerType& t) { - // get the size of the frame including payload - // and header and trailer size, e.g. payload size - // from a trailer member - return t.payloadSize + SomeParser::totalOffset; - }, - [&frames] (typename SomeParser::FrameInfo& info) { - frames.emplace_back(info); - return true; - } - ) -.EE - -.SS ForwardParser example with frame consisting of header and payload -.EX -using SomeParser = ForwardParser; -SomeParser parser; -std::vector frames; -parser.parse(ptr, size, - [] (const typename SomeParser::HeaderType& h) { - // check the header - return true; - }, - [] (const typename SomeParser::HeaderType& h) { - // get the size of the frame including payload - // and header and trailer size, e.g. payload size - // from a header member - return h.payloadSize + SomeParser::totalOffset; - }, - [&frames] (typename SomeParser::FrameInfo& info) { - frames.emplace_back(info); - return true; - } - ) -.EE - -.SH BUGS, CONTRIBUTIONS -Please add an issue to -.UR https://github.com/AliceO2Group/AliceO2/issues -.UE - -.SH SEE ALSO -.UR https://github.com/AliceO2Group/AliceO2/blob/dev/Algorithm/include/Algorithm/Parser.h -.UE diff --git a/Algorithm/include/Algorithm/BitstreamReader.h b/Algorithm/include/Algorithm/BitstreamReader.h deleted file mode 100644 index 0a112183ab5ef..0000000000000 --- a/Algorithm/include/Algorithm/BitstreamReader.h +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef BITSTREAMREADER_H -#define BITSTREAMREADER_H - -/// @file BitstreamReader.h -/// @author Matthias Richter -/// @since 2019-06-05 -/// @brief Utility class to provide bitstream access to an underlying resource - -#include -#include - -namespace o2 -{ -namespace algorithm -{ - -/// @class BitStreamReader -/// @brief Utility class to provide bitstream access to an underlying resource -/// -/// Allows to access bits of variable length, supports integral types and also -/// bitsets as target type. At the moment, the access is in direction MSB -> LSB. -/// -/// BitstreamReader reader(start, end); -/// while (reader.good() && not reader.eof()) { -/// // get an 8 bit value from the stream, moves the position -/// uint8_t ivalue; -/// reader.get(ivalue); -/// -/// // get a 13 bit bitset without moving the position -/// std::bitset<13> value; -/// reader.peek(value, value.size()); -/// // e.g. use 7 bits of the data -/// value >>= value.size() - 7; -/// // move position by the specific number of bits -/// reader.seek(7); -/// } -template -class BitstreamReader -{ - public: - using self_type = BitstreamReader; - // for the moment we simply use pointers, but with some traits this can be extended to - // containers - using value_type = BufferType; - using iterator = const value_type*; - static constexpr size_t value_size = sizeof(value_type) * 8; - BitstreamReader() = delete; - BitstreamReader(iterator start, iterator end) - : mStart(start), mEnd(end), mCurrent(mStart), mBitPosition(value_size) - { - } - ~BitstreamReader() = default; - - /// Check reader's state - /// @return true if not in error state - bool good() const - { - return mBitPosition > 0; - } - - /// Indicates end of data - /// @return true if end of resource is reached - bool eof() const - { - return mCurrent == mEnd && mBitPosition > 0; - } - - /// Reset the reader, start over at beginning - void reset() - { - mCurrent = mStart; - mBitPosition = value_size; - } - - /// Get the next N bits without moving the read position - /// if bitlength is smaller than the size of data type, result is aligned to LSB - /// TODO: this also works nicely for bitsets, but then the bitlength has to be specified - /// as template parameter, want to do a specific overload, but needs more work to catch - /// all cases. - /// @param v target variable passed by reference - /// @return number of poked bits - template - size_t peek(T& v) - { - static_assert(N <= sizeof(T) * 8); - return peek(v, N); - } - - /// Get the next n bits without moving the read position - /// if bitlength is smaller than the size of data type, result is aligned to LSB - /// @param v target variable passed by reference - /// @param bitlength number of bits to read - /// @return number of poked bits - template - size_t peek(T& v, size_t bitlength) - { - return peek(v, bitlength); - } - - /// Move read position - /// @param bitlength move count in number of bits - void seek(size_t bitlength) - { - while (good() && bitlength > 0 && mCurrent != mEnd) { - if (bitlength >= mBitPosition) { - bitlength -= mBitPosition; - mBitPosition = 0; - } else { - mBitPosition -= bitlength; - bitlength = 0; - } - if (mBitPosition == 0) { - mCurrent++; - mBitPosition = value_size; - } - } - - if (bitlength > 0) { - mBitPosition = 0; - } - } - - /// Get the next n bits and move the read position - template - T get() - { - T result; - peek(result); - seek(N); - return result; - } - - /// Get the next n and move the read position - template - T get(size_t bitlength = sizeof(T) * 8) - { - T result; - peek(result, bitlength); - seek(bitlength); - return result; - } - - /// @class Bits - /// @brief Helper class to get value of specified type which holds the number used bits - /// - /// The class holds both the extracted value access via peek method and the number of used - /// bits. The reader will be incremented when the object is destroyed. - /// The number of bits can be adjusted by using markUsed method - template - class Bits - { - public: - using field_type = FieldType; - static_assert(N <= sizeof(FieldType) * 8); - Bits() - : mParent(nullptr), mData(0), mLength(0) - { - } - Bits(ParentType* parent, FieldType&& data) - : mParent(parent), mData(std::move(data)), mLength(N) - { - } - Bits(Bits&& other) - : mParent(other.mParent), mData(std::move(other.mData)), mLength(other.mLength) - { - other.mParent = nullptr; - other.mLength = 0; - } - - ~Bits() - { - if (mParent) { - mParent->seek(mLength); - } - } - - auto& operator=(Bits&& other) - { - mParent = other.mParent; - mData = std::move(other.mData); - mLength = other.mLength; - other.mParent = nullptr; - other.mLength = 0; - - return *this; - } - - FieldType& operator*() - { - return mData; - } - - void markUsed(size_t length) - { - mLength = length; - } - - private: - ParentType* mParent; - FieldType mData; - size_t mLength; - }; - - /// Read an integral value from the stream - template ::value, int> = 0> - self_type& operator>>(T& target) - { - target = get(); - return *this; - } - - /// Read a bitstream value from the stream - template - self_type& operator>>(std::bitset& target) - { - target = get, N>(); - return *this; - } - - /// Read a Bits object from the stream - template - self_type& operator>>(Bits& target) - { - T bitfield; - peek(bitfield); - target = std::move(Bits(this, std::move(bitfield))); - return *this; - } - - private: - /// The internal peek method - template - size_t peek(T& result, size_t bitlength) - { - if constexpr (RuntimeCheck) { - // the runtime check is disabled if bitlength is derived at compile time - if (bitlength > sizeof(T) * 8) { - throw std::length_error(std::string("requested bit length ") + std::to_string(bitlength) + " does not fit size of result data type " + std::to_string(sizeof(T) * 8)); - } - } - result = 0; - size_t bitsToWrite = bitlength; - auto current = mCurrent; - auto bitsAvailable = mBitPosition; - while (bitsToWrite > 0 && current != mEnd) { - // extract available bits - value_type mask = ~value_type(0) >> (value_size - bitsAvailable); - if (bitsToWrite >= bitsAvailable) { - T value = (*current & mask) << (bitsToWrite - bitsAvailable); - result |= value; - bitsToWrite -= bitsAvailable; - bitsAvailable = 0; - } else { - value_type value = (*current & mask) >> (bitsAvailable - bitsToWrite); - result |= value; - bitsAvailable -= bitsToWrite; - bitsToWrite = 0; - } - if (bitsAvailable == 0) { - current++; - bitsAvailable = value_size; - } - } - - return bitlength - bitsToWrite; - } - - /// start of resource - iterator mStart; - /// end of resource - iterator mEnd; - /// current position in resource - iterator mCurrent; - /// bit position in current element - size_t mBitPosition; -}; -} // namespace algorithm -} // namespace o2 -#endif diff --git a/Algorithm/include/Algorithm/O2FormatParser.h b/Algorithm/include/Algorithm/O2FormatParser.h deleted file mode 100644 index d0d820391a331..0000000000000 --- a/Algorithm/include/Algorithm/O2FormatParser.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALGORITHM_O2FORMATPARSER_H -#define ALGORITHM_O2FORMATPARSER_H - -/// @file O2FormatParser.h -/// @author Matthias Richter -/// @since 2017-10-18 -/// @brief Parser for the O2 data format - -#include "HeaderStack.h" - -namespace o2 -{ - -namespace algorithm -{ - -/** - * parse an input list and try to interpret in O2 data format - * O2 format consist of header-payload message pairs. The header message - * always starts with the DataHeader, optionally there can be more - * headers in the header stack. - * - * The following callbacks are mandadory to be provided, e.g. through lambdas - * - insert function with signature (const DataHeader&, ptr, size) - * auto insertFct = [&] (const auto & dataheader, - * auto ptr, - * auto size) { - * // do something with dataheader and buffer - * }; - * - getter for the message pointer, e.g. provided std::pair is used - * auto getPointerFct = [] (const auto & arg) {return arg.first;}; - * - getter for the message size, e.g. provided std::pair is used - * auto getSizeFct = [] (const auto & arg) {return arg.second;}; - * - * Optionally, also the header stack can be parsed by specifying further - * arguments. For every header supposed to be parsed, a pair of a dummy object - * and callback has to be specified, e.g. - * // handler callback for MyHeaderStruct - * auto onMyHeaderStruct = [&] (const auto & mystruct) { - * // do something with mystruct - * }; // end handler callback - * - * parseO2Format(list, insertFct, MyHeaderStruct(), onMyHeaderStruct); - * - */ -template < - typename InputListT, typename GetPointerFctT, typename GetSizeFctT, typename InsertFctT, // (const auto&, ptr, size) - typename... HeaderStackTypes // pairs of HeaderType and CallbackType - > -int parseO2Format(const InputListT& list, - GetPointerFctT getPointer, - GetSizeFctT getSize, - InsertFctT insert, - HeaderStackTypes&&... stackArgs) -{ - const o2::header::DataHeader* dh = nullptr; - for (auto& part : list) { - if (!dh) { - // new header - payload pair, read DataHeader - dh = o2::header::get(getPointer(part), getSize(part)); - if (!dh) { - return -ENOMSG; - } - o2::algorithm::dispatchHeaderStackCallback(getPointer(part), - getSize(part), - stackArgs...); - } else { - insert(*dh, getPointer(part), getSize(part)); - dh = nullptr; - } - } - if (dh) { - return -ENOMSG; - } - return list.size() / 2; -} - -} // namespace algorithm - -} // namespace o2 - -#endif // ALGORITHM_O2FORMATPARSER_H diff --git a/Algorithm/include/Algorithm/PageParser.h b/Algorithm/include/Algorithm/PageParser.h deleted file mode 100644 index e382fc318352e..0000000000000 --- a/Algorithm/include/Algorithm/PageParser.h +++ /dev/null @@ -1,498 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALGORITHM_PAGEPARSER_H -#define ALGORITHM_PAGEPARSER_H - -/// @file PageParser.h -/// @author Matthias Richter -/// @since 2017-09-27 -/// @brief Parser for a set of data objects in consecutive memory pages. - -#include -#include -#include -#include -#include - -namespace o2 -{ - -namespace algorithm -{ - -namespace pageparser -{ -// a function to extract the number of elements from the group type -// this is the version for all but integral types -template -typename std::enable_if::value, size_t>::type - extractNElements(T* v) -{ - return 0; -} - -// the specialization for integral types -template -typename std::enable_if::value, T>::type - extractNElements(T* v) -{ - return *v; -} - -template -using DefaultGetNElementsFctT = size_t (*)(const GroupT*); - -// the default function to extract the number of elements in a group -// where the group header is a single integral type holding number of -// elements -auto defaultGetNElementsFct = [](const auto* groupdata) { - using ReturnType = size_t; - using T = typename std::remove_pointer::type; - // this default function is only for integral types - static_assert(std::is_integral::value || std::is_void::value, - "A function for extracting the number of elements from the " - "group header must be specified for non-trivial types"); - // the default function for trivial integral types means there - // is exactly one number holding the size - return static_cast(extractNElements(groupdata)); -}; - -template -T* alloc() -{ - return new T; -} - -template <> -void* alloc() -{ - return nullptr; -} - -template -void free(T* ptr) -{ - if (ptr) { - delete ptr; - } -} - -template <> -void free(void*) -{ -} - -template -size_t sizeofGroupHeader() -{ - return sizeof(T); -} - -template <> -size_t sizeofGroupHeader() -{ - return 0; -} - -template -void set(T* h, size_t v) -{ - *h = v; -} - -template <> -void set(void*, size_t) -{ -} -} // namespace pageparser - -/** - * @class PageParser - * Parser for a set of data objects in consecutive memory pages. - * - * All memory pages have a fixed size and start with a page header. - * Depending on the page size and size of the data object, some - * objects can be split at the page boundary and have the page header - * embedded. - * - * The class iterator can be used to iterate over the data objects - * transparently. - * - * In addition data elements can be grouped. In that case a group - * header comes immediately after the first page header. The header - * has to store the number of elements which follow, a getter function - * has to be provided to retrieve the number from the header. - * - * In the most simple case, the group header consists of just one - * element of arbitrary integral type, the parser implements a default - * getter function to retrieve that number. The parser can be invoked - * by simply specifying an integral type as GroupT template parameter. - * - * Multiple blocks of grouped data elements can be in the group, a block - * can wrap over page boundery. A new block of grouped elements can - * however only start in a new page right after the page header. - * - * Usage: ungrouped elements - * RawParser RawParser; - * RawParser parser(ptr, size); - * for (auto element : parser) { - * // do something with element - * } - * - * Usage: grouped elements - * RawParser RawParser; - * RawParser parser(ptr, size); - * for (auto element : parser) { - * // do something with element - * } - */ -template > -class PageParser -{ - public: - using PageHeaderType = PageHeaderT; - using BufferType = unsigned char; - using value_type = ElementT; - using GroupType = GroupT; - using GetNElements = GetNElementsFctT; - static const size_t page_size = PageSize; - - // at the moment an object can only be split among two pages - static_assert(PageSize >= sizeof(PageHeaderType) + sizeof(value_type), - "Page Header and at least one element have to fit into page"); - - // switches for the copy method, used to skip ill-formed expressions - using TargetInPageBuffer = std::true_type; - using SourceInPageBuffer = std::false_type; - - PageParser() = delete; - template - PageParser(T* buffer, size_t size, - GetNElements getNElementsFct = pageparser::defaultGetNElementsFct) - : mBuffer(nullptr), mBufferIsConst(std::is_const::value), mSize(size), mGetNElementsFct(getNElementsFct), mNPages(size > 0 ? ((size - 1) / page_size) + 1 : 0), mGroupHeader(pageparser::alloc()) - { - static_assert(sizeof(T) == sizeof(BufferType), - "buffer required to be byte-type"); - - // the buffer pointer is stored non-const, a runtime check ensures - // that iterator write works only for non-const buffers - mBuffer = const_cast(buffer); - } - ~PageParser() - { - pageparser::free(mGroupHeader); - } - - template - class Iterator - { - public: - using ParentType = PageParser; - using SelfType = Iterator; - using iterator_category = std::forward_iterator_tag; - using value_type = T; - using reference = T&; - using pointer = T*; - using difference_type = std::ptrdiff_t; - using ElementType = typename std::remove_const::type; - - Iterator() = delete; - - Iterator(ParentType const* parent, size_t position = 0) - : mParent(parent) - { - mPosition = position; - size_t argument = mPosition; - if (!mParent->getElement(argument, mElement)) { - // eof, both mPosition and mNextPosition point to buffer end - mPosition = argument; - } - mNextPosition = argument; - backup(); - } - ~Iterator() - { - sync(); - } - - // prefix increment - SelfType& operator++() - { - sync(); - mPosition = mNextPosition; - size_t argument = mPosition; - if (!mParent->getElement(argument, mElement)) { - // eof, both mPosition and mNextPosition point to buffer end - mPosition = argument; - } - mNextPosition = argument; - backup(); - return *this; - } - // postfix increment - SelfType operator++(int /*unused*/) - { - SelfType copy(*this); - operator++(); - return copy; - } - // return reference - reference operator*() - { - return mElement; - } - // comparison - bool operator==(const SelfType& rh) - { - return mPosition == rh.mPosition; - } - // comparison - bool operator!=(const SelfType& rh) - { - return mPosition != rh.mPosition; - } - - const GroupType* getGroupHeader() const - { - return mParent->getGroupHeader(); - } - - private: - // sync method for non-const iterator - template - typename std::enable_if::value, U>::type sync() - { - if (std::memcmp(&mElement, &mBackup, sizeof(value_type)) != 0) { - // mElement is changed, sync to buffer - mParent->setElement(mPosition, mElement); - } - } - - // overload for const_iterator, empty function body - template - typename std::enable_if::value, U>::type sync() - { - } - - // backup for non-const iterator - template - typename std::enable_if::value, U>::type backup() - { - mBackup = mElement; - } - - // overload for const_iterator, empty function body - template - typename std::enable_if::value, U>::type backup() - { - } - - int mPosition; - int mNextPosition; - ParentType const* mParent; - ElementType mElement; - ElementType mBackup; - }; - - /// set an object at position - size_t setElement(size_t position, const value_type& element) const - { - // write functionality not yet implemented for grouped elements - assert(std::is_void::value); - // check if we are at the end - if (position >= mSize) { - assert(position == mSize); - return mSize; - } - - // check if there is space for one element - if (position + sizeof(value_type) > mSize) { - // format error, probably throw exception - return mSize; - } - - auto source = reinterpret_cast(&element); - auto target = mBuffer + position; - return position + copy(source, target, page_size - (position % page_size)); - } - - template - size_t readGroupHeader(size_t position, T* groupHeader) const - { - assert((position % page_size) == sizeof(PageHeaderType)); - if (std::is_void::value) { - return 0; - } - - memcpy(groupHeader, mBuffer + position, pageparser::sizeofGroupHeader()); - return mGetNElementsFct(groupHeader); - } - - /// retrieve an object at position - bool getElement(size_t& position, value_type& element) const - { - // check if we are at the end - if (position >= mSize) { - assert(position == mSize); - position = mSize; - return false; - } - - // handle group if defined - if (!std::is_void::value) { - if (mNGroupElements == 0) { - // new group has to be read from the buffer - do { - if ((position % page_size) == 0) { - position += sizeof(PageHeaderType); - } - if ((position % page_size) != sizeof(PageHeaderType)) { - // forward to the next page - position += page_size - (position % page_size) + sizeof(PageHeaderType); - if (position > mSize) { - //this is probably a valid condition as the group header can just - //indicate zero clusters - //throw std::runtime_error(""); - position = mSize; - return false; - } - } - const_cast(this)->mNGroupElements = readGroupHeader(position, mGroupHeader); - position += pageparser::sizeofGroupHeader(); - } while (mNGroupElements == 0); - - size_t nPages = 0; - size_t required = pageparser::sizeofGroupHeader() + mNGroupElements * sizeof(value_type); - do { - // the block of elements can go beyond the current page, find out - // how many additional pages are required - required += sizeof(PageHeaderType); - ++nPages; - } while (required > nPages * page_size); - required -= sizeof(PageHeaderType) + pageparser::sizeofGroupHeader(); - if (position + required > mSize) { - throw std::runtime_error( - "format error: the number of group elements " - "does not fit into the remaining buffer"); - } - } - // now we will read one element - const_cast(this)->mNGroupElements -= 1; - ; - } - - // check if there is space for one element - if (position + sizeof(value_type) > mSize) { - // FIXME: not sure if this is considered an error condition if - // no groups are used, i.e. the buffer should have the correct size - // and no extra space after the last element - position = mSize; - return false; - } - - auto source = mBuffer + position; - auto target = reinterpret_cast(&element); - position += copy(source, target, page_size - (position % page_size)); - return true; - } - - // copy data, depending on compile time switch, either source or target - // pointer are treated as pointer in the raw page, i.e. can be additionally - // incremented by the page header - template - size_t copy(const BufferType* source, BufferType* target, size_t pageCapacity) const - { - size_t position = 0; - auto copySize = sizeof(value_type); - // choose which of the pointers needs additional PageHeader offsets - auto pageOffsetTarget = SwitchT::value ? &target : const_cast(&source); - if (pageCapacity == page_size) { - // skip the page header at beginning of page - position += sizeof(PageHeaderType); - pageCapacity -= sizeof(PageHeaderType); - *pageOffsetTarget += sizeof(PageHeaderType); - } - if (copySize > pageCapacity) { - // object is split at the page boundary, copy the part - // in the current page first - copySize = pageCapacity; - } - if (copySize > 0) { - memcpy(target, source, copySize); - position += copySize; - source += copySize; - target += copySize; - } - copySize = sizeof(value_type) - copySize; - if (copySize > 0) { - // skip page header at beginning of new page and copy - // remaining part of the element - position += sizeof(PageHeaderType); - *pageOffsetTarget += sizeof(PageHeaderType); - memcpy(target, source, copySize); - position += copySize; - } - return position; - } - - const GroupType getGroupHeader() const - { - return mGroupHeader; - } - - using iterator = Iterator; - using const_iterator = Iterator; - - const_iterator begin() const - { - return const_iterator(this, 0); - } - - const_iterator end() const - { - return const_iterator(this, mSize); - } - - iterator begin() - { - if (mBufferIsConst) { - // did not find a way to do this at compile time in the constructor, - // probably one needs to make the buffer type a template parameter - // to the class - throw std::runtime_error("the underlying buffer is not writeable"); - } - return iterator(this, 0); - } - - iterator end() - { - return iterator(this, mSize); - } - - private: - BufferType* mBuffer = nullptr; - bool mBufferIsConst = false; - size_t mSize = 0; - GetNElements mGetNElementsFct = nullptr; - size_t mNPages = 0; - GroupType* mGroupHeader = nullptr; - size_t mNGroupElements = 0; -}; - -} // namespace algorithm -} // namespace o2 - -#endif diff --git a/Algorithm/include/Algorithm/Parser.h b/Algorithm/include/Algorithm/Parser.h deleted file mode 100644 index a2a7468621b4c..0000000000000 --- a/Algorithm/include/Algorithm/Parser.h +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALGORITHM_PARSER_H -#define ALGORITHM_PARSER_H - -/// @file Parser.h -/// @author Matthias Richter -/// @since 2017-09-20 -/// @brief Utilities for parsing of data sequences - -#include -#include - -namespace o2 -{ - -namespace algorithm -{ - -/// helper function returning size of type with a specialization for -/// void returning 0 -template -struct typesize { - static const size_t size = sizeof(T); -}; -// specialization for void -template <> -struct typesize { - static const size_t size = 0; -}; - -/** - * @class ForwardParser - * Parser for a sequence of frames with header, trailer and variable payload. - * The size is expected to be part of the header. - * - * Trailer type can be void, which is also the default template parameter. That - * allows to define a frame consisting of only header and data. - * - * Usage: - *
- *   using SomeParser = ForwardParser;
- *   SomeParser parser;
- *   std::vector frames;
- *   parser.parse(ptr, size,
- *                [] (const typename SomeParser::HeaderType& h) {
- *                  // check the header
- *                  return true;
- *                },
- *                [] (const typename SomeParser::TrailerType& t) {
- *                  // check the trailer
- *                  return true;
- *                },
- *                [] (const typename SomeParser::HeaderType& h) {
- *                  // get the size of the frame including payload
- *                  // and header and trailer size, e.g. payload size
- *                  // from a header member
- *                  return h.payloadSize + SomeParser::totalOffset;
- *                },
- *                [&frames] (typename SomeParser::FrameInfo& info) {
- *                  frames.emplace_back(info);
- *                  return true;
- *                }
- *                )
- *
- *   // a reduced version without trailer check callback
- *   using SomeParser = ForwardParser;
- *   SomeParser parser;
- *   std::vector frames;
- *   parser.parse(ptr, size,
- *                [] (const typename SomeParser::HeaderType& h) {
- *                  // check the header
- *                  return true;
- *                },
- *                [] (const typename SomeParser::HeaderType& h) {
- *                  // get the size of the frame including payload
- *                  // and header and trailer size, e.g. payload size
- *                  // from a header member
- *                  return h.payloadSize + SomeParser::totalOffset;
- *                },
- *                [&frames] (typename SomeParser::FrameInfo& info) {
- *                  frames.emplace_back(info);
- *                  return true;
- *                }
- *                )
- * 
- */ -template -class ForwardParser -{ - public: - using HeaderType = HeaderT; - using TrailerType = TrailerT; - using PayloadType = unsigned char; - - /// @struct FrameInfo - /// a compound of header, data, and trailer - struct FrameInfo { - using PtrT = const PayloadType*; - - const HeaderType* header = nullptr; - const TrailerType* trailer = nullptr; - PtrT payload = nullptr; - size_t length = 0; - }; - - /// the length offset due to header - static const size_t headOffset = typesize::size; - /// the length offset due to trailer - static const size_t tailOffset = typesize::size; - /// total length offset due to header and trailer - static const size_t totalOffset = headOffset + tailOffset; - - /// alias for callback checking the header, return true if the object - /// is a valid header - using CheckHeaderFct = std::function; - - /// alias for the argument type to be used in the CheckTrailer function - /// have to forward to a valid type in case of void TrailerType in order - /// to allow passing by reference - using CheckTrailerFctArgumentT = typename std::conditional< - !std::is_void::value, TrailerType, int>::type; - - /// alias for callback checking the trailer, takes reference to trailer - /// object if TrailerType is a valid type, no argument otherwise - template - using CheckTrailerFct = typename std::conditional< - !std::is_void::value, - std::function, - std::function>::type; - - /// alias for callback to get the complete frame size including header, - /// trailer and the data - using GetFrameSizeFct = std::function; - - /// function callback to insert/handle one frame into, sequentially called - /// for all frames if the whole block has a valid format - using InsertFct = std::function; - - /// Parse buffer of size bufferSize, requires callbacks to check header - /// trailer, the frame size, and insert callback to handle a FrameInfo - /// object. - template - int parse(const InputType* buffer, size_t bufferSize, - CheckHeaderFct checkHeader, - CheckTrailerFct checkTrailer, - GetFrameSizeFct getFrameSize, - InsertFct insert) - { - static_assert(sizeof(InputType) == 1, - "ForwardParser currently only supports byte type buffer"); - if (buffer == nullptr || bufferSize == 0) { - return 0; - } - - size_t position = 0; - std::vector frames; - do { - FrameInfo entry; - - // check the header - if (sizeof(HeaderType) + position > bufferSize) { - break; - } - entry.header = reinterpret_cast(buffer + position); - if (!checkHeader(*entry.header)) { - break; - } - - // extract frame size from header, this is expected to be the - // total frome size including header, payload and optional trailer - auto frameSize = getFrameSize(*entry.header); - if (frameSize + position > bufferSize) { - break; - } - - // payload starts right after the header - entry.payload = reinterpret_cast(entry.header + 1); - entry.length = frameSize - totalOffset; - - // optionally extract and check trailer - if (tailOffset > 0) { - entry.trailer = nullptr; - } else { - auto trailerStart = buffer + position + frameSize - tailOffset; - entry.trailer = reinterpret_cast(trailerStart); - if (!CheckTrailer(entry, checkTrailer)) { - break; - } - } - - // store the extracted frame info and continue with remaining buffer - frames.emplace_back(entry); - position += frameSize; - } while (position < bufferSize); - - if (position == bufferSize) { - // frames found and format consistent, insert entries to target - // Note: the complete block must be consistent - for (auto entry : frames) { - if (!insert(entry)) { - break; - } - } - return frames.size(); - } else if (frames.size() == 0) { - // no frames found at all, the buffer does not contain any - return 0; - } - - // format error detected - // TODO: decide about error policy - return -1; - } - - /// Parse buffer of size bufferSize, specialization skipping the trailer - /// check, e.g. when its type is void, or when the integrity of the trailer - /// is not relevant. Requires callbacks to check header, frame size, and - /// insert callback to handle a FrameInfo object. - template - typename std::enable_if::value, int>::type - parse(const InputType* buffer, size_t bufferSize, - CheckHeaderFct checkHeader, - GetFrameSizeFct getFrameSize, - InsertFct insert) - { - auto checkTrailer = []() { return true; }; - return parse(buffer, bufferSize, checkHeader, checkTrailer, getFrameSize, insert); - } - - private: - /// internal function to check the trailer, distinguishes void and non-void - /// trailer type. - template - typename std::enable_if::value, bool>::type - CheckTrailer(const FrameInfo& entry, CheckTrailerFct& checkTrailer) const - { - return checkTrailer(*entry.trailer); - } - - template - typename std::enable_if::value, bool>::type - CheckTrailer(const FrameInfo&, CheckTrailerFct&) const - { - return true; - } -}; - -/** - * @class ReverseParser - * Parser for a sequence of frames with header, trailer and variable payload. - * The size is expected to be part of the trailer, the parsing is thus in - * reverse direction. Also the insert callback is called with the entries - * starting form the end of the buffer. - * TODO: an easy extension can be to reverse the order of the inserts, meaning - * that the entries are read from the beginning. - * - * Usage: - *
- *   using SomeParser = ReverseParser;
- *   SomeParser parser;
- *   std::vector frames;
- *   parser.parse(ptr, size,
- *                [] (const typename SomeParser::HeaderType& h) {
- *                  // check the header
- *                  return true;
- *                },
- *                [] (const typename SomeParser::TrailerType& t) {
- *                  // check the trailer
- *                  return true;
- *                },
- *                [] (const typename SomeParser::TrailerType& t) {
- *                  // get the size of the frame including payload
- *                  // and header and trailer size, e.g. payload size
- *                  // from a trailer member
- *                  return t.payloadSize + SomeParser::totalOffset;
- *                },
- *                [&frames] (typename SomeParser::FrameInfo& info) {
- *                  frames.emplace_back(info);
- *                  return true;
- *                }
- *                )
- * 
- */ -template -class ReverseParser -{ - public: - using HeaderType = HeaderT; - using TrailerType = TrailerT; - using PayloadType = unsigned char; - - /// @struct FrameInfo a compound of header, data, and trailer - struct FrameInfo { - using PtrT = const PayloadType*; - - const HeaderType* header = nullptr; - const TrailerType* trailer = nullptr; - PtrT payload = nullptr; - size_t length = 0; - }; - /// the length offset due to header - static const size_t headOffset = typesize::size; - /// the length offset due to trailer - static const size_t tailOffset = typesize::size; - /// total length offset due to header and trailer - static const size_t totalOffset = headOffset + tailOffset; - - /// alias for callback checking the header, return true if the object - /// is a valid header - using CheckHeaderFct = std::function; - /// alias for callback checking the trailer - using CheckTrailerFct = std::function; - /// alias for callback to get the complete frame size including header, - /// trailer and the data - using GetFrameSizeFct = std::function; - /// function callback to insert/handle one frame into, sequentially called - /// for all frames if the whole block has a valid format - using InsertFct = std::function; - - /// Parse buffer of size bufferSize, requires callbacks to check header - /// trailer, the frame size, and insert callback to handle a FrameInfo - /// object. - template - int parse(const InputType* buffer, size_t bufferSize, - CheckHeaderFct checkHeader, - CheckTrailerFct checkTrailer, - GetFrameSizeFct getFrameSize, - InsertFct insert) - { - static_assert(sizeof(InputType) == 1, - "ReverseParser currently only supports byte type buffer"); - if (buffer == nullptr || bufferSize == 0) { - return 0; - } - auto position = bufferSize; - std::vector frames; - do { - FrameInfo entry; - - // start from end, extract and check trailer - if (sizeof(TrailerType) > position) { - break; - } - entry.trailer = reinterpret_cast(buffer + position - sizeof(TrailerType)); - if (!checkTrailer(*entry.trailer)) { - break; - } - - // get the total frame size - auto frameSize = getFrameSize(*entry.trailer); - if (frameSize > position) { - break; - } - - // extract and check header - auto headerStart = buffer + position - frameSize; - entry.header = reinterpret_cast(headerStart); - if (!checkHeader(*entry.header)) { - break; - } - - // payload immediately after header - entry.payload = reinterpret_cast(entry.header + 1); - entry.length = frameSize - sizeof(HeaderType) - sizeof(TrailerType); - frames.emplace_back(entry); - position -= frameSize; - } while (position > 0); - - if (position == 0) { - // frames found and format consistent, the complete block must be consistent - for (auto entry : frames) { - if (!insert(entry)) { - break; - } - } - return frames.size(); - } else if (frames.size() == 0) { - // no frames found at all, the buffer does not contain any - return 0; - } - - // format error detected - // TODO: decide about error policy - return -1; - } -}; - -} // namespace algorithm - -} // namespace o2 - -#endif // ALGORITHM_PARSER_H diff --git a/Algorithm/include/Algorithm/TableView.h b/Algorithm/include/Algorithm/TableView.h deleted file mode 100644 index 36980e64d1bc9..0000000000000 --- a/Algorithm/include/Algorithm/TableView.h +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALGORITHM_TABLEVIEW_H -#define ALGORITHM_TABLEVIEW_H - -/// @file TableView.h -/// @author Matthias Richter -/// @since 2017-09-21 -/// @brief Container class for multiple sequences of data wrapped by markers - -#include -#include - -namespace o2 -{ - -namespace algorithm -{ - -/** - * @class TableView - * Container class for multiple sequences of data wrapped by markers. - * - * This is a container for data sequences of multiple frames consisting - * of a header marker struct, a payload, and an optional trailer marker - * struct. Each sequence forms a row in the TableView, the columns - * are provided by the markers/frames. - * - * A parser is used to step through the data sequence and extract - * headers, trailers and payload positions. - * - * Requirements: - * - both header and trailer type must provide an operator bool() method - * to check validity - * - the size of one frame needs to be extracted either from the header - * marker or the trailer marker. The first requires forward, the latter - * backward parsing. In the first case, the trailer is optional, while - * in the latter required - * - */ -template -class TableView -{ - public: - TableView() = default; - ~TableView() = default; - - using RowDescType = RowDescT; - using ColumnIndexType = ColumnDescT; - using ParserType = ParserT; - - /// FrameIndex is composed from column description and row number - struct FrameIndex { - ColumnIndexType columnIndex; - unsigned row; - - bool operator<(const FrameIndex& rh) const - { - if (rh.columnIndex < columnIndex) { - return false; - } - if (columnIndex < rh.columnIndex) { - return true; - } - return row < rh.row; - } - }; - - /// descriptor pointing to payload of one frame - struct FrameData { - const std::byte* buffer = nullptr; - size_t size = 0; - }; - - /** - * Add a new data sequence, the set is traversed according to parser - * - * TODO: functors to check header and trailer validity as well as retrieving - * the frame size could be passed as arguments. - * - * @param rowData Descriptive data struct for the sequence - * @param seqData Pointer to sequence - * @param seqSize Length of sequence - * @return number of inserted elements - */ - size_t addRow(RowDescType rowData, std::byte* seqData, size_t seqSize) - { - unsigned nFrames = mFrames.size(); - unsigned currentRow = mRowData.size(); - ParserType p; - p.parse( - seqData, seqSize, - [](const typename ParserT::HeaderType& h) { return (h); }, - [](const typename ParserT::TrailerType& t) { return (t); }, - [](const typename ParserT::TrailerType& t) { - return t.dataLength + ParserT::totalOffset; - }, - [this, currentRow](typename ParserT::FrameInfo entry) { - // insert the header as column index in ascending order - auto position = mColumns.begin(); - while (position != mColumns.end() && *position < *entry.header) { - position++; - } - if (position == mColumns.end() || *entry.header < *position) { - mColumns.emplace(position, *entry.header); - } - - // insert frame descriptor under key composed from header and row - auto result = mFrames.emplace(FrameIndex{*entry.header, currentRow}, - FrameData{(std::byte*)entry.payload, entry.length}); - return result.second; - }); - auto insertedFrames = mFrames.size() - nFrames; - if (insertedFrames > 0) { - mRowData.emplace_back(rowData); - } - return insertedFrames; - } - - /// clear the index, i.e. all internal lists - void clear() - { - mFrames.clear(); - mColumns.clear(); - mRowData.clear(); - } - - /// get number of columns in the created index - size_t getNColumns() const { return mColumns.size(); } - - /// get number of rows, i.e. number rows in the created index - size_t getNRows() const { return mRowData.size(); } - - /// get row data for a data set - const RowDescType& getRowData(size_t row) const - { - if (row < mRowData.size()) { - return mRowData[row]; - } - // TODO: better to throw exception? - static RowDescType dummy; - return dummy; - } - - // TODO: - // instead of a member with this pointer of parent class, the access - // function was supposed to be specified as a lambda. This definition - // was supposed to be the type of the function member. - // passing the access function to the iterator did not work because - // the typedef for the access function is without the capture, so there - // is no matching conversion. - // Solution would be to use std::function but that's probably slow and - // the function is called often. Can be checked later. - typedef FrameData (*AccessFct)(unsigned, unsigned); - - /// Iterator class for configurable direction, i.e. either row or column - class iterator - { // TODO: derive from forward_iterator - public: - struct value_type : public FrameData { - RowDescType desc; - }; - using self_type = iterator; - - enum IteratorDirections { - kAlongRow, - kAlongColumn - }; - - iterator() = delete; - ~iterator() = default; - iterator(IteratorDirections direction, TableView* parent, unsigned row = 0, unsigned column = 0) - : mDirection(direction), mRow(row), mColumn(column), mEnd(direction == kAlongRow ? parent->getNColumns() : parent->getNRows()), mParent(parent), mCache(), mIsCached(false) - { - while (!isValid() && !isEnd()) { - operator++(); - } - } - - self_type& operator++() - { - mIsCached = false; - if (mDirection == kAlongRow) { - if (mColumn < mEnd) { - mColumn++; - } - } else { - if (mRow < mEnd) { - mRow++; - } - } - while (!isEnd() && !isValid()) { - operator++(); - } - return *this; - } - - value_type operator*() const - { - if (!mIsCached) { - self_type* ncthis = const_cast(this); - mParent->get(mRow, mColumn, ncthis->mCache); - ncthis->mCache.desc = mParent->getRowData(mRow); - ncthis->mIsCached = true; - } - return mCache; - } - - bool operator==(const self_type& other) const - { - return mDirection == kAlongRow ? (mColumn == other.mColumn) : (mRow == other.mRow); - } - - bool operator!=(const self_type& other) const - { - return mDirection == kAlongRow ? (mColumn != other.mColumn) : (mRow != other.mRow); - } - - bool isEnd() const - { - return (mDirection == kAlongRow) ? (mColumn >= mEnd) : (mRow >= mEnd); - } - - bool isValid() const - { - if (!mIsCached) { - self_type* ncthis = const_cast(this); - ncthis->mIsCached = mParent->get(mRow, mColumn, ncthis->mCache); - ncthis->mCache.desc = mParent->getRowData(mRow); - } - return mIsCached; - } - - protected: - IteratorDirections mDirection; - unsigned mRow; - unsigned mColumn; - unsigned mEnd; - TableView* mParent; - value_type mCache; - bool mIsCached; - }; - - /// iterator for the outer access of the index, either row or column direction - template - class outerIterator : public iterator - { - public: - using base = iterator; - using value_type = typename base::value_type; - using self_type = outerIterator; - static const unsigned direction = Direction; - - outerIterator() = delete; - ~outerIterator() = default; - outerIterator(TableView* parent, unsigned index) - : iterator(typename iterator::IteratorDirections(direction), parent, direction == iterator::kAlongColumn ? index : 0, direction == iterator::kAlongRow ? index : 0) - { - } - - self_type& operator++() - { - if (base::mDirection == iterator::kAlongRow) { - if (base::mColumn < base::mEnd) { - base::mColumn++; - } - } else { - if (base::mRow < base::mEnd) { - base::mRow++; - } - } - return *this; - } - - /// begin the inner iteration - iterator begin() - { - return iterator((base::mDirection == iterator::kAlongColumn) ? iterator::kAlongRow : iterator::kAlongColumn, - base::mParent, - (base::mDirection == iterator::kAlongColumn) ? base::mRow : 0, - (base::mDirection == iterator::kAlongRow) ? base::mColumn : 0); - } - - /// end of the inner iteration - iterator end() - { - return iterator((base::mDirection == iterator::kAlongColumn) ? iterator::kAlongRow : iterator::kAlongColumn, - base::mParent, - (base::mDirection == iterator::kAlongRow) ? base::mParent->getNRows() : 0, - (base::mDirection == iterator::kAlongColumn) ? base::mParent->getNColumns() : 0); - } - }; - - /// definition of the outer iterator over column - using ColumnIterator = outerIterator; - /// definition of the outer iterator over row - using RowIterator = outerIterator; - - /// begin of the outer iteration - ColumnIterator begin() - { - return ColumnIterator(this, 0); - } - - /// end of outer iteration - ColumnIterator end() - { - return ColumnIterator(this, mColumns.size()); - } - - private: - /// private access function for the iterators - bool get(unsigned row, unsigned column, FrameData& data) - { - if (this->mColumns.size() == 0) { - return false; - } - auto element = this->mFrames.find(FrameIndex{this->mColumns[column], row}); - if (element != this->mFrames.end()) { - data = element->second; - return true; - } - return false; - } - - /// map of frame descriptors with key composed from header and row number - std::map mFrames; - /// list of indices in row direction - std::vector mColumns; - /// data descriptor of each row forming the columns - std::vector mRowData; -}; - -} // namespace algorithm - -} // namespace o2 - -#endif // ALGORITHM_TABLEVIEW_H diff --git a/Algorithm/test/StaticSequenceAllocator.h b/Algorithm/test/StaticSequenceAllocator.h deleted file mode 100644 index 8a684159afdb9..0000000000000 --- a/Algorithm/test/StaticSequenceAllocator.h +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file StaticSequenceAllocator.h -/// @author Matthias Richter, based on work by Mikolaj Krzewicki -/// @since 2017-09-21 -/// @brief An allocator for static sequences of object types - -namespace o2 -{ -namespace algorithm -{ - -/** - * Helper struct to define a composite element from a header, some payload - * and a trailer - */ -template -struct Composite { - using HeaderType = HeaderT; - using TrailerType = TrailerT; - size_t compositeLength = 0; - size_t trailerLength = 0; - size_t dataLength = 0; - - template - constexpr Composite(const HeaderType h, const char (&d)[N], - typename std::conditional::value, const TrailerType, int>::type t, - typename std::enable_if::value>::type* = nullptr) - : header(h), data(d), trailer(t) - { - dataLength = N; - trailerLength = sizeof(TrailerType); - compositeLength = sizeof(HeaderType) + dataLength + trailerLength; - } - - template - constexpr Composite(const HeaderType& h, const char (&d)[N], - typename std::enable_if::value>::type* = nullptr) - : header(h), data(d) - { - dataLength = N; - trailerLength = 0; - compositeLength = sizeof(HeaderType) + dataLength + trailerLength; - } - - constexpr size_t getLength() const noexcept - { - return compositeLength; - } - - constexpr size_t getDataLength() const noexcept - { - return dataLength; - } - - template - constexpr size_t insert(BufferT* buffer) const noexcept - { - static_assert(sizeof(BufferT) == 1, "buffer required to be of byte-type"); - size_t length = 0; - memcpy(buffer + length, &header, sizeof(HeaderType)); - length += sizeof(HeaderType); - memcpy(buffer + length, data, dataLength); - length += dataLength; - if (trailerLength > 0) { - memcpy(buffer + length, &trailer, trailerLength); - length += trailerLength; - } - return length; - } - - const HeaderType header; - const char* data = nullptr; - typename std::conditional::value, const TrailerType, int>::type trailer; -}; - -/// recursively calculate the length of the sequence -/// object types are fixed at compile time and so is the total length of the -/// sequence. The function is recursively invoked for all arguments of the -// variable list -template -constexpr size_t sequenceLength(const T& first, const TArgs... args) noexcept -{ - return sequenceLength(first) + sequenceLength(args...); -} - -/// template secialization of sequence length calculation for one argument, -/// this is also the terminating instance for the last argument of the recursive -/// invocation of the function template. -template -constexpr size_t sequenceLength(const T& first) noexcept -{ - return first.getLength(); -} - -/// recursive insert of variable number of objects -template -constexpr size_t sequenceInsert(BufferT* buffer, const T& first, const TArgs... args) noexcept -{ - static_assert(sizeof(BufferT) == 1, "buffer required to be of byte-type"); - auto length = sequenceInsert(buffer, first); - length += sequenceInsert(buffer + length, args...); - return length; -} - -/// terminating template specialization, i.e. for the last element -template -constexpr size_t sequenceInsert(BufferT* buffer, const T& element) noexcept -{ - // TODO: make a general algorithm, at the moment this serves the - // Composite class as a special case - return element.insert(buffer); -} - -/** - * Allocator for a buffer of a static sequence of multiple objects. - * - * The sequence of object types is fixed at compile time and given as - * a variable list of arguments to the constructor. The data of the objects - * is runtime dependent. - * - * TODO: probably the Composite struct needs to be reworked to allow this - * allocator to be more general - */ -struct StaticSequenceAllocator { - using value_type = unsigned char; - using BufferType = std::unique_ptr; - - BufferType buffer; - size_t bufferSize; - - size_t size() const { return bufferSize; } - - StaticSequenceAllocator() = delete; - - template - StaticSequenceAllocator(Targs... args) - { - bufferSize = sequenceLength(args...); - buffer = std::make_unique(bufferSize); - sequenceInsert(buffer.get(), args...); - } -}; - -} // namespace algorithm -} // namespace o2 diff --git a/Algorithm/test/o2formatparser.cxx b/Algorithm/test/o2formatparser.cxx deleted file mode 100644 index 2896631ebc5b8..0000000000000 --- a/Algorithm/test/o2formatparser.cxx +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file o2formatparser.cxx -/// @author Matthias Richter -/// @since 2017-10-18 -/// @brief Unit test for O2 format parser - -#define BOOST_TEST_MODULE Test Algorithm HeaderStack -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include -#include -#include -#include // memcmp -#include "Headers/DataHeader.h" // hexdump, DataHeader -#include "../include/Algorithm/O2FormatParser.h" - -template -void hexDump(Targs... Fargs) -{ - // a simple redirect to enable/disable the hexdump printout - o2::header::hexDump(Fargs...); -} - -BOOST_AUTO_TEST_CASE(test_o2formatparser) -{ - std::vector thedata = { - "I'm raw data", - "reconstructed data"}; - unsigned dataidx = 0; - std::vector dataheaders; - dataheaders.emplace_back(o2::header::DataDescription("RAWDATA"), - o2::header::DataOrigin("DET"), - 0, - strlen(thedata[dataidx++])); - dataheaders.emplace_back(o2::header::DataDescription("RECODATA"), - o2::header::DataOrigin("DET"), - 0, - strlen(thedata[dataidx++])); - - std::vector> messages; - for (dataidx = 0; dataidx < thedata.size(); ++dataidx) { - messages.emplace_back(reinterpret_cast(&dataheaders[dataidx]), - sizeof(o2::header::DataHeader)); - messages.emplace_back(thedata[dataidx], - dataheaders[dataidx].payloadSize); - } - - // handler callback for parseO2Format method - auto insertFct = [&](const auto& dataheader, - auto ptr, - auto size) { - hexDump("header", &dataheader, sizeof(dataheader)); - hexDump("data", ptr, size); - BOOST_CHECK(dataheader == dataheaders[dataidx]); - BOOST_CHECK(strncmp(ptr, thedata[dataidx], size) == 0); - ++dataidx; - }; // end handler callback - - // handler callback to get the pointer for message - auto getPointerFct = [](auto arg) { return arg.first; }; - // handler callback to get the size for message - auto getSizeFct = [](auto arg) { return arg.second; }; - - dataidx = 0; - auto result = o2::algorithm::parseO2Format(messages, - getPointerFct, - getSizeFct, - insertFct); - - BOOST_REQUIRE(result >= 0); - BOOST_CHECK(result == 2); -} diff --git a/Algorithm/test/pageparser.cxx b/Algorithm/test/pageparser.cxx deleted file mode 100644 index 14b24c670cfd6..0000000000000 --- a/Algorithm/test/pageparser.cxx +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file parser.cxx -/// @author Matthias Richter -/// @since 2017-09-27 -/// @brief Unit test for parser of objects in memory pages - -#define BOOST_TEST_MODULE Test Algorithm Parser -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include -#include -#include -#include -#include "Headers/DataHeader.h" // hexdump -#include "../include/Algorithm/PageParser.h" -#include "StaticSequenceAllocator.h" - -struct PageHeader { - uint32_t magic = 0x45474150; - uint32_t pageid; - - PageHeader(uint32_t id) : pageid(id) {} -}; - -struct ClusterData { - uint32_t magic = 0x54534c43; - uint32_t clusterid; - uint16_t x; - uint16_t y; - uint16_t z; - uint8_t e; - - ClusterData() - : clusterid(0), x(0), y(0), z(0), e(0) - { - } - - ClusterData(uint32_t _id, uint16_t _x, uint16_t _y, uint16_t _z, uint8_t _e) - : clusterid(_id), x(_x), y(_y), z(_z), e(_e) - { - } - - bool operator==(const ClusterData& rhs) - { - return clusterid == rhs.clusterid && x == rhs.x && y == rhs.y && z == rhs.z && e == rhs.e; - } -}; - -template -std::pair, size_t> MakeBuffer(size_t pagesize, - PageHeaderT pageheader, - const ListT& dataset) -{ - static_assert(std::is_void::value || std::is_integral::value, - "Invalid group type"); - auto totalSize = dataset.size() * sizeof(typename ListT::value_type); - totalSize += o2::algorithm::pageparser::sizeofGroupHeader(); - auto maxElementsPerPage = pagesize - (sizeof(pageheader) + o2::algorithm::pageparser::sizeofGroupHeader()); - maxElementsPerPage /= sizeof(typename ListT::value_type); - - if (std::is_void::value || !GroupHeaderPerPage) { - unsigned nPages = 0; - do { - totalSize += sizeof(PageHeaderT); - ++nPages; - } while (nPages * pagesize < totalSize); - } else { - auto nRequiredPages = dataset.size() / maxElementsPerPage; - if (dataset.size() % maxElementsPerPage > 0) { - ++nRequiredPages; - } - totalSize = (nRequiredPages > 0 ? nRequiredPages : 1) * pagesize; - } - - auto buffer = std::make_unique(totalSize); - memset(buffer.get(), 0, totalSize); - - unsigned position = 0; - auto target = buffer.get(); - GroupT* groupHeader = nullptr; - size_t nElementsInCurrentGroup = 0; - for (auto element : dataset) { - if (GroupHeaderPerPage && nElementsInCurrentGroup == maxElementsPerPage) { - // write the number of elements in the group and forward to next - // page boundary - o2::algorithm::pageparser::set(groupHeader, nElementsInCurrentGroup); - nElementsInCurrentGroup = 0; - if (position % pagesize) { - target += pagesize - (position % pagesize); - position += pagesize - (position % pagesize); - } - } - auto source = reinterpret_cast(&element); - auto copySize = sizeof(typename ListT::value_type); - if ((position % pagesize) == 0) { - memcpy(target, &pageheader, sizeof(PageHeaderT)); - position += sizeof(PageHeaderT); - target += sizeof(PageHeaderT); - } - if (!std::is_void::value && - (position % pagesize) == sizeof(PageHeader) && - (GroupHeaderPerPage || position < pagesize)) { - // write one GroupHeader at the beginning of the data, currently - // GroupHeader must be of integral type - groupHeader = reinterpret_cast(target); - position += o2::algorithm::pageparser::sizeofGroupHeader(); - target += o2::algorithm::pageparser::sizeofGroupHeader(); - } - ++nElementsInCurrentGroup; - if ((position % pagesize) + copySize > pagesize) { - copySize -= ((position % pagesize) + copySize) - pagesize; - } - if (copySize > 0) { - memcpy(target, source, copySize); - position += copySize; - target += copySize; - source += copySize; - } - copySize = sizeof(typename ListT::value_type) - copySize; - if (copySize > 0) { - memcpy(target, &pageheader, sizeof(PageHeaderT)); - position += sizeof(PageHeaderT); - target += sizeof(PageHeaderT); - memcpy(target, source, copySize); - } - position += copySize; - target += copySize; - } - if (!std::is_void::value) { - o2::algorithm::pageparser::set(groupHeader, nElementsInCurrentGroup); - } - - std::pair, size_t> result; - result.first = std::move(buffer); - result.second = totalSize; - return result; -} - -template -void FillData(ListT& dataset, unsigned entries) -{ - for (unsigned i = 0; i < entries; i++) { - dataset.emplace_back(i, 0xaa, 0xbb, 0xcc, 0xd); - } -} - -template -void runParserTest(const DataSetT& dataset) -{ - std::cout << std::endl - << "Testing PageParser in grouped mode and " - << (GroupHeaderPerPage ? "multiple" : "single") - << " group header(s)" << std::endl - << " pagesize " << pagesize << std::endl; - auto buffer = MakeBuffer(pagesize, PageHeaderT(0), dataset); - o2::header::hexDump("pagebuffer", buffer.first.get(), buffer.second); - - using RawParser = o2::algorithm::PageParser; - const RawParser parser(buffer.first.get(), buffer.second); - - unsigned dataidx = 0; - for (auto i : parser) { - o2::header::hexDump("clusterdata", &i, sizeof(ClusterData)); - BOOST_REQUIRE(i == dataset[dataidx++]); - } -} - -BOOST_AUTO_TEST_CASE(test_pageparser) -{ - constexpr unsigned pagesize = 128; - std::vector dataset; - FillData(dataset, 20); - auto buffer = MakeBuffer(pagesize, PageHeader(0), dataset); - o2::header::hexDump("pagebuffer", buffer.first.get(), buffer.second); - - using RawParser = o2::algorithm::PageParser; - const RawParser parser(buffer.first.get(), buffer.second); - - unsigned dataidx = 0; - for (auto i : parser) { - o2::header::hexDump("clusterdata", &i, sizeof(ClusterData)); - BOOST_REQUIRE(i == dataset[dataidx++]); - } - - std::vector linearizedData; - linearizedData.insert(linearizedData.begin(), parser.begin(), parser.end()); - dataidx = 0; - for (auto i : linearizedData) { - BOOST_REQUIRE(i == dataset[dataidx++]); - } - - dataidx = 0; - RawParser writer(buffer.first.get(), buffer.second); - std::vector> xvalues; - for (auto& i : writer) { - i.x = (dataidx * 3) % 7; - xvalues.emplace_back(i.x, dataidx); - ++dataidx; - } - o2::header::hexDump("changed buffer", buffer.first.get(), buffer.second); - - dataidx = 0; - for (auto i : parser) { - o2::header::hexDump("clusterdata", &i, sizeof(ClusterData)); - BOOST_REQUIRE(i.x == xvalues[dataidx++].first); - } -} - -BOOST_AUTO_TEST_CASE(test_pageparser_group) -{ - using DataSetT = std::vector; - DataSetT dataset; - FillData(dataset, 20); - - runParserTest(dataset); - runParserTest(dataset); - runParserTest(dataset); -} - -BOOST_AUTO_TEST_CASE(test_pageparser_group_perpage) -{ - using DataSetT = std::vector; - DataSetT dataset; - FillData(dataset, 20); - - runParserTest(dataset); - runParserTest(dataset); - runParserTest(dataset); -} diff --git a/Algorithm/test/parser.cxx b/Algorithm/test/parser.cxx deleted file mode 100644 index 0f31df99ca825..0000000000000 --- a/Algorithm/test/parser.cxx +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file parser.cxx -/// @author Matthias Richter -/// @since 2017-09-20 -/// @brief Unit test for data parsing methods in Algorithm/Parser.h - -#define BOOST_TEST_MODULE Test Algorithm Parser -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include -#include -#include -#include -#include "../include/Algorithm/Parser.h" -#include "StaticSequenceAllocator.h" - -// header test class -struct Header { - unsigned identifier = 0xdeadbeef; - size_t payloadSize = 0; - - Header(size_t ps) : payloadSize(ps) {} -}; - -// trailer test class -struct Trailer { - unsigned identifier = 0xaaffee00; - unsigned char flags = 0xaa; - - Trailer(unsigned char f) : flags(f) {} -}; - -// trailer test class including payload size -struct SizedTrailer { - unsigned identifier = 0xaaffee00; - unsigned char flags = 0xaa; - size_t payloadSize = 0; - - SizedTrailer(size_t s, unsigned char f) : flags(f), payloadSize(s) {} -}; - -BOOST_AUTO_TEST_CASE(test_forwardparser_header_and_trailer) -{ - using FrameT = o2::algorithm::Composite; - // note: the length of the data is set in the header word - using TestFrame = o2::algorithm::StaticSequenceAllocator; - TestFrame tf(FrameT(16, "lotsofsillydata", 0xaa), - FrameT(5, "test", 0xcc), - FrameT(10, "dummydata", 0x33)); - - using ParserT = o2::algorithm::ForwardParser; - - auto checkHeader = [](const typename FrameT::HeaderType& header) { - return header.identifier == 0xdeadbeef; - }; - auto checkTrailer = [](const typename FrameT::TrailerType& trailer) { - return trailer.identifier == 0xaaffee00; - }; - auto getFrameSize = [](const typename ParserT::HeaderType& header) { - // frame size includes total offset from header and trailer - return header.payloadSize + ParserT::totalOffset; - }; - - std::vector frames; - auto insert = [&frames](typename ParserT::FrameInfo& info) { - frames.emplace_back(info); - return true; - }; - - ParserT parser; - auto result = parser.parse(tf.buffer.get(), tf.size(), - checkHeader, - checkTrailer, - getFrameSize, - insert); - - BOOST_REQUIRE(result == 3); - BOOST_REQUIRE(frames.size() == 3); - - BOOST_CHECK(memcmp(frames[0].payload, "lotsofsillydata", frames[0].length) == 0); - BOOST_CHECK(memcmp(frames[1].payload, "test", frames[1].length) == 0); - BOOST_CHECK(memcmp(frames[2].payload, "dummydata", frames[2].length) == 0); -} - -BOOST_AUTO_TEST_CASE(test_forwardparser_header_and_void_trailer) -{ - using FrameT = o2::algorithm::Composite
; - // note: the length of the data is set in the header word - using TestFrame = o2::algorithm::StaticSequenceAllocator; - TestFrame tf(FrameT(16, "lotsofsillydata"), - FrameT(5, "test"), - FrameT(10, "dummydata")); - - using ParserT = o2::algorithm::ForwardParser; - - auto checkHeader = [](const typename FrameT::HeaderType& header) { - return header.identifier == 0xdeadbeef; - }; - - auto getFrameSize = [](const typename ParserT::HeaderType& header) { - // frame size includes total offset from header and trailer - return header.payloadSize + ParserT::totalOffset; - }; - - std::vector frames; - auto insert = [&frames](typename ParserT::FrameInfo& info) { - frames.emplace_back(info); - return true; - }; - - ParserT parser; - auto result = parser.parse(tf.buffer.get(), tf.size(), - checkHeader, - getFrameSize, - insert); - - BOOST_REQUIRE(result == 3); - BOOST_REQUIRE(frames.size() == 3); - - BOOST_CHECK(memcmp(frames[0].payload, "lotsofsillydata", frames[0].length) == 0); - BOOST_CHECK(memcmp(frames[1].payload, "test", frames[1].length) == 0); - BOOST_CHECK(memcmp(frames[2].payload, "dummydata", frames[2].length) == 0); -} - -BOOST_AUTO_TEST_CASE(test_forwardparser_no_frames) -{ - using FrameT = o2::algorithm::Composite
; - // note: the length of the data is set in the header word - using TestFrame = o2::algorithm::StaticSequenceAllocator; - TestFrame tf(FrameT(16, "lotsofsillydata"), - FrameT(5, "test"), - FrameT(10, "dummydata")); - - using ParserT = o2::algorithm::ForwardParser; - - auto checkHeader = [](const typename FrameT::HeaderType& header) { - // simply indicate invalid header to read no frames - return false; - }; - - auto getFrameSize = [](const typename ParserT::HeaderType& header) { - // frame size includes total offset from header and trailer - return header.payloadSize + ParserT::totalOffset; - }; - - std::vector frames; - auto insert = [&frames](typename ParserT::FrameInfo& info) { - frames.emplace_back(info); - return true; - }; - - ParserT parser; - auto result = parser.parse(tf.buffer.get(), tf.size(), - checkHeader, - getFrameSize, - insert); - - // check that there are really no frames found - BOOST_REQUIRE(result == 0); -} - -BOOST_AUTO_TEST_CASE(test_forwardparser_format_error) -{ - using FrameT = o2::algorithm::Composite
; - // note: the length of the data is set in the header word - using TestFrame = o2::algorithm::StaticSequenceAllocator; - TestFrame tf(FrameT(16, "lotsofsillydata"), - FrameT(4, "test"), // <- note wrong size - FrameT(10, "dummydata")); - - using ParserT = o2::algorithm::ForwardParser; - - auto checkHeader = [](const typename FrameT::HeaderType& header) { - return header.identifier == 0xdeadbeef; - }; - - auto getFrameSize = [](const typename ParserT::HeaderType& header) { - // frame size includes total offset from header and trailer - return header.payloadSize + ParserT::totalOffset; - }; - - std::vector frames; - auto insert = [&frames](typename ParserT::FrameInfo& info) { - frames.emplace_back(info); - return true; - }; - - ParserT parser; - auto result = parser.parse(tf.buffer.get(), tf.size(), - checkHeader, - getFrameSize, - insert); - - BOOST_REQUIRE(result == -1); -} - -BOOST_AUTO_TEST_CASE(test_reverseparser) -{ - using FrameT = o2::algorithm::Composite; - // note: the length of the data is set in the trailer word - using TestFrame = o2::algorithm::StaticSequenceAllocator; - TestFrame tf(FrameT(0, "lotsofsillydata", {16, 0xaa}), - FrameT(0, "test", {5, 0xcc}), - FrameT(0, "dummydata", {10, 0x33})); - - using ParserT = o2::algorithm::ReverseParser; - - auto checkHeader = [](const typename FrameT::HeaderType& header) { - return header.identifier == 0xdeadbeef; - }; - auto checkTrailer = [](const typename FrameT::TrailerType& trailer) { - return trailer.identifier == 0xaaffee00; - }; - auto getFrameSize = [](const typename ParserT::TrailerType& trailer) { - return trailer.payloadSize + ParserT::totalOffset; - }; - - std::vector frames; - auto insert = [&frames](const typename ParserT::FrameInfo& info) { - frames.emplace_back(info); - return true; - }; - - ParserT parser; - auto result = parser.parse(tf.buffer.get(), tf.size(), - checkHeader, - checkTrailer, - getFrameSize, - insert); - - BOOST_REQUIRE(result == 3); - BOOST_REQUIRE(frames.size() == 3); - - BOOST_CHECK(memcmp(frames[2].payload, "lotsofsillydata", frames[2].length) == 0); - BOOST_CHECK(memcmp(frames[1].payload, "test", frames[1].length) == 0); - BOOST_CHECK(memcmp(frames[0].payload, "dummydata", frames[0].length) == 0); -} diff --git a/Algorithm/test/tableview.cxx b/Algorithm/test/tableview.cxx deleted file mode 100644 index c303d531b541e..0000000000000 --- a/Algorithm/test/tableview.cxx +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file headerstack.cxx -/// @author Matthias Richter -/// @since 2017-09-21 -/// @brief Unit test for table view abstraction class - -#define BOOST_TEST_MODULE Test Algorithm TableView -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include -#include -#include -#include // memcmp -#include "Headers/DataHeader.h" // hexdump, DataHeader -#include "Headers/HeartbeatFrame.h" // HeartbeatHeader, HeartbeatTrailer -#include "../include/Algorithm/TableView.h" -#include "../include/Algorithm/Parser.h" -#include "StaticSequenceAllocator.h" - -using DataHeader = o2::header::DataHeader; -using HeartbeatHeader = o2::header::HeartbeatHeader; -using HeartbeatTrailer = o2::header::HeartbeatTrailer; - -template -void hexDump(Targs... Fargs) -{ - // a simple redirect to enable/disable the hexdump printout - o2::header::hexDump(Fargs...); -} - -BOOST_AUTO_TEST_CASE(test_tableview_reverse) -{ - using FrameT = o2::algorithm::Composite; - using TestFrame = o2::algorithm::StaticSequenceAllocator; - // the length of the data is set in the trailer word - // the header is used as column description, using slightly different - // orbit numbers in the to data sets which will result in two complete - // columns at beginning and end, while the two in the middle only have - // one row entry - TestFrame tf1(FrameT({0x1100000000000000}, "heartbeatdata", {0x510000000000000e}), - FrameT({0x1100000000000001}, "test", {0x5100000000000005}), - FrameT({0x1100000000000003}, "dummydata", {0x510000000000000a})); - TestFrame tf2(FrameT({0x1100000000000000}, "frame2a", {0x5100000000000008}), - FrameT({0x1100000000000002}, "frame2b", {0x5100000000000008}), - FrameT({0x1100000000000003}, "frame2c", {0x5100000000000008})); - hexDump("Test frame 1", tf1.buffer.get(), tf1.size()); - hexDump("Test frame 2", tf2.buffer.get(), tf2.size()); - - // the payload length is set in the trailer, so we need a reverse parser - using ParserT = o2::algorithm::ReverseParser; - - // define the view type for DataHeader as row descriptor, - // HeartbeatHeader as column descriptor and the reverse parser - using ViewType = o2::algorithm::TableView; - ViewType heartbeatview; - - o2::header::DataHeader dh1; - dh1.dataDescription = o2::header::DataDescription("FIRSTROW"); - dh1.dataOrigin = o2::header::DataOrigin("TST"); - dh1.subSpecification = 0; - dh1.payloadSize = 0; - - o2::header::DataHeader dh2; - dh2.dataDescription = o2::header::DataDescription("SECONDROW"); - dh2.dataOrigin = o2::header::DataOrigin("TST"); - dh2.subSpecification = 0xdeadbeef; - dh2.payloadSize = 0; - - heartbeatview.addRow(dh1, (std::byte*)tf1.buffer.get(), tf1.size()); - heartbeatview.addRow(dh2, (std::byte*)tf2.buffer.get(), tf2.size()); - - std::cout << "slots: " << heartbeatview.getNRows() - << " columns: " << heartbeatview.getNColumns() - << std::endl; - - // definitions for the data check - const char* dataset1[] = { - "heartbeatdata", - "test", - "dummydata"}; - const char* dataset2[] = { - "frame2a", - "frame2b", - "frame2c"}; - - // four orbits are populated, 0 and 3 with 2 rows, 1 and 2 with one row - BOOST_REQUIRE(heartbeatview.getNColumns() == 4); - BOOST_REQUIRE(heartbeatview.getNRows() == 2); - unsigned requiredNofRowsInColumn[] = {2, 1, 1, 2}; - - unsigned colidx = 0; - unsigned dataset1idx = 0; - unsigned dataset2idx = 0; - for (auto columnIt = heartbeatview.begin(), end = heartbeatview.end(); - columnIt != end; ++columnIt, ++colidx) { - unsigned rowidx = 0; - std::cout << "---------------------------------------" << std::endl; - for (auto row : columnIt) { - auto dataset = (rowidx == 1 || colidx == 2) ? dataset2 : dataset1; - auto& datasetidx = (rowidx == 1 || colidx == 2) ? dataset2idx : dataset1idx; - hexDump("Entry", row.buffer, row.size); - BOOST_CHECK(memcmp(row.buffer, dataset[datasetidx++], row.size) == 0); - ++rowidx; - } - BOOST_CHECK(rowidx == requiredNofRowsInColumn[colidx]); - } -} - -BOOST_AUTO_TEST_CASE(test_tableview_formaterror) -{ - using FrameT = o2::algorithm::Composite; - using TestFrame = o2::algorithm::StaticSequenceAllocator; - // note: the length of the data is set in the trailer word - // specifying wrong length in the second entry, no frames should be added - TestFrame tf1(FrameT({0x1100000000000000}, "heartbeatdata", {0x510000000000000e}), - FrameT({0x1100000000000001}, "test", {0x5100000000000004}), - FrameT({0x1100000000000003}, "dummydata", {0x510000000000000a})); - - // the payload length is set in the trailer, so we need a reverse parser - using ParserT = o2::algorithm::ReverseParser; - - // define the view type for DataHeader as row descriptor, - // HeartbeatHeader as column descriptor and the reverse parser - using ViewType = o2::algorithm::TableView; - ViewType heartbeatview; - - o2::header::DataHeader dh; - dh.dataDescription = o2::header::DataDescription("FIRSTSLOT"); - dh.dataOrigin = o2::header::DataOrigin("TST"); - dh.subSpecification = 0; - dh.payloadSize = 0; - - heartbeatview.addRow(dh, (std::byte*)tf1.buffer.get(), tf1.size()); - - BOOST_CHECK(heartbeatview.getNRows() == 0); - BOOST_CHECK(heartbeatview.getNColumns() == 0); -} diff --git a/Algorithm/test/test_BitstreamReader.cxx b/Algorithm/test/test_BitstreamReader.cxx deleted file mode 100644 index 41e3b47f5f276..0000000000000 --- a/Algorithm/test/test_BitstreamReader.cxx +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file test_BitstreamReader.cxx -/// @author Matthias Richter -/// @since 2019-06-05 -/// @brief Test program for BitstreamReader utility - -#define BOOST_TEST_MODULE Algorithm BitstreamReader unit test -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include -#include -#include -#include -#include -#include -#include "../include/Algorithm/BitstreamReader.h" - -namespace o2 -{ -namespace algorithm -{ - -BOOST_AUTO_TEST_CASE(test_BitstreamReader_basic) -{ - std::array data = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f'}; - std::array expected7bit = {0x32, 0x19, 0x2c, 0x16, 0x23, 0x09, 0x4a, 0x65, 0x33, 0x0}; - auto reference = expected7bit.begin(); - constexpr size_t totalBits = data.size() * sizeof(decltype(data)::value_type) * 8; - size_t bitsRead = 0; - - BitstreamReader reader(data.data(), data.data() + data.size()); - while (bitsRead < totalBits) { - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK(reader.eof() == false); - uint8_t value; - reader.peek(value); - // we use 7 bits of the data - value >>= 1; - reader.seek(7); - bitsRead += 7; - // in the last call should there is not enough data - BOOST_CHECK(reader.good() == (bitsRead <= totalBits)); - BOOST_REQUIRE(reference != expected7bit.end()); - //std::cout << "value " << (int)value << " expected " << (int)*reference << std::endl; - BOOST_CHECK(value == *reference); - ++reference; - } -} - -BOOST_AUTO_TEST_CASE(test_BitstreamReader_operator) -{ - std::array data = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f'}; - std::array expected7bit = {0x32, 0x19, 0x2c, 0x16, 0x23, 0x09, 0x4a, 0x65, 0x33, 0x0}; - auto reference = expected7bit.begin(); - constexpr size_t totalBits = data.size() * sizeof(decltype(data)::value_type) * 8; - size_t bitsRead = 0; - - BitstreamReader reader(data.data(), data.data() + data.size()); - while (bitsRead < totalBits) { - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK(reader.eof() == false); - { - decltype(reader)::Bits value; - reader >> value; - // we use 7 bits of the data - *value >>= 1; - value.markUsed(7); - //std::cout << "value " << (int)*value << " expected " << (int)*reference << std::endl; - BOOST_CHECK(*value == *reference); - } - bitsRead += 7; - // in the last call should there is not enough data - BOOST_CHECK(reader.good() == (bitsRead <= totalBits)); - BOOST_REQUIRE(reference != expected7bit.end()); - ++reference; - } -} - -BOOST_AUTO_TEST_CASE(test_BitstreamReader_bitset) -{ - std::array data = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f'}; - std::array expected7bit = {0x32, 0x19, 0x2c, 0x16, 0x23, 0x09, 0x4a, 0x65, 0x33, 0x0}; - auto reference = expected7bit.begin(); - constexpr size_t totalBits = data.size() * sizeof(decltype(data)::value_type) * 8; - size_t bitsRead = 0; - - BitstreamReader reader(data.data(), data.data() + data.size()); - while (bitsRead < totalBits) { - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK(reader.eof() == false); - std::bitset<13> value; - reader.peek(value, value.size()); - // we use 7 bits of the data - value >>= value.size() - 7; - reader.seek(7); - bitsRead += 7; - // in the last call should there is not enough data - BOOST_CHECK(reader.good() == (bitsRead <= totalBits)); - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK_MESSAGE(value.to_ulong() == *reference, std::string("mismatch: value ") << value.to_ulong() << ", expected " << (int)*reference); - ++reference; - } - - reader.reset(); - std::bitset<16> aBitset; - reader >> aBitset; - BOOST_CHECK_MESSAGE(aBitset.to_ulong() == 0x6465, std::string("mismatch: value 0x") << std::hex << aBitset.to_ulong() << ", expected 0x6465"); -} - -} // namespace algorithm -} // namespace o2 diff --git a/CCDB/CMakeLists.txt b/CCDB/CMakeLists.txt index 27ebc39e57c41..691c3311e117c 100644 --- a/CCDB/CMakeLists.txt +++ b/CCDB/CMakeLists.txt @@ -21,6 +21,7 @@ o2_add_library(CCDB FairMQ::FairMQ libjalien::libjalienO2 LibUV::LibUV + O2::Framework TARGETVARNAME targetName) o2_target_root_dictionary(CCDB @@ -44,6 +45,11 @@ o2_add_executable(upload SOURCES src/UploadTool.cxx PUBLIC_LINK_LIBRARIES O2::CCDB) +o2_add_executable(cleansemaphores + COMPONENT_NAME ccdb + SOURCES src/CleanCCDBSemaphores.cxx + PUBLIC_LINK_LIBRARIES O2::CCDB) + o2_add_executable(downloadccdbfile COMPONENT_NAME ccdb SOURCES src/DownloadCCDBFile.cxx @@ -86,6 +92,12 @@ o2_add_test(CcdbDownloader PUBLIC_LINK_LIBRARIES O2::CCDB LABELS ccdb) +o2_add_test(CcdbApi-Headers + SOURCES test/testCcdbApiHeaders.cxx + COMPONENT_NAME ccdb + PUBLIC_LINK_LIBRARIES O2::CCDB + LABELS ccdb) + # extra CcdbApi test which dispatches to CCDBDownloader (tmp until full move done) #o2_add_test_command(NAME CcdbApi-MultiHandle # WORKING_DIRECTORY ${SIMTESTDIR} diff --git a/CCDB/README.md b/CCDB/README.md index ce8d9e19f7b27..1ae5f29dcf0e2 100644 --- a/CCDB/README.md +++ b/CCDB/README.md @@ -13,7 +13,7 @@ in circumstances of reduced or no network connectivity. There are currently 2 different kinds of store/retrieve functions, which we expect to unify in the immediate future: 2. `storeAsTFile/retrieveFromTFile` API serializing a `TObject` in a ROOT `TFile`. -3. A strongly-typed `storeAsTFileAny/retrieveFromTFileAny` API allowing to handle any type T +3. A strongly-typed `storeAsTFileAny/retrieveFromTFileAny` API allowing to handle any type T having a ROOT dictionary. We encourage to use this API by default. ## Central and local instances of the CCDB @@ -31,18 +31,18 @@ If you access the CCDB with a web browser, add `/browse` at the end of the URL t ```c++ // init CcdbApi api; -map metadata; // can be empty +std::map metadata; // can be empty api.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation // store abitrary user object in strongly typed manner auto deadpixels = new o2::FOO::DeadPixelMap(); api.storeAsTFileAny(deadpixels, "FOO/DeadPixels", metadata); // read like this (you have to specify the type) -auto deadpixelsback = api.retrieveFromTFileAny("FOO/DeadPixels", metadata); -// read like this to get the headers as well, and thus the metadata attached to the object -map headers; -auto deadpixelsback = api.retrieveFromTFileAny("FOO/DeadPixels", metadata /* constraint the objects retrieved to those matching the metadata */, -1 /* timestamp */, &headers /* the headers attached to the returned object */); +auto deadpixelsback = api.retrieveFromTFileAny("FOO/DeadPixels", metadata); +// read like this to get the headers as well, and thus the metadata attached to the object +std::map headers; +auto deadpixelsback = api.retrieveFromTFileAny("FOO/DeadPixels", metadata /* constraint the objects retrieved to those matching the metadata */, -1 /* timestamp */, &headers /* the headers attached to the returned object */); // finally, use this method to retrieve only the headers (and thus the metadata) -std::map headers = f.api.retrieveHeaders("FOO/DeadPixels", f.metadata); +std::map headers = api.retrieveHeaders("FOO/DeadPixels", metadata); ``` * creating a local snapshot and fetching objects therefrom @@ -50,7 +50,7 @@ std::map headers = f.api.retrieveHeaders("FOO/DeadPixe ```c++ // init CcdbApi api; -map metadata; // can be empty +std::map metadata; // can be empty api.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation // create a local snapshot of everthing in or below the FOO folder valid for timestamp 12345 api.snapshot("FOO", "/tmp/CCDBSnapshot/", 12345); @@ -85,7 +85,7 @@ user code. This class The class was written for the use-case of transport MC simulation. Typical usage should be like ```c++ -// setup manager once (at start of processing) +// setup manager once (at start of processing) auto& mgr = o2::ccdb::BasicCCDBManager::instance(); mgr.setURL("http://ourccdbserverver.cern.ch"); mgr.setTimestamp(timestamp_which_we_want_to_anchor_to); @@ -111,6 +111,12 @@ This feature is useful to avoid using newer objects if the CCDB is updated in pa In cached mode, the manager can check that local objects are still valid by requiring `mgr.setLocalObjectValidityChecking(true)`, in this case a CCDB query is performed only if the cached object is no longer valid. +If you want the headers/metadata for the object retrieved from the CCDB there is an optional paramater to `BasicCCDBManager::getForTimeStamp`. These headers are also cached (when caching is enabled) and is updated when a CCDB query is sent. +```c++ +std::map headers; +mgr.getForTimeStamp(path, timstamp, metadata, &headers); +``` + ## Future ideas / todo: - [ ] offer improved error handling / exceptions @@ -129,26 +135,26 @@ A few prototypic command line tools are offered. These can be used in scriptable and facilitate the following tasks: 1. Upload and annotate a generic C++ object serialized in a ROOT file - + ```bash o2-ccdb-upload -f myRootFile.root --key histogram1 --path /Detector1/QA/ --meta "Description=Foo;Author=Person1;Uploader=Person2" ``` This will upload the object serialized in `myRootFile.root` under the key `histogram1`. Object will be put to the CCDB path `/Detector1/QA`. For full list of options see `o2-ccdb-upload --help`. - + 2. Download a CCDB object to a local ROOT file (including its meta information) - + ```bash o2-ccdb-downloadccdbfile --path /Detector1/QA/ --dest /tmp/CCDB --timestamp xxx ``` This will download the CCDB object under path given by `--path` to a directory given by `--dest` on the disc. (The final filename will be `/tmp/CCDB/Detector1/QA/snapshot.root` for the moment). All meta-information as well as the information associated to this query will be appended to the file. - + For full list of options see `o2-ccdb-downloadccdbfile --help`. - + 3. Inspect the content of a ROOT file and print summary about type of contained (CCDB) objects and its meta information - + ```bash o2-ccdb-inspectccdbfile filename ``` diff --git a/CCDB/include/CCDB/BasicCCDBManager.h b/CCDB/include/CCDB/BasicCCDBManager.h index af1c0831cfcba..6ad645a0a893e 100644 --- a/CCDB/include/CCDB/BasicCCDBManager.h +++ b/CCDB/include/CCDB/BasicCCDBManager.h @@ -18,9 +18,14 @@ #include "CCDB/CcdbApi.h" #include "CCDB/CCDBTimeStampUtils.h" #include "CommonUtils/NameConf.h" +#include "Framework/DataTakingContext.h" +#include "Framework/DefaultsHelpers.h" +#include "Framework/ServiceRegistryRef.h" +#include "Framework/DataProcessingStats.h" #include #include #include +#include #include #include #include @@ -48,10 +53,21 @@ class CCDBManagerInstance std::string uuid; long startvalidity = 0; long endvalidity = -1; + long cacheValidFrom = 0; // time for which the object was cached + long cacheValidUntil = -1; // object is guaranteed to be valid till this time (modulo new updates) + size_t size = 0; + size_t minSize = -1ULL; + size_t maxSize = 0; int queries = 0; int fetches = 0; int failures = 0; - bool isValid(long ts) { return ts < endvalidity && ts > startvalidity; } + std::map cacheOfHeaders; + bool isValid(long ts) { return ts < endvalidity && ts >= startvalidity; } + bool isCacheValid(long ts) + { + LOGP(debug, "isCacheValid : {} : {} : {} --> {}", cacheValidFrom, ts, cacheValidUntil, isValid(ts)); + return ts < cacheValidUntil && isValid(ts); + } void clear() { noCleanupPtr = nullptr; @@ -59,6 +75,7 @@ class CCDBManagerInstance uuid = ""; startvalidity = 0; endvalidity = -1; + cacheOfHeaders.clear(); } }; @@ -68,6 +85,7 @@ class CCDBManagerInstance CCDBManagerInstance(std::string const& path) : mCCDBAccessor{} { mCCDBAccessor.init(path); + mDeplMode = o2::framework::DefaultsHelpers::deploymentMode(); } /// set a URL to query from void setURL(const std::string& url); @@ -86,19 +104,31 @@ class CCDBManagerInstance /// query timestamp long getTimestamp() const { return mTimestamp; } - /// retrieve an object of type T from CCDB as stored under path and timestamp + /// retrieve an object of type T from CCDB as stored under path and timestamp. Optional to get the headers. template - T* getForTimeStamp(std::string const& path, long timestamp); + T* getForTimeStamp(std::string const& path, long timestamp, std::map* headers = nullptr); + + /// retrieve an object of type T from CCDB as stored under path and using the timestamp in the middle of the run + template + T* getForRun(std::string const& path, int runNumber, bool setRunMetadata = false); /// retrieve an object of type T from CCDB as stored under path, timestamp and metaData template - T* getSpecific(std::string const& path, long timestamp = -1, MD metaData = MD()) + T* getSpecific(std::string const& path, long timestamp = -1, MD metaData = MD(), std::map* headers = nullptr) { // TODO: add some error info/handling when failing mMetaData = metaData; - return getForTimeStamp(path, timestamp); + auto obj = getForTimeStamp(path, timestamp, headers); + return obj; } + /// retrieve an object of type T from CCDB as stored under path and using the timestamp in the middle of the run + metadata. The run number is provided separately to conform to typical analysis use (in which case metadata does not include runNumber) + template + T* getSpecificForRun(std::string const& path, int runNumber, MD const& metaData = MD()); + + /// detect online processing modes (i.e. CCDB objects may be updated in the lifetime of the manager) + bool isOnline() const { return mDeplMode == o2::framework::DeploymentMode::OnlineAUX || mDeplMode == o2::framework::DeploymentMode::OnlineDDS || mDeplMode == o2::framework::DeploymentMode::OnlineECS; } + /// retrieve an object of type T from CCDB as stored under path; will use the timestamp member template T* get(std::string const& path) @@ -106,6 +136,9 @@ class CCDBManagerInstance return getForTimeStamp(path, mTimestamp); } + // gain access to underlaying CCDB layer (to allow for more complex queries without need to reinit another API) + CcdbApi& getCCDBAccessor() { return mCCDBAccessor; } + bool isHostReachable() const { return mCCDBAccessor.isHostReachable(); } /// clear all entries in the cache @@ -132,7 +165,7 @@ class CCDBManagerInstance if (!isCachingEnabled()) { return false; } - return mCache[path].isValid(timestamp); + return (mCheckObjValidityEnabled && mCache[path].isValid(timestamp)) || mCache[path].isCacheValid(timestamp); // use stricter check } /// check if checks of object validity before CCDB query is enabled @@ -165,17 +198,19 @@ class CCDBManagerInstance void setFatalWhenNull(bool b) { mFatalWhenNull = b; } /// A convenience function for MC to fetch - /// valid start and end timestamps given an ALICE run number. - /// On error it fatals (if fatal == true) or else returns the pair -1, -1. - std::pair getRunDuration(int runnumber, bool fatal = true) const; - - /// A convenience function for MC to fetch - /// valid start and end timestamps given an ALICE run number. + /// valid start and end timestamps for recorded TF data given an ALICE run number. + /// In absence of STF/ETF fields in the RCT with fall back to CTP SOX/EOX then to + /// ECS SOR/EOR. /// On error it fatals (if fatal == true) or else returns the pair -1, -1. + std::pair getRunDuration(int runnumber, bool fatal = true); static std::pair getRunDuration(o2::ccdb::CcdbApi const& api, int runnumber, bool fatal = true); - + static std::pair getRunDuration(const MD& headers); std::string getSummaryString() const; + size_t getFetchedSize() const { return mFetchedSize; } + + void report(bool longrep = false); + void endOfStream(); private: @@ -190,25 +225,28 @@ class CCDBManagerInstance bool mCanDefault = false; // whether default is ok --> useful for testing purposes done standalone/isolation bool mCachingEnabled = true; // whether caching is enabled bool mCheckObjValidityEnabled = false; // wether the validity of cached object is checked before proceeding to a CCDB API query + bool mFatalWhenNull = true; // if nullptr blob replies should be treated as fatal (can be set by user) long mCreatedNotAfter = 0; // upper limit for object creation timestamp (TimeMachine mode) - If-Not-After HTTP header long mCreatedNotBefore = 0; // lower limit for object creation timestamp (TimeMachine mode) - If-Not-Before HTTP header long mTimerMS = 0; // timer for queries - bool mFatalWhenNull = true; // if nullptr blob replies should be treated as fatal (can be set by user) + size_t mFetchedSize = 0; // total fetched size + size_t mRequestedSize = 0; // total requested size (fetched + served from cache) int mQueries = 0; // total number of object queries int mFetches = 0; // total number of succesful fetches from CCDB int mFailures = 0; // total number of failed fetches - + o2::framework::DeploymentMode mDeplMode; // O2 deployment mode ClassDefNV(CCDBManagerInstance, 1); }; template -T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) +T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp, std::map* headers) { + mHeaders.clear(); // we clear at the beginning; to allow to retrieve the header information in a subsequent call T* ptr = nullptr; mQueries++; auto start = std::chrono::system_clock::now(); if (!isCachingEnabled()) { - ptr = mCCDBAccessor.retrieveFromTFileAny(path, mMetaData, timestamp, nullptr, "", + ptr = mCCDBAccessor.retrieveFromTFileAny(path, mMetaData, timestamp, &mHeaders, "", mCreatedNotAfter ? std::to_string(mCreatedNotAfter) : "", mCreatedNotBefore ? std::to_string(mCreatedNotBefore) : ""); if (!ptr) { @@ -218,16 +256,40 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) mFailures++; } else { mFetches++; + auto sh = mHeaders.find("fileSize"); + if (sh != mHeaders.end()) { + size_t s = atol(sh->second.c_str()); + mFetchedSize += s; + mRequestedSize += s; + } + } + + if (headers) { + *headers = mHeaders; } } else { auto& cached = mCache[path]; - if (mCheckObjValidityEnabled && cached.isValid(timestamp)) { - cached.queries++; + cached.queries++; + if ((!isOnline() && cached.isCacheValid(timestamp)) || (mCheckObjValidityEnabled && cached.isValid(timestamp))) { + // Give back the cached/saved headers + if (headers) { + *headers = cached.cacheOfHeaders; + } + mRequestedSize += cached.size; return reinterpret_cast(cached.noCleanupPtr ? cached.noCleanupPtr : cached.objPtr.get()); } ptr = mCCDBAccessor.retrieveFromTFileAny(path, mMetaData, timestamp, &mHeaders, cached.uuid, mCreatedNotAfter ? std::to_string(mCreatedNotAfter) : "", mCreatedNotBefore ? std::to_string(mCreatedNotBefore) : ""); + // update the cached headers + for (auto const& h : mHeaders) { + cached.cacheOfHeaders[h.first] = h.second; + } + // return the cached headers + if (headers) { + *headers = cached.cacheOfHeaders; + } + if (ptr) { // new object was shipped, old one (if any) is not valid anymore cached.fetches++; mFetches++; @@ -238,19 +300,44 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) cached.objPtr.reset(ptr); } cached.uuid = mHeaders["ETag"]; - try { // this conversion can throw, better to catch immediately - cached.startvalidity = std::stol(mHeaders["Valid-From"]); - cached.endvalidity = std::stol(mHeaders["Valid-Until"]); + + try { + if (mHeaders.find("Valid-From") != mHeaders.end()) { + cached.startvalidity = std::stol(mHeaders["Valid-From"]); + } else { + // if meta-information missing assume infinit validity + // (should happen only for locally created objects) + cached.startvalidity = 0; + } + if (mHeaders.find("Valid-Until") != mHeaders.end()) { + cached.endvalidity = std::stol(mHeaders["Valid-Until"]); + } else { + cached.endvalidity = std::numeric_limits::max(); + } + cached.cacheValidFrom = timestamp; } catch (std::exception const& e) { reportFatal("Failed to read validity from CCDB response (Valid-From : " + mHeaders["Valid-From"] + std::string(" Valid-Until: ") + mHeaders["Valid-Until"] + std::string(")")); } + auto sh = mHeaders.find("fileSize"); + if (sh != mHeaders.end()) { + size_t s = atol(sh->second.c_str()); + mFetchedSize += s; + mRequestedSize += s; + cached.size = s; + cached.minSize = std::min(s, cached.minSize); + cached.maxSize = std::max(s, cached.minSize); + } } else if (mHeaders.count("Error")) { // in case of errors the pointer is 0 and headers["Error"] should be set cached.failures++; cached.clear(); // in case of any error clear cache for this object - } else { // the old object is valid - ptr = reinterpret_cast(cached.noCleanupPtr ? cached.noCleanupPtr : cached.objPtr.get()); } - mHeaders.clear(); + // the old object is valid, fetch cache end of validity + ptr = reinterpret_cast(cached.noCleanupPtr ? cached.noCleanupPtr : cached.objPtr.get()); + if (mHeaders.find("Cache-Valid-Until") != mHeaders.end()) { + cached.cacheValidUntil = std::stol(mHeaders["Cache-Valid-Until"]); + } else { + cached.cacheValidUntil = -1; + } mMetaData.clear(); if (!ptr) { if (mFatalWhenNull) { @@ -261,9 +348,39 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) } auto end = std::chrono::system_clock::now(); mTimerMS += std::chrono::duration_cast(end - start).count(); + auto* ref = o2::framework::ServiceRegistryRef::globalDeviceRef(); + if (ref && ref->active()) { + auto& stats = ref->get(); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_HIT, o2::framework::DataProcessingStats::Op::Set, (int64_t)mQueries - mFailures - mFetches}); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_MISS, o2::framework::DataProcessingStats::Op::Set, (int64_t)mFetches}); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_FAILURE, o2::framework::DataProcessingStats::Op::Set, (int64_t)mFailures}); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_FETCHED_BYTES, o2::framework::DataProcessingStats::Op::Set, (int64_t)mFetchedSize}); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_REQUESTED_BYTES, o2::framework::DataProcessingStats::Op::Set, (int64_t)mRequestedSize}); + } return ptr; } +template +T* CCDBManagerInstance::getForRun(std::string const& path, int runNumber, bool setRunMetadata) +{ + auto metaData = setRunMetadata ? MD{{"runNumber", std::to_string(runNumber)}} : MD{}; + mMetaData = metaData; + return getSpecificForRun(path, runNumber, metaData); +} + +template +T* CCDBManagerInstance::getSpecificForRun(std::string const& path, int runNumber, MD const& metaData) +{ + auto [start, stop] = getRunDuration(runNumber, mFatalWhenNull); + if (start < 0 || stop < 0) { + if (mFatalWhenNull) { + reportFatal(std::string("Failed to get run duration for run ") + std::to_string(runNumber) + std::string(" from CCDB")); + } + return nullptr; + } + return getSpecific(path, start / 2 + stop / 2, metaData); +} + class BasicCCDBManager : public CCDBManagerInstance { public: @@ -291,4 +408,4 @@ class BasicCCDBManager : public CCDBManagerInstance } // namespace o2::ccdb -#endif //O2_BASICCCDBMANAGER_H +#endif // O2_BASICCCDBMANAGER_H diff --git a/CCDB/include/CCDB/CCDBDownloader.h b/CCDB/include/CCDB/CCDBDownloader.h index a8abb46dcff19..6c057a537a096 100644 --- a/CCDB/include/CCDB/CCDBDownloader.h +++ b/CCDB/include/CCDB/CCDBDownloader.h @@ -11,6 +11,10 @@ #ifndef O2_CCDBDOWNLOADER_H_ #define O2_CCDBDOWNLOADER_H_ +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) +#include "MemoryResources/MemoryResources.h" +#endif + #include #include #include @@ -21,6 +25,8 @@ #include #include #include +#include +#include typedef struct uv_loop_s uv_loop_t; typedef struct uv_timer_s uv_timer_t; @@ -29,11 +35,30 @@ typedef struct uv_signal_s uv_signal_t; typedef struct uv_async_s uv_async_t; typedef struct uv_handle_s uv_handle_t; -using namespace std; - namespace o2::ccdb { +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) +struct HeaderObjectPair_t { + std::multimap header; + o2::pmr::vector* object = nullptr; + int counter = 0; +}; + +typedef struct DownloaderRequestData { + std::vector hosts; + std::vector locations; + std::string path; + long timestamp; + HeaderObjectPair_t hoPair; + std::map* headers; + std::string userAgent; + curl_slist* optionsList; + + std::function localContentCallback; +} DownloaderRequestData; +#endif + /* Some functions below aren't member functions of CCDBDownloader because both curl and libuv require callback functions which have to be either static or non-member. Because non-static functions are used in the functions below, they must be non-member. @@ -63,6 +88,11 @@ curl_socket_t opensocketCallback(void* clientp, curlsocktype purpose, struct cur */ void onUVClose(uv_handle_t* handle); +enum DownloaderErrorLevel { + MINOR, + SEVERE +}; + /// A class encapsulating and performing simple CURL requests in terms of a so-called CURL multi-handle. /// A multi-handle allows to use a connection pool (connection cache) in the CURL layer even /// with short-lived CURL easy-handles. Thereby the overhead of connection to servers can be @@ -75,29 +105,50 @@ class CCDBDownloader public: /** * Timer starts for each socket when its respective transfer finishes, and is stopped when another transfer starts for that handle. - * When the timer runs out it closes the socket. The period for which socket stays open is defined by socketTimoutMS. + * When the timer runs out it closes the socket. The period for which socket stays open is defined by socketTimeoutMS. */ std::unordered_map mSocketTimerMap; /** - * The UV loop which handles transfers. + * The UV loop which handles transfers. Can be created internally or provided through a constructor. */ uv_loop_t* mUVLoop; + /** + * Map used to store active uv_handles belonging to the CcdbDownloader. If internal uv_loop is used, then all uv_handles should be marked in this map. + */ std::unordered_map mHandleMap; - // ADD COMMENT /** * Time for which sockets will stay open after last download finishes */ - int mSocketTimeoutMS = 4000; + int mKeepaliveTimeoutMS = 100; + + /** + * Time for connection to start before it times out. + */ + int mConnectionTimeoutMS = 60000; + + /** + * Time for request to finish before it times out. + */ + int mRequestTimeoutMS = 300000; + + /** + * Head start of IPv6 in regards to IPv4. + */ + int mHappyEyeballsHeadstartMS = 500; /** * Max number of handles that can be used at the same time */ int mMaxHandlesInUse = 3; - // CCDBDownloader(uv_loop_t uv_loop); + /** + * Variable denoting whether an external or internal uv_loop is being used. + */ + bool mExternalLoop; + CCDBDownloader(uv_loop_t* uv_loop = nullptr); ~CCDBDownloader(); @@ -108,13 +159,6 @@ class CCDBDownloader */ CURLcode perform(CURL* handle); - /** - * Perform on a batch of handles. Callback will be exectuted in it's own thread after all handles finish their transfers. - * - * @param handles Handles to be performed on. - */ - std::vector asynchBatchPerformWithCallback(std::vector const& handles, bool* completionFlag, void (*cbFun)(void*), void* cbData); - /** * Perform on a batch of handles in a blocking manner. Has the same effect as calling curl_easy_perform() on all handles in the vector. * @param handleVector Handles to be performed on. @@ -122,11 +166,12 @@ class CCDBDownloader std::vector batchBlockingPerform(std::vector const& handleVector); /** - * Perform on a batch of handles. Completion flag will be set to true when all handles finish their transfers. - * @param handleVector Handles to be performed on. - * @param completionFlag Should be set to false before passing it to this function. Will be set to true after all transfers finish. + * Schedules an asynchronous transfer but doesn't perform it. + * + * @param handle Handle to be performed on. + * @param requestCounter Counter shared by a batch of CURL handles. */ - std::vector batchAsynchPerform(std::vector const& handleVector, bool* completionFlag); + void asynchSchedule(CURL* handle, size_t* requestCounter); /** * Limits the number of parallel connections. Should be used only if no transfers are happening. @@ -136,62 +181,97 @@ class CCDBDownloader /** * Limits the time a socket and its connection will be opened after transfer finishes. */ - void setSocketTimoutTime(int timoutMS); + void setKeepaliveTimeoutTime(int timeoutMS); - private: /** - * Indicates whether the loop that the downloader is running on has been created by it or provided externally. - * In case of external loop, the loop will not be closed after downloader is deleted. + * Setter for the connection timeout. */ - bool mIsExternalLoop; + void setConnectionTimeoutTime(int timeoutMS); /** - * Current amount of handles which are performed on. + * Setter for the request timeout. */ - int mHandlesInUse = 0; + void setRequestTimeoutTime(int timeoutMS); /** - * Multi handle which controlls all network flow. + * Setter for the happy eyeballs headstart. */ - CURLM* mCurlMultiHandle = nullptr; + void setHappyEyeballsHeadstartTime(int headstartMS); /** - * The timeout clock that is be used by CURL. + * Sets the timeout values selected for the offline environment. */ - uv_timer_t* mTimeoutTimer; + void setOfflineTimeoutSettings(); /** - * Queue of handles awaiting their transfers to start. + * Sets the timeout values selected for the online environment. */ - std::vector mHandlesToBeAdded; + void setOnlineTimeoutSettings(); /** - * Lock protecting the mHandlesToBeAdded container + * Run the uvLoop once. + * + * @param noWait Using this flag will cause the loop to run only if sockets have pendind data. */ - std::mutex mHandlesQueueLock; + void runLoop(bool noWait); /** - * Thread on which the thread with uv_loop runs. + * Returns a message describing the transfer an it's result. */ - std::thread* mLoopThread; + std::string prepareLogMessage(std::string host_url, std::string userAgent, const std::string& path, long ts, const std::map* headers, long httpCode) const; /** - * Vector with reference to callback threads with a flag marking whether they finished running. + * Leaves only the protocol and host part of the url, discrading path and metadata. */ - std::vector> mThreadFlagPairVector; + std::string trimHostUrl(std::string full_host_url) const; + private: /** - * Flag used to signall the loop to close. + * Recognizes whether the address is a full url, or a partial one (like for example "/Task/Detector/1") and combines it with potentialHost if needed. */ - bool mCloseLoop = false; + std::string prepareRedirectedURL(std::string address, std::string potentialHost) const; + + /** + * Updates the locations vector with the the locations. + * + * @param headerMap Map containing response headers. + * @param locations Location list to be updated. + * @param locIndex Index of the next locaiton to be tried. + */ + void updateLocations(std::multimap* headerMap, std::vector* locations, int* locIndex) const; + + std::string mUserAgentId = "CCDBDownloader"; + /** + * Sets up internal UV loop. + */ + void setupInternalUVLoop(); + + /** + * Current amount of handles which are performed on. + */ + int mHandlesInUse = 0; + + /** + * Multi handle which controlls all network flow. + */ + CURLM* mCurlMultiHandle = nullptr; + + /** + * The timeout clock that is be used by CURL. + */ + uv_timer_t* mTimeoutTimer; + + /** + * Queue of handles awaiting their transfers to start. + */ + std::vector mHandlesToBeAdded; /** * Types of requests. */ enum RequestType { BLOCKING, - ASYNCHRONOUS, - ASYNCHRONOUS_WITH_CALLBACK + ASYNCHRONOUS }; /** @@ -213,29 +293,46 @@ class CCDBDownloader DataForSocket mSocketData; +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) /** * Structure which is stored in a easy_handle. It carries information about the request which the easy_handle is part of. - * All easy handles coming from one request have an identical PerformData structure. */ typedef struct PerformData { - std::condition_variable* cv; - bool* completionFlag; CURLcode* codeDestination; - void (*cbFun)(void*); - std::thread* cbThread; - void* cbData; size_t* requestsLeft; RequestType type; + int hostInd; + int locInd; + DownloaderRequestData* requestData; + curl_slist** options; } PerformData; +#endif /** - * Called by CURL in order to close a socket. It will be called by CURL even if a timout timer closed the socket beforehand. + * Called by CURL in order to close a socket. It will be called by CURL even if a timeout timer closed the socket beforehand. * * @param clientp Pointer to the CCDBDownloader instance which controls the socket. * @param item File descriptor of the socket. */ static void closesocketCallback(void* clientp, curl_socket_t item); +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) + // Returns a new location string or an empty string if all locations under current host have been accessedd + std::string getNewLocation(PerformData* performData, std::vector& locations) const; + + // Reschedules the transfer to be performed with a different host. + void tryNewHost(PerformData* performData, CURL* easy_handle); + + // Retrieves content from either alien, cvmfs or local storage using a callback to CCDBApi. + void getLocalContent(PerformData* performData, std::string& newLocation, bool& contentRetrieved, std::vector& locations); + + // Continues a transfer via a http redirect. + void httpRedirect(PerformData* performData, std::string& newLocation, CURL* easy_handle); + + // Continues a transfer via a redirect. The redirect can point to a local file, alien file or a http address. + void followRedirect(PerformData* performData, CURL* easy_handle, std::vector& locations, bool& rescheduled, bool& contentRetrieved); +#endif + /** * Is used to react to polling file descriptors in poll_handle. * @@ -245,25 +342,11 @@ class CCDBDownloader */ static void curlPerform(uv_poll_t* handle, int status, int events); - /** - * Check if loop was signalled to close. The handle connected with this callbacks is always active as to prevent the uv_loop from stopping. - * - * @param handle uv_handle to which this callbacks is assigned - */ - static void checkStopSignal(uv_timer_t* handle); - /** * Used by CURL to react to action happening on a socket. */ static int handleSocket(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp); - /** - * Asynchronously notify the loop to check its CURL handle queue. - * - * @param handle Handle which is assigned to this callback. - */ - static void asyncUVHandleCheckQueue(uv_async_t* handle); - /** * Close socket assigned to the timer handle. * @@ -295,17 +378,12 @@ class CCDBDownloader /** * Connect curl timer with uv timer. * - * @param multi Multi handle for which the timout will be set + * @param multi Multi handle for which the timeout will be set * @param timeout_ms Time until timeout * @param userp Pointer to the uv_timer_t handle that is used for timeout. */ static int startTimeout(CURLM* multi, long timeout_ms, void* userp); - /** - * Check if any of the callback threads have finished running and approprietly join them. - */ - void checkForThreadsToJoin(); - /** * Create a new multi_handle for the downloader */ @@ -324,10 +402,12 @@ class CCDBDownloader */ void checkMultiInfo(); +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) /** * Set openSocketCallback and closeSocketCallback with appropriate arguments. Stores data inside the CURL handle. */ void setHandleOptions(CURL* handle, PerformData* data); +#endif /** * Create structure holding information about a socket including a poll handle assigned to it @@ -336,20 +416,10 @@ class CCDBDownloader */ curl_context_t* createCurlContext(curl_socket_t sockfd); - /** - * Asynchroniously signal the event loop to check for new easy_handles to add to multi handle. - */ - void makeLoopCheckQueueAsync(); - /** * If multi_handles uses less then maximum number of handles then add handles from the queue. */ void checkHandleQueue(); - - /** - * Start the event loop. This function should be ran in the `loopThread`. - */ - void runLoop(); }; /** @@ -360,6 +430,6 @@ typedef struct DataForClosingSocket { curl_socket_t socket; } DataForClosingSocket; -} // namespace o2 +} // namespace o2::ccdb #endif // O2_CCDB_CCDBDOWNLOADER_H diff --git a/CCDB/include/CCDB/CcdbApi.h b/CCDB/include/CCDB/CcdbApi.h index b2bd50326eee6..acb0ad10f6c9a 100644 --- a/CCDB/include/CCDB/CcdbApi.h +++ b/CCDB/include/CCDB/CcdbApi.h @@ -24,20 +24,22 @@ #include #include #include "CCDB/CcdbObjectInfo.h" -#include "CCDB/CCDBDownloader.h" #include #include #include #if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) #include "MemoryResources/MemoryResources.h" +#include #include #else class TJAlienCredentials; #endif +#include "CCDB/CCDBDownloader.h" + class TFile; -class TGrid; +#include namespace o2 { @@ -62,6 +64,10 @@ class CcdbApi //: public DatabaseInterface /// \brief Default destructor virtual ~CcdbApi(); + // Delete copy and copy assignment constructor + CcdbApi(const CcdbApi&) = delete; + void operator=(const CcdbApi&) = delete; + const std::string getUniqueAgentID() const { return mUniqueAgentID; } static bool checkAlienToken(); @@ -119,34 +125,34 @@ class CcdbApi //: public DatabaseInterface static std::unique_ptr> createObjectImage(const void* obj, std::type_info const& tinfo, CcdbObjectInfo* info = nullptr); /** - * Store into the CCDB a TFile containing the ROOT object. - * - * @param rootObject Raw pointer to the object to store. - * @param path The path where the object is going to be stored. - * @param metadata Key-values representing the metadata for this object. - * @param startValidityTimestamp Start of validity. If omitted, current timestamp is used. - * @param endValidityTimestamp End of validity. If omitted, current timestamp + 1 day is used. - * @return 0 -> ok, - * positive number -> curl error (https://curl.se/libcurl/c/libcurl-errors.html), - * -1 : object bigger than maxSize, - * -2 : curl initialization error - */ + * Store into the CCDB a TFile containing the ROOT object. + * + * @param rootObject Raw pointer to the object to store. + * @param path The path where the object is going to be stored. + * @param metadata Key-values representing the metadata for this object. + * @param startValidityTimestamp Start of validity. If omitted, current timestamp is used. + * @param endValidityTimestamp End of validity. If omitted, current timestamp + 1 day is used. + * @return 0 -> ok, + * positive number -> curl error (https://curl.se/libcurl/c/libcurl-errors.html), + * -1 : object bigger than maxSize, + * -2 : curl initialization error + */ int storeAsTFile(const TObject* rootObject, std::string const& path, std::map const& metadata, long startValidityTimestamp = -1, long endValidityTimestamp = -1, std::vector::size_type maxSize = 0 /*bytes*/) const; /** - * Store into the CCDB a TFile containing an object of type T (which needs to have a ROOT dictionary) - * - * @param obj Raw pointer to the object to store. - * @param path The path where the object is going to be stored. - * @param metadata Key-values representing the metadata for this object. - * @param startValidityTimestamp Start of validity. If omitted, current timestamp is used. - * @param endValidityTimestamp End of validity. If omitted, current timestamp + 1 day is used. - * @return 0 -> ok, - * positive number -> curl error (https://curl.se/libcurl/c/libcurl-errors.html), - * -1 : object bigger than maxSize, - * -2 : curl initialization error - */ + * Store into the CCDB a TFile containing an object of type T (which needs to have a ROOT dictionary) + * + * @param obj Raw pointer to the object to store. + * @param path The path where the object is going to be stored. + * @param metadata Key-values representing the metadata for this object. + * @param startValidityTimestamp Start of validity. If omitted, current timestamp is used. + * @param endValidityTimestamp End of validity. If omitted, current timestamp + 1 day is used. + * @return 0 -> ok, + * positive number -> curl error (https://curl.se/libcurl/c/libcurl-errors.html), + * -1 : object bigger than maxSize, + * -2 : curl initialization error + */ template int storeAsTFileAny(const T* obj, std::string const& path, std::map const& metadata, long startValidityTimestamp = -1, long endValidityTimestamp = -1, std::vector::size_type maxSize = 0 /*bytes*/) const @@ -208,8 +214,9 @@ class CcdbApi //: public DatabaseInterface * @param metadata The metadata to update * @param timestamp The timestamp to select the object * @param id The id, if any, to select the object + * @return anithing non-0 is CURL error code or -1 */ - void updateMetadata(std::string const& path, std::map const& metadata, long timestamp, std::string const& id = "", long newEOV = 0); + int updateMetadata(std::string const& path, std::map const& metadata, long timestamp, std::string const& id = "", long newEOV = 0); /** * Return the listing of objects, and in some cases subfolders, matching this path. @@ -232,7 +239,7 @@ class CcdbApi //: public DatabaseInterface * @param returnFormat The format of the returned string -> one of "text/plain (default)", "application/json", "text/xml" * @return The listing of folder and/or objects in the format requested */ - std::string list(std::string const& path = "", bool latestOnly = false, std::string const& returnFormat = "text/plain") const; + std::string list(std::string const& path = "", bool latestOnly = false, std::string const& returnFormat = "text/plain", long createdNotAfter = -1, long createdNotBefore = -1) const; /** * Make a local snapshot of all valid objects, given a timestamp, of the CCDB under a given local path. @@ -248,12 +255,12 @@ class CcdbApi //: public DatabaseInterface bool isHostReachable() const; /** - * Helper function to extract the list of sub-folders from a list reply into a vector container. - * Can be used to achieve full recursive traversal/listing of the CCDB. - * - * @param reply The reply that we got from a GET/browse sort of request. - * @return The vector of sub-folders. - */ + * Helper function to extract the list of sub-folders from a list reply into a vector container. + * Can be used to achieve full recursive traversal/listing of the CCDB. + * + * @param reply The reply that we got from a GET/browse sort of request. + * @return The vector of sub-folders. + */ std::vector parseSubFolders(std::string const& reply) const; /** @@ -274,7 +281,7 @@ class CcdbApi //: public DatabaseInterface * @return: True in case operation successful or false if there was a failure/problem. */ bool retrieveBlob(std::string const& path, std::string const& targetdir, std::map const& metadata, long timestamp, - bool preservePathStructure = true, std::string const& localFileName = "snapshot.root", std::string const& createdNotAfter = "", std::string const& createdNotBefore = "") const; + bool preservePathStructure = true, std::string const& localFileName = "snapshot.root", std::string const& createdNotAfter = "", std::string const& createdNotBefore = "", std::map* headers = nullptr) const; /** * Retrieve the headers of a CCDB entry, if it exists. @@ -341,14 +348,57 @@ class CcdbApi //: public DatabaseInterface TObject* retrieveFromTFile(std::string const& path, std::map const& metadata, long timestamp, std::map* headers, std::string const& etag, const std::string& createdNotAfter, const std::string& createdNotBefore) const; + void loadFileToMemory(std::vector& dest, std::string const& path, + std::map const& metadata, long timestamp, + std::map* headers, std::string const& etag, + const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot = true) const; #if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) - void loadFileToMemory(o2::pmr::vector& dest, const std::string& path, std::map* localHeaders = nullptr) const; + typedef struct RequestContext { + o2::pmr::vector& dest; + std::string path; + std::map const& metadata; + long timestamp; + std::map& headers; + std::string etag; + std::string createdNotAfter; + std::string createdNotBefore; + bool considerSnapshot; + + RequestContext(o2::pmr::vector& d, + std::map const& m, + std::map& h) + : dest(d), metadata(m), headers(h) {} + } RequestContext; + + // Stores file associated with requestContext as a snapshot. + void saveSnapshot(RequestContext& requestContext) const; + + // Schedules download via CCDBDownloader, but doesn't perform it until mUVLoop is ran. + void scheduleDownload(RequestContext& requestContext, size_t* requestCounter) const; + + void getFromSnapshot(bool createSnapshot, std::string const& path, + long timestamp, std::map& headers, + std::string& snapshotpath, o2::pmr::vector& dest, int& fromSnapshot, std::string const& etag) const; + void releaseNamedSemaphore(boost::interprocess::named_semaphore* sem, std::string const& path) const; + boost::interprocess::named_semaphore* createNamedSemaphore(std::string const& path) const; + static std::string determineSemaphoreName(std::string const& basedir, std::string const& objectpath); + // queries and optionally removes a named semaphore from the system + // returns true when successful (either found or found + removed) + static bool removeSemaphore(std::string const& name, bool remove = false); + static void removeLeakingSemaphores(std::string const& basedir, bool remove = false); + + void loadFileToMemory(o2::pmr::vector& dest, const std::string& path, std::map* localHeaders = nullptr, bool fetchLocalMetaData = true) const; void loadFileToMemory(o2::pmr::vector& dest, std::string const& path, std::map const& metadata, long timestamp, std::map* headers, std::string const& etag, const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot = true) const; - void navigateURLsAndLoadFileToMemory(o2::pmr::vector& dest, CURL* curl_handle, std::string const& url, std::map* headers) const; + + // Loads files from alien and cvmfs into given destination. + bool loadLocalContentToMemory(o2::pmr::vector& dest, std::string& url) const; + + // add annotated flattened headers in the end of the blob + static void appendFlatHeader(o2::pmr::vector& dest, const std::map& headers); // the failure to load the file to memory is signaled by 0 size and non-0 capacity static bool isMemoryFileInvalid(const o2::pmr::vector& v) { return v.size() == 0 && v.capacity() > 0; } @@ -363,9 +413,36 @@ class CcdbApi //: public DatabaseInterface } return obj; } + + /** + * Retrieves files either as snapshot or schedules them to be downloaded via CCDBDownloader. + * + * @param requestContext Structure giving details about the transfer. + * @param fromSnapshot After navigateSourcesAndLoadFile returns signals whether file was retrieved from snapshot. + * @param requestCounter Pointer to the variable storing the number of requests to be done. + */ + void navigateSourcesAndLoadFile(RequestContext& requestContext, int& fromSnapshot, size_t* requestCounter) const; + + /** + * Retrieves files described via RequestContexts into memory. Downloads are performed in parallel via CCDBDownloader. + * + * @param requestContext Structure giving details about the transfer. + */ + void vectoredLoadFileToMemory(std::vector& requestContext) const; #endif private: + // Sets the unique agent ID + void setUniqueAgentID(); + + /** + * Schedules download of data associated with the curl_handle. Doing that increments the requestCounter by 1. Requests are performed by running the mUVLoop + * + * @param handle CURL handle associated with the request. + * @param requestCounter Pointer to the variable storing the number of requests to be done. + */ + void asynchPerform(CURL* handle, size_t* requestCounter) const; + // internal helper function to update a CCDB file with meta information static void updateMetaInformationInLocalFile(std::string const& filename, std::map const* headers, CCDBQuery const* querysummary = nullptr); @@ -447,6 +524,23 @@ class CcdbApi //: public DatabaseInterface long timestamp = -1, std::map* headers = nullptr, std::string const& etag = "", const std::string& createdNotAfter = "", const std::string& createdNotBefore = "") const; + /** + * Run the uvLoop belonging to mDownloader once. + * + * @param noWait Using this flag will cause the loop to run only if sockets have pendind data. + */ + void runDownloaderLoop(bool noWait); + /** + * Set the number of times curl should retry in case of failure and the delay between thte attempts. + * @param numberRetries + * @param delay + */ + void setCurlRetriesParameters(int numberRetries, int delay = 100000 /* microseconds */) + { + mCurlRetries = numberRetries; + mCurlDelayRetries = delay; + } + private: /** * A helper function to extract object from a local ROOT file @@ -457,12 +551,12 @@ class CcdbApi //: public DatabaseInterface void* extractFromLocalFile(std::string const& filename, std::type_info const& tinfo, std::map* headers) const; /** - * Helper function to download binary content from alien:// storage + * Helper function to download binary content from alien://, cvmfs or local storage * @param fullUrl The alien URL * @param tcl The TClass object describing the serialized type * @return raw pointer to created object */ - void* downloadAlienContent(std::string const& fullUrl, std::type_info const& tinfo) const; + void* downloadFilesystemContent(std::string const& fullUrl, std::type_info const& tinfo, std::map* headers) const; // initialize the TGrid (Alien connection) bool initTGrid() const; @@ -482,9 +576,6 @@ class CcdbApi //: public DatabaseInterface // convert type_info to TClass, throw on failure static TClass* tinfo2TClass(std::type_info const& tinfo); - // split string on delimiters and return tokens as vector - std::vector splitString(const std::string& str, const char* delimiters); - typedef size_t (*CurlWriteCallback)(void*, size_t, size_t, void*); void initCurlOptionsForRetrieve(CURL* curlHandle, void* pointer, CurlWriteCallback writeCallback, bool followRedirect = true) const; @@ -505,9 +596,9 @@ class CcdbApi //: public DatabaseInterface const std::string& createdNotAfter, const std::string& createdNotBefore, bool followRedirect, CurlWriteCallback writeCallback) const; /** - * Initialize hostsPool - * @param hosts string with hosts separated by "," or ";" - */ + * Initialize hostsPool + * @param hosts string with hosts separated by "," or ";" + */ void initHostsPool(std::string hosts); std::string getHostUrl(int hostIndex) const; @@ -524,12 +615,22 @@ class CcdbApi //: public DatabaseInterface return getSnapshotDir(topdir, path) + '/' + sfile; } + template // can be either std::map or std::multimap + static size_t getFlatHeaderSize(const MAP& Headers) + { + size_t hsize = sizeof(int) + sizeof(FlatHeaderAnnot); // annotation size + for (auto& h : Headers) { + hsize += h.first.length() + h.second.length() + 2; // 2*(string_buffer + terminating null character) + } + return hsize; + } + // tmp helper and single point of entry for a CURL perform call // helps to switch between easy handle perform and multi handles in a single place CURLcode CURL_perform(CURL* handle) const; mutable CCDBDownloader* mDownloader = nullptr; //! the multi-handle (async) CURL downloader - bool mIsCCDBDownloaderEnabled = false; + bool mIsCCDBDownloaderPreferred = false; /// Base URL of the CCDB (with port) std::string mUniqueAgentID{}; // Unique User-Agent ID communicated to server for logging std::string mUrl{}; @@ -541,6 +642,12 @@ class CcdbApi //: public DatabaseInterface mutable TGrid* mAlienInstance = nullptr; // a cached connection to TGrid (needed for Alien locations) bool mNeedAlienToken = true; // On EPN and FLP we use a local cache and don't need the alien token static std::unique_ptr mJAlienCredentials; // access JAliEn credentials + int mCurlRetries = 3; + int mCurlDelayRetries = 100000; // in microseconds + size_t mCurlTimeoutDownload = 15; // download timeout in seconds, can be configured via ALICEO2_CCDB_CURL_TIMEOUT_DOWNLOAD, updated according to the deployment mode + size_t mCurlTimeoutUpload = 15; // upload timeout in seconds, can be configured via ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD, updated according to the deployment mode + + static constexpr char FlatHeaderAnnot[] = "$HEADER$"; // annotation for flat header ClassDefNV(CcdbApi, 1); }; @@ -572,4 +679,4 @@ typename std::enable_if::value, } // namespace ccdb } // namespace o2 -#endif //PROJECT_CCDBAPI_H +#endif // PROJECT_CCDBAPI_H diff --git a/CCDB/include/CCDB/CcdbObjectInfo.h b/CCDB/include/CCDB/CcdbObjectInfo.h index 6db4cffd63b85..117ca1123104f 100644 --- a/CCDB/include/CCDB/CcdbObjectInfo.h +++ b/CCDB/include/CCDB/CcdbObjectInfo.h @@ -93,6 +93,16 @@ class CcdbObjectInfo [[nodiscard]] long getEndValidityTimestamp() const { return mEnd; } void setEndValidityTimestamp(long end) { mEnd = end; } + bool operator<(const CcdbObjectInfo& other) const + { + return mStart < other.mStart; + } + + bool operator>(const CcdbObjectInfo& other) const + { + return mStart > other.mStart; + } + private: std::string mObjType{}; // object type (e.g. class) std::string mFileName{}; // file name in the CCDB @@ -107,4 +117,17 @@ class CcdbObjectInfo } // namespace o2::ccdb +namespace std +{ +// defining std::hash for InteractionRecord to be used with std containers +template <> +struct hash { + public: + size_t operator()(const o2::ccdb::CcdbObjectInfo& info) const + { + return info.getStartValidityTimestamp(); + } +}; +} // namespace std + #endif // O2_CCDB_CCDBOBJECTINFO_H_ diff --git a/CCDB/src/BasicCCDBManager.cxx b/CCDB/src/BasicCCDBManager.cxx index 148de1048b4d9..53055d0d1231d 100644 --- a/CCDB/src/BasicCCDBManager.cxx +++ b/CCDB/src/BasicCCDBManager.cxx @@ -13,6 +13,8 @@ // Created by Sandro Wenzel on 2019-08-14. // #include "CCDB/BasicCCDBManager.h" +#include "Framework/ServiceRegistryRef.h" +#include "Framework/DataProcessingStats.h" #include #include #include @@ -32,29 +34,74 @@ void CCDBManagerInstance::reportFatal(std::string_view err) LOG(fatal) << err; } -std::pair CCDBManagerInstance::getRunDuration(o2::ccdb::CcdbApi const& api, int runnumber, bool fatal) +std::pair CCDBManagerInstance::getRunDuration(const std::map& headers) { - auto response = api.retrieveHeaders("RCT/Info/RunInformation", std::map(), runnumber); - if (response.size() == 0 || response.find("SOR") == response.end() || response.find("EOR") == response.end()) { - if (fatal) { - LOG(fatal) << "Empty or missing response from query to RCT/Info/RunInformation for run " << runnumber; - } else { - return std::make_pair(-1L, -1L); + if (headers.size() != 0) { + std::string report{}; + auto strt = headers.find("STF"); + auto stop = headers.find("ETF"); + long valStrt = (strt == headers.end()) ? -1L : boost::lexical_cast(strt->second); + long valStop = (stop == headers.end()) ? -1L : boost::lexical_cast(stop->second); + if (valStrt < 0 || valStop < 0) { + report += "Missing STF/EFT -> use SOX/EOX;"; + strt = headers.find("SOX"); + valStrt = (strt == headers.end()) ? -1L : boost::lexical_cast(strt->second); + if (valStrt < 1) { + report += fmt::format(" Missing/invalid SOX -> use SOR"); + strt = headers.find("SOR"); + valStrt = (strt == headers.end()) ? -1L : boost::lexical_cast(strt->second); + } + stop = headers.find("EOX"); + valStop = (stop == headers.end()) ? -1L : boost::lexical_cast(stop->second); + if (valStop < 1) { + report += fmt::format(" | Missing/invalid EOX -> use EOR"); + stop = headers.find("EOR"); + valStop = (stop == headers.end()) ? -1L : boost::lexical_cast(stop->second); + } + if (!report.empty()) { + LOGP(warn, "{}", report); + } } + return std::make_pair(valStrt, valStop); } - auto sor = boost::lexical_cast(response["SOR"]); - auto eor = boost::lexical_cast(response["EOR"]); - return std::make_pair(sor, eor); + return std::make_pair(-1L, -1L); } -std::pair CCDBManagerInstance::getRunDuration(int runnumber, bool fatal) const +std::pair CCDBManagerInstance::getRunDuration(o2::ccdb::CcdbApi const& api, int runnumber, bool fatal) { - return CCDBManagerInstance::getRunDuration(mCCDBAccessor, runnumber, fatal); + auto headers = api.retrieveHeaders("RCT/Info/RunInformation", std::map(), runnumber); + auto response = getRunDuration(headers); + if ((response.first <= 0 || response.second < response.first) && fatal) { + LOG(fatal) << "Empty, missing or invalid response from query to RCT/Info/RunInformation for run " << runnumber; + } + return response; +} + +std::pair CCDBManagerInstance::getRunDuration(int runnumber, bool fatal) +{ + mQueries++; + if (!isCachingEnabled()) { + return CCDBManagerInstance::getRunDuration(mCCDBAccessor, runnumber, fatal); + } + auto& cached = mCache["RCT-Run-Info HeaderOnly"]; + std::pair rd; + cached.queries++; + if (cached.startvalidity != runnumber) { // need to fetch + rd = CCDBManagerInstance::getRunDuration(mCCDBAccessor, runnumber, fatal); + cached.objPtr = std::make_shared>(rd); + cached.startvalidity = runnumber; + cached.endvalidity = runnumber + 1; + cached.minSize = cached.maxSize = 0; + cached.fetches++; + } else { + rd = *reinterpret_cast*>(cached.objPtr.get()); + } + return rd; } std::string CCDBManagerInstance::getSummaryString() const { - std::string res = fmt::format("{} queries", mQueries); + std::string res = fmt::format("{} queries, {} fetched / {} requested bytes", mQueries, fmt::group_digits(mFetchedSize), fmt::group_digits(mRequestedSize)); if (mCachingEnabled) { res += fmt::format(" for {} objects", mCache.size()); } @@ -72,9 +119,20 @@ std::string CCDBManagerInstance::getSummaryString() const return res; } -void CCDBManagerInstance::endOfStream() +void CCDBManagerInstance::report(bool longrep) { LOG(info) << "CCDBManager summary: " << getSummaryString(); + if (longrep && mCachingEnabled) { + LOGP(info, "CCDB cache miss/hit/failures"); + for (const auto& obj : mCache) { + LOGP(info, " {}: {}/{}/{} ({}-{} bytes)", obj.first, obj.second.fetches, obj.second.queries - obj.second.fetches - obj.second.failures, obj.second.failures, obj.second.minSize, obj.second.maxSize); + } + } +} + +void CCDBManagerInstance::endOfStream() +{ + report(true); } } // namespace ccdb diff --git a/CCDB/src/CCDBDownloader.cxx b/CCDB/src/CCDBDownloader.cxx index c879a245c6987..2f033a50b36e7 100644 --- a/CCDB/src/CCDBDownloader.cxx +++ b/CCDB/src/CCDBDownloader.cxx @@ -10,10 +10,11 @@ // or submit itself to any jurisdiction. #include +#include "CommonUtils/StringUtils.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "Framework/Signpost.h" #include -#include - #include #include #include @@ -26,133 +27,153 @@ #include #include #include +#include +#include + +O2_DECLARE_DYNAMIC_STACKTRACE_LOG(ccdb_downloader); namespace o2::ccdb { +void uvErrorCheck(int code, DownloaderErrorLevel level) +{ + if (code != 0) { + char buf[1000]; + uv_strerror_r(code, buf, 1000); + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + if (level == SEVERE) { + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "UV error - %{public}s", buf); + } else { + O2_SIGNPOST_EVENT_EMIT_WARN(ccdb_downloader, sid, "CCDBDownloader", "UV minor error - %{public}s", buf); + } + } +} + +void curlEasyErrorCheck(CURLcode code) +{ + if (code != CURLE_OK) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CURL error - %{public}s", curl_easy_strerror(code)); + } +} + +void curlMultiErrorCheck(CURLMcode code) +{ + if (code != CURLM_OK) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CURL error - %{public}s", curl_multi_strerror(code)); + } +} +namespace +{ +std::string uniqueAgentID() +{ + std::string host = boost::asio::ip::host_name(); + char const* jobID = getenv("ALIEN_PROC_ID"); + if (jobID) { + return fmt::format("{}-{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6), jobID); + } else { + return fmt::format("{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6)); + } +} +} // namespace + CCDBDownloader::CCDBDownloader(uv_loop_t* uv_loop) + : mUserAgentId(uniqueAgentID()) { if (uv_loop) { + mExternalLoop = true; mUVLoop = uv_loop; - mIsExternalLoop = true; } else { - mUVLoop = new uv_loop_t(); - mIsExternalLoop = false; + mExternalLoop = false; + setupInternalUVLoop(); } // Preparing timer to be used by curl - mTimeoutTimer = new uv_timer_t(); + mTimeoutTimer = (uv_timer_t*)malloc(sizeof(*mTimeoutTimer)); mTimeoutTimer->data = this; - uv_loop_init(mUVLoop); - uv_timer_init(mUVLoop, mTimeoutTimer); + uvErrorCheck(uv_timer_init(mUVLoop, mTimeoutTimer), SEVERE); mHandleMap[(uv_handle_t*)mTimeoutTimer] = true; - // Preparing curl handle initializeMultiHandle(); +} - // Global timer - // uv_loop runs only when there are active handles, this handle guarantees the loop won't close immedietly after starting - auto timerCheckQueueHandle = new uv_timer_t(); - timerCheckQueueHandle->data = this; - uv_timer_init(mUVLoop, timerCheckQueueHandle); - mHandleMap[(uv_handle_t*)timerCheckQueueHandle] = true; - uv_timer_start(timerCheckQueueHandle, checkStopSignal, 100, 100); - - mLoopThread = new std::thread(&CCDBDownloader::runLoop, this); +void CCDBDownloader::setupInternalUVLoop() +{ + mUVLoop = new uv_loop_t(); + uvErrorCheck(uv_loop_init(mUVLoop), SEVERE); } void CCDBDownloader::initializeMultiHandle() { mCurlMultiHandle = curl_multi_init(); - curl_multi_setopt(mCurlMultiHandle, CURLMOPT_SOCKETFUNCTION, handleSocket); + curlMultiErrorCheck(curl_multi_setopt(mCurlMultiHandle, CURLMOPT_SOCKETFUNCTION, handleSocket)); auto socketData = &mSocketData; socketData->curlm = mCurlMultiHandle; socketData->CD = this; - curl_multi_setopt(mCurlMultiHandle, CURLMOPT_SOCKETDATA, socketData); - curl_multi_setopt(mCurlMultiHandle, CURLMOPT_TIMERFUNCTION, startTimeout); - curl_multi_setopt(mCurlMultiHandle, CURLMOPT_TIMERDATA, mTimeoutTimer); - curl_multi_setopt(mCurlMultiHandle, CURLMOPT_MAX_TOTAL_CONNECTIONS, mMaxHandlesInUse); + curlMultiErrorCheck(curl_multi_setopt(mCurlMultiHandle, CURLMOPT_SOCKETDATA, socketData)); + curlMultiErrorCheck(curl_multi_setopt(mCurlMultiHandle, CURLMOPT_TIMERFUNCTION, startTimeout)); + curlMultiErrorCheck(curl_multi_setopt(mCurlMultiHandle, CURLMOPT_TIMERDATA, mTimeoutTimer)); + curlMultiErrorCheck(curl_multi_setopt(mCurlMultiHandle, CURLMOPT_MAX_TOTAL_CONNECTIONS, mMaxHandlesInUse)); } CCDBDownloader::~CCDBDownloader() { - // Cleanup and close all socket timers (curl_multi_cleanup will take care of the sockets) - for (auto socketTimerPair : mSocketTimerMap) { - auto timer = socketTimerPair.second; - if (timer->data) { - delete (DataForClosingSocket*)timer->data; - } - uv_timer_stop(socketTimerPair.second); - uv_close((uv_handle_t*)socketTimerPair.second, onUVClose); - } - // all timers have been closed --> so clear this map (otherwise it may get accessed in different callbacks again - // ... for instance when called from within curl_multi_cleanup) - mSocketTimerMap.clear(); - - // Close loop thread - mCloseLoop = true; - mLoopThread->join(); - delete mLoopThread; - - // Close the loop and if any handles are running then signal to close, and run loop once to close them - // This may take more then one iteration of loop - hence the "while" - if (mIsExternalLoop) { - uv_walk(mUVLoop, closeHandles, this); - } else { - while (UV_EBUSY == uv_loop_close(mUVLoop)) { - mCloseLoop = false; + // Loop has been ordered to stop via signalToClose() + curlMultiErrorCheck(curl_multi_cleanup(mCurlMultiHandle)); + + if (!mExternalLoop) { + // Schedule all handles to close. Execute loop to allow them to execute their destructors. + while (uv_loop_alive(mUVLoop) || (uv_loop_close(mUVLoop) == UV_EBUSY)) { uv_walk(mUVLoop, closeHandles, this); uv_run(mUVLoop, UV_RUN_ONCE); } delete mUVLoop; } - - // delete timer - // delete mTimeoutTimer; ---> not necessay (done elsewhere??) - - curl_multi_cleanup(mCurlMultiHandle); } void closeHandles(uv_handle_t* handle, void* arg) { auto CD = (CCDBDownloader*)arg; - if (!uv_is_closing(handle) && CD->mHandleMap.find(handle) != CD->mHandleMap.end()) { - CD->mHandleMap.erase(handle); - uv_close(handle, onUVClose); + // Close only handles belonging to the Downloader + if (CD->mHandleMap.find(handle) != CD->mHandleMap.end()) { + if (!uv_is_closing(handle)) { + uv_close(handle, onUVClose); + } } } void onUVClose(uv_handle_t* handle) { if (handle != nullptr) { - delete handle; + free(handle); } } -void CCDBDownloader::checkStopSignal(uv_timer_t* handle) -{ - // Check for closing signal - auto CD = (CCDBDownloader*)handle->data; - if (CD->mCloseLoop) { - uv_timer_stop(handle); - uv_stop(CD->mUVLoop); - } - CD->checkForThreadsToJoin(); -} - void CCDBDownloader::closesocketCallback(void* clientp, curl_socket_t item) { auto CD = (CCDBDownloader*)clientp; - if (CD->mSocketTimerMap.find(item) != CD->mSocketTimerMap.end()) { - auto timer = CD->mSocketTimerMap[item]; - uv_timer_stop(timer); - // we are getting rid of the uv_timer_t pointer ... so we need - // to free possibly attached user data pointers as well. Counteracts action of opensocketCallback - if (timer->data) { - delete (DataForClosingSocket*)timer->data; + if (CD->mExternalLoop) { + // If external uv loop is used then the keepalive mechanism is active. + if (CD->mSocketTimerMap.find(item) != CD->mSocketTimerMap.end()) { + auto timer = CD->mSocketTimerMap[item]; + uvErrorCheck(uv_timer_stop(timer), SEVERE); + // we are getting rid of the uv_timer_t pointer ... so we need + // to free possibly attached user data pointers as well. Counteracts action of opensocketCallback + if (timer->data) { + delete (DataForClosingSocket*)timer->data; + } + CD->mSocketTimerMap.erase(item); + if (close(item) == -1) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to close"); + } + } + } else { + if (close(item) == -1) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to close"); } - CD->mSocketTimerMap.erase(item); - close(item); } } @@ -160,26 +181,25 @@ curl_socket_t opensocketCallback(void* clientp, curlsocktype purpose, struct cur { auto CD = (CCDBDownloader*)clientp; auto sock = socket(address->family, address->socktype, address->protocol); + if (sock == -1) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to open"); + } - CD->mSocketTimerMap[sock] = new uv_timer_t(); - uv_timer_init(CD->mUVLoop, CD->mSocketTimerMap[sock]); - CD->mHandleMap[(uv_handle_t*)CD->mSocketTimerMap[sock]] = true; + if (CD->mExternalLoop) { + CD->mSocketTimerMap[sock] = (uv_timer_t*)malloc(sizeof(*CD->mSocketTimerMap[sock])); + uvErrorCheck(uv_timer_init(CD->mUVLoop, CD->mSocketTimerMap[sock]), SEVERE); + CD->mHandleMap[(uv_handle_t*)CD->mSocketTimerMap[sock]] = true; - auto data = new DataForClosingSocket(); - data->CD = CD; - data->socket = sock; - CD->mSocketTimerMap[sock]->data = data; + auto data = new DataForClosingSocket(); + data->CD = CD; + data->socket = sock; + CD->mSocketTimerMap[sock]->data = data; + } return sock; } -void CCDBDownloader::asyncUVHandleCheckQueue(uv_async_t* handle) -{ - auto CD = (CCDBDownloader*)handle->data; - uv_close((uv_handle_t*)handle, onUVClose); - CD->checkHandleQueue(); -} - void CCDBDownloader::closeSocketByTimer(uv_timer_t* handle) { auto data = (DataForClosingSocket*)handle->data; @@ -187,10 +207,12 @@ void CCDBDownloader::closeSocketByTimer(uv_timer_t* handle) auto sock = data->socket; if (CD->mSocketTimerMap.find(sock) != CD->mSocketTimerMap.end()) { - uv_timer_stop(CD->mSocketTimerMap[sock]); + uvErrorCheck(uv_timer_stop(CD->mSocketTimerMap[sock]), SEVERE); CD->mSocketTimerMap.erase(sock); - close(sock); - + if (close(sock) == -1) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to close"); + } delete data; } } @@ -205,6 +227,7 @@ void CCDBDownloader::curlTimeout(uv_timer_t* handle) void CCDBDownloader::curlPerform(uv_poll_t* handle, int status, int events) { + uvErrorCheck(status, MINOR); int running_handles; int flags = 0; if (events & UV_READABLE) { @@ -216,7 +239,7 @@ void CCDBDownloader::curlPerform(uv_poll_t* handle, int status, int events) auto context = (CCDBDownloader::curl_context_t*)handle->data; - curl_multi_socket_action(context->CD->mCurlMultiHandle, context->sockfd, flags, &running_handles); + curlMultiErrorCheck(curl_multi_socket_action(context->CD->mCurlMultiHandle, context->sockfd, flags, &running_handles)); context->CD->checkMultiInfo(); } @@ -233,7 +256,7 @@ int CCDBDownloader::handleSocket(CURL* easy, curl_socket_t s, int action, void* case CURL_POLL_INOUT: curl_context = socketp ? (CCDBDownloader::curl_context_t*)socketp : CD->createCurlContext(s); - curl_multi_assign(socketData->curlm, s, (void*)curl_context); + curlMultiErrorCheck(curl_multi_assign(socketData->curlm, s, (void*)curl_context)); if (action != CURL_POLL_IN) { events |= UV_WRITABLE; @@ -242,20 +265,23 @@ int CCDBDownloader::handleSocket(CURL* easy, curl_socket_t s, int action, void* events |= UV_READABLE; } - if (CD->mSocketTimerMap.find(s) != CD->mSocketTimerMap.end()) { - uv_timer_stop(CD->mSocketTimerMap[s]); + if (CD->mExternalLoop && CD->mSocketTimerMap.find(s) != CD->mSocketTimerMap.end()) { + uvErrorCheck(uv_timer_stop(CD->mSocketTimerMap[s]), SEVERE); } - uv_poll_start(curl_context->poll_handle, events, curlPerform); + uvErrorCheck(uv_poll_start(curl_context->poll_handle, events, curlPerform), SEVERE); break; case CURL_POLL_REMOVE: if (socketp) { - if (CD->mSocketTimerMap.find(s) != CD->mSocketTimerMap.end()) { - uv_timer_start(CD->mSocketTimerMap[s], closeSocketByTimer, CD->mSocketTimeoutMS, 0); + if (CD->mExternalLoop) { + // If external loop is used then start the keepalive timeout. + if (CD->mSocketTimerMap.find(s) != CD->mSocketTimerMap.end()) { + uvErrorCheck(uv_timer_start(CD->mSocketTimerMap[s], closeSocketByTimer, CD->mKeepaliveTimeoutMS, 0), SEVERE); + } } - uv_poll_stop(((CCDBDownloader::curl_context_t*)socketp)->poll_handle); + uvErrorCheck(uv_poll_stop(((CCDBDownloader::curl_context_t*)socketp)->poll_handle), SEVERE); CD->destroyCurlContext((CCDBDownloader::curl_context_t*)socketp); - curl_multi_assign(socketData->curlm, s, nullptr); + curlMultiErrorCheck(curl_multi_assign(socketData->curlm, s, nullptr)); } break; default: @@ -270,21 +296,38 @@ void CCDBDownloader::setMaxParallelConnections(int limit) mMaxHandlesInUse = limit; } -void CCDBDownloader::setSocketTimoutTime(int timoutMS) +void CCDBDownloader::setKeepaliveTimeoutTime(int timeoutMS) { - mSocketTimeoutMS = timoutMS; + mKeepaliveTimeoutMS = timeoutMS; } -void CCDBDownloader::checkForThreadsToJoin() +void CCDBDownloader::setConnectionTimeoutTime(int timeoutMS) { - for (int i = 0; i < mThreadFlagPairVector.size(); i++) { - if (*(mThreadFlagPairVector[i].second)) { - mThreadFlagPairVector[i].first->join(); - delete (mThreadFlagPairVector[i].first); - delete (mThreadFlagPairVector[i].second); - mThreadFlagPairVector.erase(mThreadFlagPairVector.begin() + i); - } - } + mConnectionTimeoutMS = timeoutMS; +} + +void CCDBDownloader::setRequestTimeoutTime(int timeoutMS) +{ + mRequestTimeoutMS = timeoutMS; +} + +void CCDBDownloader::setHappyEyeballsHeadstartTime(int headstartMS) +{ + mHappyEyeballsHeadstartMS = headstartMS; +} + +void CCDBDownloader::setOfflineTimeoutSettings() +{ + setConnectionTimeoutTime(60000); + setRequestTimeoutTime(300000); + setHappyEyeballsHeadstartTime(500); +} + +void CCDBDownloader::setOnlineTimeoutSettings() +{ + setConnectionTimeoutTime(5000); + setRequestTimeoutTime(30000); + setHappyEyeballsHeadstartTime(500); } CCDBDownloader::curl_context_t* CCDBDownloader::createCurlContext(curl_socket_t sockfd) @@ -294,9 +337,9 @@ CCDBDownloader::curl_context_t* CCDBDownloader::createCurlContext(curl_socket_t context = (curl_context_t*)malloc(sizeof(*context)); context->CD = this; context->sockfd = sockfd; - context->poll_handle = new uv_poll_t(); + context->poll_handle = (uv_poll_t*)malloc(sizeof(*context->poll_handle)); - uv_poll_init_socket(mUVLoop, context->poll_handle, sockfd); + uvErrorCheck(uv_poll_init_socket(mUVLoop, context->poll_handle, sockfd), SEVERE); mHandleMap[(uv_handle_t*)(context->poll_handle)] = true; context->poll_handle->data = context; @@ -306,7 +349,7 @@ CCDBDownloader::curl_context_t* CCDBDownloader::createCurlContext(curl_socket_t void CCDBDownloader::curlCloseCB(uv_handle_t* handle) { auto* context = (curl_context_t*)handle->data; - delete context->poll_handle; + free(context->poll_handle); free(context); } @@ -315,46 +358,233 @@ void CCDBDownloader::destroyCurlContext(curl_context_t* context) uv_close((uv_handle_t*)context->poll_handle, curlCloseCB); } -void callbackWrappingFunction(void (*cbFun)(void*), void* data, bool* completionFlag) +void CCDBDownloader::tryNewHost(PerformData* performData, CURL* easy_handle) +{ + auto requestData = performData->requestData; + std::string newUrl = requestData->hosts.at(performData->hostInd) + "/" + requestData->path + "/" + std::to_string(requestData->timestamp); + LOG(debug) << "Connecting to another host " << newUrl << "\n"; + requestData->hoPair.header.clear(); + curl_easy_setopt(easy_handle, CURLOPT_URL, newUrl.c_str()); + mHandlesToBeAdded.push_back(easy_handle); +} + +void CCDBDownloader::getLocalContent(PerformData* performData, std::string& newLocation, bool& contentRetrieved, std::vector& locations) +{ + auto requestData = performData->requestData; + LOG(debug) << "Redirecting to local content " << newLocation << "\n"; + if (requestData->localContentCallback(newLocation)) { + contentRetrieved = true; + LOG(debug) << "Local content retrieved succesfully: " << newLocation << " n"; + } else { + // Prepare next redirect url + newLocation = getNewLocation(performData, locations); + LOG(debug) << "Failed to retrieve local content: " << newLocation << "\n"; + } +} + +std::string CCDBDownloader::getNewLocation(PerformData* performData, std::vector& locations) const { - cbFun(data); - *completionFlag = true; + auto requestData = performData->requestData; + if (performData->locInd < locations.size()) { + std::string newLocation = locations.at(performData->locInd++); + std::string hostUrl = requestData->hosts.at(performData->hostInd); + std::string newUrl = prepareRedirectedURL(newLocation, hostUrl); + return newUrl; + } else { + return ""; + } +} + +void CCDBDownloader::httpRedirect(PerformData* performData, std::string& newLocation, CURL* easy_handle) +{ + auto requestData = performData->requestData; + LOG(debug) << "Trying content location " << newLocation << "\n"; + curl_easy_setopt(easy_handle, CURLOPT_URL, newLocation.c_str()); + mHandlesToBeAdded.push_back(easy_handle); +} + +void CCDBDownloader::followRedirect(PerformData* performData, CURL* easy_handle, std::vector& locations, bool& rescheduled, bool& contentRetrieved) +{ + std::string newLocation = getNewLocation(performData, locations); + while (!contentRetrieved && (newLocation.find("alien:/", 0) != std::string::npos || newLocation.find("file:/", 0) != std::string::npos)) { + getLocalContent(performData, newLocation, contentRetrieved, locations); + } + if (!contentRetrieved && newLocation != "") { + httpRedirect(performData, newLocation, easy_handle); + rescheduled = true; + } +} + +std::string CCDBDownloader::trimHostUrl(std::string full_host_url) const +{ + CURLU* host_url = curl_url(); + curl_url_set(host_url, CURLUPART_URL, full_host_url.c_str(), 0); + + // Get host part (the only critical part) + char* host; + CURLUcode host_result = curl_url_get(host_url, CURLUPART_HOST, &host, 0); + if (host_result != CURLUE_OK) { + LOG(error) << "CCDBDownloader: Malformed url detected when processing redirect, could not identify the host part: " << full_host_url; + curl_url_cleanup(host_url); + return ""; + } + // Get scheme (protocol) part + char* scheme; + CURLUcode scheme_result = curl_url_get(host_url, CURLUPART_SCHEME, &scheme, 0); + // Get port + char* port; + CURLUcode port_result = curl_url_get(host_url, CURLUPART_PORT, &port, 0); + + curl_url_cleanup(host_url); + + // Assemble parts + std::string trimmed_url = ""; + if (scheme_result == CURLUE_OK) { + trimmed_url += scheme + std::string("://"); + free(scheme); + } + trimmed_url += host; + free(host); + if (port_result == CURLUE_OK) { + trimmed_url += std::string(":") + port; + free(port); + } + return trimmed_url; +} + +std::string CCDBDownloader::prepareRedirectedURL(std::string address, std::string potentialHost) const +{ + // If it is an alien or local address it does not need preparation + if (address.find("alien:/") != std::string::npos || address.find("file:/") != std::string::npos) { + return address; + } + // Check if URL contains a scheme (protocol) + CURLU* redirected_url = curl_url(); + curl_url_set(redirected_url, CURLUPART_URL, address.c_str(), 0); + char* scheme; + CURLUcode scheme_result = curl_url_get(redirected_url, CURLUPART_SCHEME, &scheme, 0); + curl_free(scheme); + curl_url_cleanup(redirected_url); + if (scheme_result == CURLUE_OK) { + // The redirected_url contains a scheme (protocol) so there is no need for preparation + return address; + } + // If the address doesn't contain a scheme it means it is a relative url. We need to append it to the trimmed host url + // The host url must be trimmed from it's path (if it ends in one) as otherwise the redirection url would be appended after said path + return trimHostUrl(potentialHost) + address; } void CCDBDownloader::transferFinished(CURL* easy_handle, CURLcode curlCode) { mHandlesInUse--; - PerformData* data; - curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &data); + PerformData* performData; + curlEasyErrorCheck(curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &performData)); - curl_multi_remove_handle(mCurlMultiHandle, easy_handle); - *data->codeDestination = curlCode; + curlMultiErrorCheck(curl_multi_remove_handle(mCurlMultiHandle, easy_handle)); + *performData->codeDestination = curlCode; - // If no requests left then signal finished based on type of operation - if (--(*data->requestsLeft) == 0) { - switch (data->type) { - case BLOCKING: - data->cv->notify_all(); - break; - case ASYNCHRONOUS: - *data->completionFlag = true; - break; - case ASYNCHRONOUS_WITH_CALLBACK: - *data->completionFlag = true; - bool* cbFlag = (bool*)malloc(sizeof(bool)); - *cbFlag = false; - auto cbThread = new std::thread(&callbackWrappingFunction, data->cbFun, data->cbData, cbFlag); - mThreadFlagPairVector.emplace_back(cbThread, cbFlag); - break; - } + bool rescheduled = false; + bool contentRetrieved = false; + + if (curlCode != 0) { + LOG(error) << "CCDBDownloader CURL transfer error - " << curl_easy_strerror(curlCode) << "\n"; + } + + switch (performData->type) { + case BLOCKING: { + --(*performData->requestsLeft); + } break; + case ASYNCHRONOUS: { + DownloaderRequestData* requestData = performData->requestData; + if (requestData->headers) { + for (auto& p : requestData->hoPair.header) { + (*requestData->headers)[p.first] = p.second; + } + } + // Log that transfer finished + long httpCode; + curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &httpCode); + char* url; + curl_easy_getinfo(easy_handle, CURLINFO_EFFECTIVE_URL, &url); + LOG(debug) << "Transfer for " << url << " finished with code " << httpCode << "\n"; + std::string currentHost = requestData->hosts[performData->hostInd]; + std::string loggingMessage = prepareLogMessage(currentHost, requestData->userAgent, requestData->path, requestData->timestamp, requestData->headers, httpCode); + + // Get new locations based on received headers + updateLocations(&(requestData->hoPair.header), &requestData->locations, &performData->locInd); + + // React to received http code + if (200 <= httpCode && httpCode < 400) { + LOG(debug) << loggingMessage; + if (304 == httpCode) { + LOGP(debug, "Object exists but I am not serving it since it's already in your possession"); + contentRetrieved = true; + } else if (300 <= httpCode && httpCode < 400 && performData->locInd < requestData->locations.size()) { + followRedirect(performData, easy_handle, requestData->locations, rescheduled, contentRetrieved); + } else if (200 <= httpCode && httpCode < 300) { + contentRetrieved = true; // Can be overruled by following error check + } + } else { + LOG(error) << loggingMessage; + } + + // Check for errors + if (curlCode != 0) { + contentRetrieved = false; + } + + // Check if content was retrieved or scheduled to be retrieved + if (!rescheduled && !contentRetrieved) { + // Current location failed without providing 3xx http code, try next redirect for the same host + if (performData->locInd < requestData->locations.size()) { + followRedirect(performData, easy_handle, requestData->locations, rescheduled, contentRetrieved); + } + } + + // Check again because content might have been retrieved or rescheduled via a redirect + if (!rescheduled && !contentRetrieved) { + // Ran out of locations to redirect, try new host + if (++performData->hostInd < requestData->hosts.size()) { + tryNewHost(performData, easy_handle); + rescheduled = true; + } else { + LOG(error) << "File " << requestData->path << " could not be retrieved. No more hosts to try."; + } + } + + if (!rescheduled) { + // No more transfers will be done for this request, do cleanup specific for ASYNCHRONOUS calls + if (!contentRetrieved) { + if (requestData->hoPair.object) { + requestData->hoPair.object->clear(); + } + if (requestData->headers) { + (*requestData->headers)["Error"] = "An error occurred during retrieval"; + } + LOGP(alarm, "Curl request to {}, response code: {}", url, httpCode); + } else { + if (requestData->headers && requestData->headers->find("fileSize") == requestData->headers->end()) { + (*requestData->headers)["fileSize"] = fmt::format("{}", requestData->hoPair.object ? requestData->hoPair.object->size() : 0); + } + } + --(*performData->requestsLeft); + curl_slist_free_all(*performData->options); + delete requestData; + delete performData->codeDestination; + curl_easy_cleanup(easy_handle); + } + } break; + } + if (!rescheduled) { + // No more transfers will be done for this request, do general cleanup + delete performData; } - delete data; checkHandleQueue(); - // Calling timout starts a new download if a new easy_handle was added. + // Calling timeout starts a new download if a new easy_handle was added. int running_handles; - curl_multi_socket_action(mCurlMultiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles); + curlMultiErrorCheck(curl_multi_socket_action(mCurlMultiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles)); checkMultiInfo(); } @@ -382,44 +612,45 @@ int CCDBDownloader::startTimeout(CURLM* multi, long timeout_ms, void* userp) auto timeout = (uv_timer_t*)userp; if (timeout_ms < 0) { - uv_timer_stop(timeout); + uvErrorCheck(uv_timer_stop(timeout), SEVERE); } else { if (timeout_ms == 0) { timeout_ms = 1; // Calling curlTimeout when timeout = 0 could create an infinite loop } - uv_timer_start(timeout, curlTimeout, timeout_ms, 0); + uvErrorCheck(uv_timer_start(timeout, curlTimeout, timeout_ms, 0), SEVERE); } return 0; } void CCDBDownloader::setHandleOptions(CURL* handle, PerformData* data) { - curl_easy_setopt(handle, CURLOPT_PRIVATE, data); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_PRIVATE, data)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_CLOSESOCKETFUNCTION, closesocketCallback)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_CLOSESOCKETDATA, this)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_OPENSOCKETFUNCTION, opensocketCallback)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_OPENSOCKETDATA, this)); - curl_easy_setopt(handle, CURLOPT_CLOSESOCKETFUNCTION, closesocketCallback); - curl_easy_setopt(handle, CURLOPT_CLOSESOCKETDATA, this); - curl_easy_setopt(handle, CURLOPT_OPENSOCKETFUNCTION, opensocketCallback); - curl_easy_setopt(handle, CURLOPT_OPENSOCKETDATA, this); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, mRequestTimeoutMS)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT_MS, mConnectionTimeoutMS)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, mHappyEyeballsHeadstartMS)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_USERAGENT, mUserAgentId.c_str())); } void CCDBDownloader::checkHandleQueue() { - // Lock access to handle queue - mHandlesQueueLock.lock(); if (mHandlesToBeAdded.size() > 0) { // Add handles without going over the limit while (mHandlesToBeAdded.size() > 0 && mHandlesInUse < mMaxHandlesInUse) { - curl_multi_add_handle(mCurlMultiHandle, mHandlesToBeAdded.front()); + curlMultiErrorCheck(curl_multi_add_handle(mCurlMultiHandle, mHandlesToBeAdded.front())); mHandlesInUse++; mHandlesToBeAdded.erase(mHandlesToBeAdded.begin()); } } - mHandlesQueueLock.unlock(); } -void CCDBDownloader::runLoop() +void CCDBDownloader::runLoop(bool noWait) { - uv_run(mUVLoop, UV_RUN_DEFAULT); + uv_run(mUVLoop, noWait ? UV_RUN_NOWAIT : UV_RUN_ONCE); } CURLcode CCDBDownloader::perform(CURL* handle) @@ -429,92 +660,113 @@ CURLcode CCDBDownloader::perform(CURL* handle) return batchBlockingPerform(handleVector).back(); } -std::vector CCDBDownloader::batchAsynchPerform(std::vector const& handleVector, bool* completionFlag) +void CCDBDownloader::updateLocations(std::multimap* headerMap, std::vector* locations, int* locIndex) const { - std::vector codeVector(handleVector.size()); - size_t* requestsLeft = new size_t(); - *requestsLeft = handleVector.size(); - - mHandlesQueueLock.lock(); - for (int i = 0; i < handleVector.size(); i++) { - auto* data = new CCDBDownloader::PerformData(); - - data->codeDestination = &(codeVector)[i]; - (codeVector)[i] = CURLE_FAILED_INIT; + std::vector newLocations; - data->requestsLeft = requestsLeft; - data->completionFlag = completionFlag; - data->type = ASYNCHRONOUS; + auto iter = headerMap->find("Location"); + if (iter != headerMap->end()) { + auto range = headerMap->equal_range("Location"); + for (auto it = range.first; it != range.second; ++it) { + if (std::find(locations->begin(), locations->end(), it->second) == locations->end()) { + if (std::find(newLocations.begin(), newLocations.end(), it->second) == newLocations.end()) { + newLocations.push_back(it->second); + } + } + } + } - setHandleOptions(handleVector[i], data); - mHandlesToBeAdded.push_back(handleVector[i]); + // add alternative locations (not yet included) + auto iter2 = headerMap->find("Content-Location"); + if (iter2 != headerMap->end()) { + auto range = headerMap->equal_range("Content-Location"); + for (auto it = range.first; it != range.second; ++it) { + if (std::find(locations->begin(), locations->end(), it->second) == locations->end()) { + if (std::find(newLocations.begin(), newLocations.end(), it->second) == newLocations.end()) { + newLocations.push_back(it->second); + } + } + } } - mHandlesQueueLock.unlock(); - makeLoopCheckQueueAsync(); - return codeVector; + + // Insert location list at the current location index. This assures that the provided locations will be tried first. + locations->insert(locations->begin() + (*locIndex), newLocations.begin(), newLocations.end()); } std::vector CCDBDownloader::batchBlockingPerform(std::vector const& handleVector) { - std::condition_variable cv; - std::mutex cv_m; - std::unique_lock lk(cv_m); - std::vector codeVector(handleVector.size()); size_t requestsLeft = handleVector.size(); - mHandlesQueueLock.lock(); for (int i = 0; i < handleVector.size(); i++) { auto* data = new CCDBDownloader::PerformData(); data->codeDestination = &codeVector[i]; codeVector[i] = CURLE_FAILED_INIT; - data->cv = &cv; data->type = BLOCKING; data->requestsLeft = &requestsLeft; - setHandleOptions(handleVector[i], data); mHandlesToBeAdded.push_back(handleVector[i]); } - mHandlesQueueLock.unlock(); - makeLoopCheckQueueAsync(); - cv.wait(lk); + checkHandleQueue(); + while (requestsLeft > 0) { + uv_run(mUVLoop, UV_RUN_ONCE); + } + return codeVector; } -std::vector CCDBDownloader::asynchBatchPerformWithCallback(std::vector const& handleVector, bool* completionFlag, void (*cbFun)(void*), void* cbData) +void CCDBDownloader::asynchSchedule(CURL* handle, size_t* requestCounter) { - std::vector codeVector(handleVector.size()); - size_t* requestsLeft = new size_t(); - *requestsLeft = handleVector.size(); + (*requestCounter)++; - mHandlesQueueLock.lock(); - for (int i = 0; i < handleVector.size(); i++) { - auto* data = new CCDBDownloader::PerformData(); + CURLcode* codeVector = new CURLcode(); - data->codeDestination = &(codeVector)[i]; - (codeVector)[i] = CURLE_FAILED_INIT; + // Get data about request + DownloaderRequestData* requestData; + std::multimap* headerMap; + std::vector* hostsPool; + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &requestData); + headerMap = &(requestData->hoPair.header); + hostsPool = &(requestData->hosts); + auto* options = &(requestData->optionsList); - data->requestsLeft = requestsLeft; - data->completionFlag = completionFlag; - data->type = ASYNCHRONOUS_WITH_CALLBACK; - data->cbFun = cbFun; - data->cbData = cbData; + // Prepare temporary data about transfer + auto* data = new CCDBDownloader::PerformData(); // Freed in transferFinished + data->codeDestination = codeVector; + *codeVector = CURLE_FAILED_INIT; - setHandleOptions(handleVector[i], data); - mHandlesToBeAdded.push_back(handleVector[i]); - } - mHandlesQueueLock.unlock(); - makeLoopCheckQueueAsync(); - return codeVector; + data->type = ASYNCHRONOUS; + data->requestsLeft = requestCounter; + data->hostInd = 0; + data->locInd = 0; + data->requestData = requestData; + data->options = options; + + // Prepare handle and schedule download + setHandleOptions(handle, data); + mHandlesToBeAdded.push_back(handle); + + checkHandleQueue(); + + // return codeVector; } -void CCDBDownloader::makeLoopCheckQueueAsync() +std::string CCDBDownloader::prepareLogMessage(std::string host_url, std::string userAgent, const std::string& path, long ts, const std::map* headers, long httpCode) const { - auto asyncHandle = new uv_async_t(); - asyncHandle->data = this; - uv_async_init(mUVLoop, asyncHandle, asyncUVHandleCheckQueue); - uv_async_send(asyncHandle); + std::string upath{path}; + if (headers) { + auto ent = headers->find("Valid-From"); + if (ent != headers->end()) { + upath += "/" + ent->second; + } + ent = headers->find("ETag"); + if (ent != headers->end()) { + upath += "/" + ent->second; + } + } + upath.erase(remove(upath.begin(), upath.end(), '\"'), upath.end()); + return fmt::format("CcdbDownloader finished transfer {}{}{} for {} (agent_id: {}) with http code: {}", host_url, (host_url.back() == '/') ? "" : "/", upath, (ts < 0) ? getCurrentTimestamp() : ts, userAgent, httpCode); } -} // namespace o2 +} // namespace o2::ccdb diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index e695f2e19fa95..93a79ad56c477 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -20,9 +20,11 @@ #include "CommonUtils/StringUtils.h" #include "CommonUtils/FileSystemUtils.h" #include "CommonUtils/MemFileHelper.h" -#include "MemoryResources/MemoryResources.h" +#include "Framework/DefaultsHelpers.h" +#include "Framework/DataTakingContext.h" #include #include +#include #include #include #include @@ -38,12 +40,17 @@ #include #include #include -#include #include #include #include #include #include +#include +#include +#include +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" namespace o2::ccdb { @@ -53,23 +60,64 @@ using namespace std; std::mutex gIOMutex; // to protect TMemFile IO operations unique_ptr CcdbApi::mJAlienCredentials = nullptr; +/** + * Object, encapsulating a semaphore, regulating + * concurrent (multi-process) access to CCDB snapshot files. + * Intended to be used with smart pointers to achieve automatic resource + * cleanup after the smart pointer goes out of scope. + */ +class CCDBSemaphore +{ + public: + CCDBSemaphore(std::string const& cachepath, std::string const& path); + ~CCDBSemaphore(); + + private: + boost::interprocess::named_semaphore* mSem = nullptr; + std::string mSemName{}; // name under which semaphore is kept by the OS kernel +}; + +// Small registry class with the purpose that a static object +// ensures cleanup of registered semaphores even when programs +// "crash". +class SemaphoreRegistry +{ + public: + SemaphoreRegistry() = default; + ~SemaphoreRegistry(); + void add(CCDBSemaphore const* ptr); + void remove(CCDBSemaphore const* ptr); + + private: + std::unordered_set mStore; +}; +static SemaphoreRegistry gSemaRegistry; + CcdbApi::CcdbApi() { - std::string host = boost::asio::ip::host_name(); - mUniqueAgentID = fmt::format("{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6)); + using namespace o2::framework; + setUniqueAgentID(); - mIsCCDBDownloaderEnabled = getenv("ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI") && atoi(getenv("ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI")); - if (mIsCCDBDownloaderEnabled) { - mDownloader = new CCDBDownloader(); + DeploymentMode deploymentMode = DefaultsHelpers::deploymentMode(); + mIsCCDBDownloaderPreferred = 0; + if (deploymentMode == DeploymentMode::OnlineDDS && deploymentMode == DeploymentMode::OnlineECS && deploymentMode == DeploymentMode::OnlineAUX && deploymentMode == DeploymentMode::FST) { + mIsCCDBDownloaderPreferred = 1; + } + if (getenv("ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI")) { // todo rename ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI to ALICEO2_PREFER_MULTIHANDLE_CCDBAPI + mIsCCDBDownloaderPreferred = atoi(getenv("ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI")); } + mDownloader = new CCDBDownloader(); } CcdbApi::~CcdbApi() { curl_global_cleanup(); - if (mDownloader) { - delete mDownloader; - } + delete mDownloader; +} + +void CcdbApi::setUniqueAgentID() +{ + mUniqueAgentID = TAlienUserAgent::BasedOnEnvironment().ToString(); } bool CcdbApi::checkAlienToken() @@ -99,20 +147,22 @@ void CcdbApi::curlInit() CcdbApi::mJAlienCredentials->loadCredentials(); CcdbApi::mJAlienCredentials->selectPreferedCredentials(); - // allow to configure the socket timeout of CCDBDownloader, if activated (for some tuning studies) - if (mIsCCDBDownloaderEnabled) { - if (getenv("ALICEO2_CCDB_SOCKET_TIMEOUT")) { - auto timeoutMS = atoi(getenv("ALICEO2_CCDB_SOCKET_TIMEOUT")); - if (timeoutMS >= 0) { - LOG(info) << "Setting socket timeout to " << timeoutMS << " milliseconds"; - mDownloader->setSocketTimoutTime(timeoutMS); - } + // allow to configure the socket timeout of CCDBDownloader (for some tuning studies) + if (getenv("ALICEO2_CCDB_SOCKET_TIMEOUT")) { + auto timeoutMS = atoi(getenv("ALICEO2_CCDB_SOCKET_TIMEOUT")); + if (timeoutMS >= 0) { + LOG(info) << "Setting socket timeout to " << timeoutMS << " milliseconds"; + mDownloader->setKeepaliveTimeoutTime(timeoutMS); } } } void CcdbApi::init(std::string const& host) { + if (host.empty()) { + throw std::invalid_argument("Empty url passed CcdbApi, cannot initialize. Aborting."); + } + // if host is prefixed with "file://" this is a local snapshot // in this case we init the API in snapshot (readonly) mode constexpr const char* SNAPSHOTPREFIX = "file://"; @@ -133,21 +183,26 @@ void CcdbApi::init(std::string const& host) // In addition, we can monitor exactly which objects are fetched and what is their content. // One can also distribute so obtained caches to sites without network access. // + // THE INFORMATION BELOW IS TEMPORARILY WRONG: the functionality of checking the validity if IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE + // is NOT set is broken. At the moment the code is modified to behave as if the IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE is always set + // whenever the ALICEO2_CCDB_LOCALCACHE is defined. + // // When used with the DPL CCDB fetcher (i.e. loadFileToMemory is called), in order to prefer the available snapshot w/o its validity // check an extra variable IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE must be defined, otherwhise the object will be fetched from the // server after the validity check and new snapshot will be created if needed std::string snapshotReport{}; const char* cachedir = getenv("ALICEO2_CCDB_LOCALCACHE"); + namespace fs = std::filesystem; if (cachedir) { if (cachedir[0] == 0) { - mSnapshotCachePath = "."; + mSnapshotCachePath = fs::weakly_canonical(fs::absolute(".")); } else { - mSnapshotCachePath = cachedir; + mSnapshotCachePath = fs::weakly_canonical(fs::absolute(cachedir)); } snapshotReport = fmt::format("(cache snapshots to dir={}", mSnapshotCachePath); } - if (getenv("IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE")) { + if (cachedir) { // || getenv("IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE")) { mPreferSnapshotCache = true; if (mSnapshotCachePath.empty()) { LOGP(fatal, "IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE is defined but the ALICEO2_CCDB_LOCALCACHE is not"); @@ -158,10 +213,58 @@ void CcdbApi::init(std::string const& host) snapshotReport += ')'; } - mNeedAlienToken = (host.find("https://") != std::string::npos) || (host.find("alice-ccdb.cern.ch") != std::string::npos); + mNeedAlienToken = (host.find("https://") != std::string::npos) || (host.find("alice-ccdb.cern.ch") != std::string::npos) || (host.find("ccdb-test.cern.ch") != std::string::npos); + // Set the curl timeout. It can be forced with an env var or it has different defaults based on the deployment mode. + if (getenv("ALICEO2_CCDB_CURL_TIMEOUT_DOWNLOAD")) { + auto timeout = atoi(getenv("ALICEO2_CCDB_CURL_TIMEOUT_DOWNLOAD")); + if (timeout >= 0) { // if valid int + mCurlTimeoutDownload = timeout; + } + } else { // set a default depending on the deployment mode + o2::framework::DeploymentMode deploymentMode = o2::framework::DefaultsHelpers::deploymentMode(); + if (deploymentMode == o2::framework::DeploymentMode::OnlineDDS || + deploymentMode == o2::framework::DeploymentMode::OnlineAUX || + deploymentMode == o2::framework::DeploymentMode::OnlineECS) { + mCurlTimeoutDownload = 15; + } else if (deploymentMode == o2::framework::DeploymentMode::Grid || + deploymentMode == o2::framework::DeploymentMode::FST) { + mCurlTimeoutDownload = 15; + } else if (deploymentMode == o2::framework::DeploymentMode::Local) { + mCurlTimeoutDownload = 5; + } + } - LOGP(info, "Init CcdApi with UserAgentID: {}, Host: {}{}", mUniqueAgentID, host, - mInSnapshotMode ? "(snapshot readonly mode)" : snapshotReport.c_str()); + if (getenv("ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD")) { + auto timeout = atoi(getenv("ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD")); + if (timeout >= 0) { // if valid int + mCurlTimeoutUpload = timeout; + } + } else { // set a default depending on the deployment mode + o2::framework::DeploymentMode deploymentMode = o2::framework::DefaultsHelpers::deploymentMode(); + if (deploymentMode == o2::framework::DeploymentMode::OnlineDDS || + deploymentMode == o2::framework::DeploymentMode::OnlineAUX || + deploymentMode == o2::framework::DeploymentMode::OnlineECS) { + mCurlTimeoutUpload = 3; + } else if (deploymentMode == o2::framework::DeploymentMode::Grid || + deploymentMode == o2::framework::DeploymentMode::FST) { + mCurlTimeoutUpload = 20; + } else if (deploymentMode == o2::framework::DeploymentMode::Local) { + mCurlTimeoutUpload = 20; + } + } + if (mDownloader) { + mDownloader->setRequestTimeoutTime(mCurlTimeoutDownload * 1000L); + } + + LOGP(debug, "Curl timeouts are set to: download={:2}, upload={:2} seconds", mCurlTimeoutDownload, mCurlTimeoutUpload); + + LOGP(info, "Init CcdApi with UserAgentID: {}, Host: {}{}, Curl timeouts: upload:{} download:{}", mUniqueAgentID, host, + mInSnapshotMode ? "(snapshot readonly mode)" : snapshotReport.c_str(), mCurlTimeoutUpload, mCurlTimeoutDownload); +} + +void CcdbApi::runDownloaderLoop(bool noWait) +{ + mDownloader->runLoop(noWait); } // A helper function used in a few places. Updates a ROOT file with meta/header information. @@ -192,7 +295,7 @@ void CcdbApi::updateMetaInformationInLocalFile(std::string const& filename, std: */ std::string sanitizeObjectName(const std::string& objectName) { - string tmpObjectName = objectName; + std::string tmpObjectName = objectName; tmpObjectName.erase(std::remove_if(tmpObjectName.begin(), tmpObjectName.end(), [](auto const& c) -> bool { return (!std::isalnum(c) && c != '_' && c != '/' && c != '.'); }), tmpObjectName.end()); @@ -233,6 +336,10 @@ int CcdbApi::storeAsTFile_impl(const void* obj, std::type_info const& tinfo, std std::vector::size_type maxSize) const { // We need the TClass for this type; will verify if dictionary exists + if (!obj) { + LOGP(error, "nullptr is provided for object {}/{}/{}", path, startValidityTimestamp, endValidityTimestamp); + return -1; + } CcdbObjectInfo info; auto img = createObjectImage(obj, tinfo, &info); return storeAsBinaryFile(img->data(), img->size(), info.getFileName(), info.getObjectType(), @@ -261,6 +368,10 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin sanitizedEndValidityTimestamp = getFutureTimestamp(60 * 60 * 24 * 1); } if (mInSnapshotMode) { // write local file + if (filename.empty() || buffer == nullptr || size == 0) { + LOGP(alarm, "Snapshot mode does not support headers-only upload"); + return -3; + } auto pthLoc = getSnapshotDir(mSnapshotTopPath, path); o2::utils::createDirectoriesIfAbsent(pthLoc); auto flLoc = getSnapshotFile(mSnapshotTopPath, path, filename); @@ -301,15 +412,17 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin checkMetadataKeys(metadata); if (curl != nullptr) { - struct curl_httppost* formpost = nullptr; - struct curl_httppost* lastptr = nullptr; - curl_formadd(&formpost, - &lastptr, - CURLFORM_COPYNAME, "send", - CURLFORM_BUFFER, filename.c_str(), - CURLFORM_BUFFERPTR, buffer, //.Buffer(), - CURLFORM_BUFFERLENGTH, size, - CURLFORM_END); + auto mime = curl_mime_init(curl); + auto field = curl_mime_addpart(mime); + curl_mime_name(field, "send"); + if (!filename.empty()) { + curl_mime_filedata(field, filename.c_str()); + } + if (buffer != nullptr && size > 0) { + curl_mime_data(field, buffer, size); + } else { + curl_mime_data(field, "", 0); + } struct curl_slist* headerlist = nullptr; static const char buf[] = "Expect:"; @@ -317,15 +430,16 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin curlSetSSLOptions(curl); + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); - curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, mCurlTimeoutUpload); CURLcode res = CURL_LAST; for (size_t hostIndex = 0; hostIndex < hostsPool.size() && res > 0; hostIndex++) { - string fullUrl = getFullUrlForStorage(curl, path, objectType, metadata, sanitizedStartValidityTimestamp, sanitizedEndValidityTimestamp, hostIndex); + std::string fullUrl = getFullUrlForStorage(curl, path, objectType, metadata, sanitizedStartValidityTimestamp, sanitizedEndValidityTimestamp, hostIndex); LOG(debug3) << "Full URL Encoded: " << fullUrl; /* what URL that receives this POST */ curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str()); @@ -334,7 +448,11 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin res = CURL_perform(curl); /* Check for errors */ if (res != CURLE_OK) { - LOGP(alarm, "curl_easy_perform() failed: {}", curl_easy_strerror(res)); + if (res == CURLE_OPERATION_TIMEDOUT) { + LOGP(alarm, "curl_easy_perform() timed out. Consider increasing the timeout using the env var `ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD` (seconds), current one is {}", mCurlTimeoutUpload); + } else { // generic message + LOGP(alarm, "curl_easy_perform() failed: {}", curl_easy_strerror(res)); + } returnValue = res; } } @@ -342,10 +460,10 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin /* always cleanup */ curl_easy_cleanup(curl); - /* then cleanup the formpost chain */ - curl_formfree(formpost); /* free slist */ curl_slist_free_all(headerlist); + /* free mime */ + curl_mime_free(mime); } else { LOGP(alarm, "curl initialization failure"); returnValue = -2; @@ -357,35 +475,39 @@ int CcdbApi::storeAsTFile(const TObject* rootObject, std::string const& path, st long startValidityTimestamp, long endValidityTimestamp, std::vector::size_type maxSize) const { // Prepare file + if (!rootObject) { + LOGP(error, "nullptr is provided for object {}/{}/{}", path, startValidityTimestamp, endValidityTimestamp); + return -1; + } CcdbObjectInfo info; auto img = createObjectImage(rootObject, &info); return storeAsBinaryFile(img->data(), img->size(), info.getFileName(), info.getObjectType(), path, metadata, startValidityTimestamp, endValidityTimestamp, maxSize); } -string CcdbApi::getFullUrlForStorage(CURL* curl, const string& path, const string& objtype, - const map& metadata, - long startValidityTimestamp, long endValidityTimestamp, int hostIndex) const +std::string CcdbApi::getFullUrlForStorage(CURL* curl, const std::string& path, const std::string& objtype, + const std::map& metadata, + long startValidityTimestamp, long endValidityTimestamp, int hostIndex) const { // Prepare timestamps - string startValidityString = getTimestampString(startValidityTimestamp < 0 ? getCurrentTimestamp() : startValidityTimestamp); - string endValidityString = getTimestampString(endValidityTimestamp < 0 ? getFutureTimestamp(60 * 60 * 24 * 1) : endValidityTimestamp); + std::string startValidityString = getTimestampString(startValidityTimestamp < 0 ? getCurrentTimestamp() : startValidityTimestamp); + std::string endValidityString = getTimestampString(endValidityTimestamp < 0 ? getFutureTimestamp(60 * 60 * 24 * 1) : endValidityTimestamp); // Get url - string url = getHostUrl(hostIndex); + std::string url = getHostUrl(hostIndex); // Build URL - string fullUrl = url + "/" + path + "/" + startValidityString + "/" + endValidityString + "/"; + std::string fullUrl = url + "/" + path + "/" + startValidityString + "/" + endValidityString + "/"; // Add type as part of metadata // we need to URL encode the object type, since in case it has special characters (like the "<", ">" for templated classes) it won't work otherwise char* objtypeEncoded = curl_easy_escape(curl, objtype.c_str(), objtype.size()); - fullUrl += "ObjectType=" + string(objtypeEncoded) + "/"; + fullUrl += "ObjectType=" + std::string(objtypeEncoded) + "/"; curl_free(objtypeEncoded); // Add general metadata for (auto& kv : metadata) { - string mfirst = kv.first; - string msecond = kv.second; + std::string mfirst = kv.first; + std::string msecond = kv.second; // same trick for the metadata as for the object type char* mfirstEncoded = curl_easy_escape(curl, mfirst.c_str(), mfirst.size()); char* msecondEncoded = curl_easy_escape(curl, msecond.c_str(), msecond.size()); - fullUrl += string(mfirstEncoded) + "=" + string(msecondEncoded) + "/"; + fullUrl += std::string(mfirstEncoded) + "=" + std::string(msecondEncoded) + "/"; curl_free(mfirstEncoded); curl_free(msecondEncoded); } @@ -393,26 +515,26 @@ string CcdbApi::getFullUrlForStorage(CURL* curl, const string& path, const strin } // todo make a single method of the one above and below -string CcdbApi::getFullUrlForRetrieval(CURL* curl, const string& path, const map& metadata, long timestamp, int hostIndex) const +std::string CcdbApi::getFullUrlForRetrieval(CURL* curl, const std::string& path, const std::map& metadata, long timestamp, int hostIndex) const { if (mInSnapshotMode) { return getSnapshotFile(mSnapshotTopPath, path); } // Prepare timestamps - string validityString = getTimestampString(timestamp < 0 ? getCurrentTimestamp() : timestamp); + std::string validityString = getTimestampString(timestamp < 0 ? getCurrentTimestamp() : timestamp); // Get host url - string hostUrl = getHostUrl(hostIndex); + std::string hostUrl = getHostUrl(hostIndex); // Build URL - string fullUrl = hostUrl + "/" + path + "/" + validityString + "/"; + std::string fullUrl = hostUrl + "/" + path + "/" + validityString + "/"; // Add metadata for (auto& kv : metadata) { - string mfirst = kv.first; - string msecond = kv.second; + std::string mfirst = kv.first; + std::string msecond = kv.second; // trick for the metadata in case it contains special characters char* mfirstEncoded = curl_easy_escape(curl, mfirst.c_str(), mfirst.size()); char* msecondEncoded = curl_easy_escape(curl, msecond.c_str(), msecond.size()); - fullUrl += string(mfirstEncoded) + "=" + string(msecondEncoded) + "/"; + fullUrl += std::string(mfirstEncoded) + "=" + std::string(msecondEncoded) + "/"; curl_free(mfirstEncoded); curl_free(msecondEncoded); } @@ -420,8 +542,8 @@ string CcdbApi::getFullUrlForRetrieval(CURL* curl, const string& path, const map } /** - * Struct to store the data we will receive from the CCDB with CURL. - */ + * Struct to store the data we will receive from the CCDB with CURL. + */ struct MemoryStruct { char* memory; unsigned int size; @@ -541,7 +663,38 @@ size_t header_map_callback(char* buffer, size_t size, size_t nitems, void* userd if (index != std::string::npos) { const auto key = boost::algorithm::trim_copy(header.substr(0, index)); const auto value = boost::algorithm::trim_copy(header.substr(index + 1)); - headers->insert(std::make_pair(key, value)); + LOGP(debug, "Adding #{} {} -> {}", headers->size(), key, value); + bool insert = true; + if (key == "Content-Length") { + auto cl = headers->find("Content-Length"); + if (cl != headers->end()) { + if (std::stol(cl->second) < stol(value)) { + headers->erase(key); + } else { + insert = false; + } + } + } + + // Keep only the first ETag encountered + if (key == "ETag") { + auto cl = headers->find("ETag"); + if (cl != headers->end()) { + insert = false; + } + } + + // Keep only the first Content-Type encountered + if (key == "Content-Type") { + auto cl = headers->find("Content-Type"); + if (cl != headers->end()) { + insert = false; + } + } + + if (insert) { + headers->insert(std::make_pair(key, value)); + } } return size * nitems; } @@ -597,6 +750,7 @@ bool CcdbApi::receiveObject(void* dataHolder, std::string const& path, std::map< CURL* curlHandle; curlHandle = curl_easy_init(); + curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); if (curlHandle != nullptr) { @@ -609,7 +763,7 @@ bool CcdbApi::receiveObject(void* dataHolder, std::string const& path, std::map< CURLcode curlResultCode = CURL_LAST; for (size_t hostIndex = 0; hostIndex < hostsPool.size() && (responseCode >= 400 || curlResultCode > 0); hostIndex++) { - string fullUrl = getFullUrlForRetrieval(curlHandle, path, metadata, timestamp, hostIndex); + std::string fullUrl = getFullUrlForRetrieval(curlHandle, path, metadata, timestamp, hostIndex); curl_easy_setopt(curlHandle, CURLOPT_URL, fullUrl.c_str()); curlResultCode = CURL_perform(curlHandle); @@ -684,7 +838,7 @@ TObject* CcdbApi::retrieveFromTFile(std::string const& path, std::map const& metadata, - long timestamp, bool preservePath, std::string const& localFileName, std::string const& createdNotAfter, std::string const& createdNotBefore) const + long timestamp, bool preservePath, std::string const& localFileName, std::string const& createdNotAfter, std::string const& createdNotBefore, std::map* outHeaders) const { // we setup the target path for this blob @@ -693,7 +847,7 @@ bool CcdbApi::retrieveBlob(std::string const& path, std::string const& targetdir try { o2::utils::createDirectoriesIfAbsent(fulltargetdir); } catch (std::exception e) { - LOGP(error, fmt::format("Could not create local snapshot cache directory {}, reason: {}", fulltargetdir, e.what())); + LOGP(error, "Could not create local snapshot cache directory {}, reason: {}", fulltargetdir, e.what()); return false; } @@ -732,6 +886,9 @@ bool CcdbApi::retrieveBlob(std::string const& path, std::string const& targetdir CCDBQuery querysummary(path, metadata, timestamp); updateMetaInformationInLocalFile(targetpath.c_str(), &headers, &querysummary); + if (outHeaders) { + *outHeaders = std::move(headers); + } return true; } @@ -739,7 +896,7 @@ void CcdbApi::snapshot(std::string const& ccdbrootpath, std::string const& local { // query all subpaths to ccdbrootpath const auto allfolders = getAllFolders(ccdbrootpath); - std::map metadata; + std::map metadata; for (auto& folder : allfolders) { retrieveBlob(folder, localDir, metadata, timestamp); } @@ -803,20 +960,23 @@ void* CcdbApi::extractFromLocalFile(std::string const& filename, std::type_info if ((isSnapshotMode() || mPreferSnapshotCache) && headers->find("ETag") == headers->end()) { // generate dummy ETag to profit from the caching (*headers)["ETag"] = filename; } + if (headers->find("fileSize") == headers->end()) { + (*headers)["fileSize"] = fmt::format("{}", f.GetEND()); + } } return extractFromTFile(f, tcl); } bool CcdbApi::initTGrid() const { - if (mNeedAlienToken && !mAlienInstance) { + if (mNeedAlienToken && !gGrid) { static bool allowNoToken = getenv("ALICEO2_CCDB_NOTOKENCHECK") && atoi(getenv("ALICEO2_CCDB_NOTOKENCHECK")); if (!allowNoToken && !checkAlienToken()) { LOG(fatal) << "Alien Token Check failed - Please get an alien token before running with https CCDB endpoint, or alice-ccdb.cern.ch!"; } - mAlienInstance = TGrid::Connect("alien"); + TGrid::Connect("alien"); static bool errorShown = false; - if (!mAlienInstance && errorShown == false) { + if (!gGrid && errorShown == false) { if (allowNoToken) { LOG(error) << "TGrid::Connect returned nullptr. May be due to missing alien token"; } else { @@ -825,12 +985,12 @@ bool CcdbApi::initTGrid() const errorShown = true; } } - return mAlienInstance != nullptr; + return gGrid != nullptr; } -void* CcdbApi::downloadAlienContent(std::string const& url, std::type_info const& tinfo) const +void* CcdbApi::downloadFilesystemContent(std::string const& url, std::type_info const& tinfo, std::map* headers) const { - if (!initTGrid()) { + if ((url.find("alien:/", 0) != std::string::npos) && !initTGrid()) { return nullptr; } std::lock_guard guard(gIOMutex); @@ -838,6 +998,9 @@ void* CcdbApi::downloadAlienContent(std::string const& url, std::type_info const if (memfile) { auto cl = tinfo2TClass(tinfo); auto content = extractFromTFile(*memfile, cl); + if (headers && headers->find("fileSize") == headers->end()) { + (*headers)["fileSize"] = fmt::format("{}", memfile->GetEND()); + } delete memfile; return content; } @@ -864,7 +1027,7 @@ void* CcdbApi::interpretAsTMemFileAndExtract(char* contentptr, size_t contentsiz } // navigate sequence of URLs until TFile content is found; object is extracted and returned -void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string const& url, std::type_info const& tinfo, std::map* headers) const +void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string const& url, std::type_info const& tinfo, std::map* headers) const { // a global internal data structure that can be filled with HTTP header information // static --> to avoid frequent alloc/dealloc as optimization @@ -872,8 +1035,8 @@ void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string con static thread_local std::multimap headerData; // let's see first of all if the url is something specific that curl cannot handle - if (url.find("alien:/", 0) != std::string::npos) { - return downloadAlienContent(url, tinfo); + if ((url.find("alien:/", 0) != std::string::npos) || (url.find("file:/", 0) != std::string::npos)) { + return downloadFilesystemContent(url, tinfo, headers); } // add other final cases here // example root:// @@ -904,6 +1067,9 @@ void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string con if (200 <= response_code && response_code < 300) { // good response and the content is directly provided and should have been dumped into "chunk" content = interpretAsTMemFileAndExtract(chunk.memory, chunk.size, tinfo); + if (headers && headers->find("fileSize") == headers->end()) { + (*headers)["fileSize"] = fmt::format("{}", chunk.size); + } } else if (response_code == 304) { // this means the object exist but I am not serving // it since it's already in your possession @@ -979,18 +1145,7 @@ void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const& { if (!mSnapshotCachePath.empty()) { // protect this sensitive section by a multi-process named semaphore - boost::interprocess::named_semaphore* sem = nullptr; - std::hash hasher; - const auto semhashedstring = "aliceccdb" + std::to_string(hasher(mSnapshotCachePath + path)).substr(0, 16); - try { - sem = new boost::interprocess::named_semaphore(boost::interprocess::open_or_create_t{}, semhashedstring.c_str(), 1); - } catch (std::exception e) { - LOG(warn) << "Exception occurred during CCDB (cache) semaphore setup; Continuing without"; - sem = nullptr; - } - if (sem) { - sem->wait(); // wait until we can enter (no one else there) - } + auto semaphore_barrier = std::make_unique(mSnapshotCachePath, path); std::string logfile = mSnapshotCachePath + "/log"; std::fstream out(logfile, ios_base::out | ios_base::app); if (out.is_open()) { @@ -1008,14 +1163,7 @@ void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const& } else { out << "CCDB-access[" << getpid() << "] ... " << mUniqueAgentID << "serving from local snapshot " << snapshotfile << "\n"; } - if (sem) { - sem->post(); - if (sem->try_wait()) { - // if nobody else is waiting remove the semaphore resource - sem->post(); - boost::interprocess::named_semaphore::remove(semhashedstring.c_str()); - } - } + auto res = extractFromLocalFile(snapshotfile, tinfo, headers); if (!snapshoting) { // if snapshot was created at this call, the log was already done logReading(path, timestamp, headers, "retrieve from snapshot"); @@ -1026,11 +1174,15 @@ void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const& // normal mode follows CURL* curl_handle = curl_easy_init(); - string fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp); // todo check if function still works correctly in case mInSnapshotMode + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); + std::string fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp); // todo check if function still works correctly in case mInSnapshotMode // if we are in snapshot mode we can simply open the file; extract the object and return if (mInSnapshotMode) { - return extractFromLocalFile(fullUrl, tinfo, headers); - logReading(path, timestamp, headers, "retrieve from snapshot"); + auto res = extractFromLocalFile(fullUrl, tinfo, headers); + if (res) { + logReading(path, timestamp, headers, "retrieve from snapshot"); + } + return res; } curl_slist* option_list = nullptr; @@ -1064,7 +1216,7 @@ size_t CurlWrite_CallbackFunc_StdString2(void* contents, size_t size, size_t nme return size * nmemb; } -std::string CcdbApi::list(std::string const& path, bool latestOnly, std::string const& returnFormat) const +std::string CcdbApi::list(std::string const& path, bool latestOnly, std::string const& returnFormat, long createdNotAfter, long createdNotBefore) const { CURL* curl; CURLcode res = CURL_LAST; @@ -1074,15 +1226,22 @@ std::string CcdbApi::list(std::string const& path, bool latestOnly, std::string if (curl != nullptr) { curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString2); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); struct curl_slist* headers = nullptr; - headers = curl_slist_append(headers, (string("Accept: ") + returnFormat).c_str()); - headers = curl_slist_append(headers, (string("Content-Type: ") + returnFormat).c_str()); + headers = curl_slist_append(headers, (std::string("Accept: ") + returnFormat).c_str()); + headers = curl_slist_append(headers, (std::string("Content-Type: ") + returnFormat).c_str()); + if (createdNotAfter >= 0) { + headers = curl_slist_append(headers, ("If-Not-After: " + std::to_string(createdNotAfter)).c_str()); + } + if (createdNotBefore >= 0) { + headers = curl_slist_append(headers, ("If-Not-Before: " + std::to_string(createdNotBefore)).c_str()); + } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curlSetSSLOptions(curl); - string fullUrl; + std::string fullUrl; // Perform the request, res will get the return code for (size_t hostIndex = 0; hostIndex < hostsPool.size() && res != CURLE_OK; hostIndex++) { fullUrl = getHostUrl(hostIndex); @@ -1119,6 +1278,7 @@ void CcdbApi::deleteObject(std::string const& path, long timestamp) const curl = curl_easy_init(); if (curl != nullptr) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); curlSetSSLOptions(curl); for (size_t hostIndex = 0; hostIndex < hostsPool.size(); hostIndex++) { @@ -1141,10 +1301,11 @@ void CcdbApi::truncate(std::string const& path) const CURLcode res; stringstream fullUrl; for (size_t i = 0; i < hostsPool.size(); i++) { - string url = getHostUrl(i); + std::string url = getHostUrl(i); fullUrl << url << "/truncate/" << path; curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); if (curl != nullptr) { curl_easy_setopt(curl, CURLOPT_URL, fullUrl.str().c_str()); @@ -1172,6 +1333,7 @@ bool CcdbApi::isHostReachable() const bool result = false; curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); if (curl) { for (size_t hostIndex = 0; hostIndex < hostsPool.size() && res != CURLE_OK; hostIndex++) { curl_easy_setopt(curl, CURLOPT_URL, mUrl.data()); @@ -1223,53 +1385,140 @@ size_t header_callback(char* buffer, size_t size, size_t nitems, void* userdata) } } // namespace -std::map CcdbApi::retrieveHeaders(std::string const& path, std::map const& metadata, long timestamp) const +bool stdmap_to_jsonfile(std::map const& meta, std::string const& filename) { - CURL* curl = curl_easy_init(); - CURLcode res = CURL_LAST; - string fullUrl = getFullUrlForRetrieval(curl, path, metadata, timestamp); - std::map headers; - if (curl != nullptr) { - struct curl_slist* list = nullptr; - list = curl_slist_append(list, ("If-None-Match: " + std::to_string(timestamp)).c_str()); + // create directory structure if necessary + auto p = std::filesystem::path(filename).parent_path(); + if (!std::filesystem::exists(p)) { + std::filesystem::create_directories(p); + } - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + writer.StartObject(); + for (const auto& pair : meta) { + writer.Key(pair.first.c_str()); + writer.String(pair.second.c_str()); + } + writer.EndObject(); - /* get us the resource without a body! */ - curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_map_callback<>); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers); - curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); + // Write JSON to file + std::ofstream file(filename); + if (file.is_open()) { + file << buffer.GetString(); + file.close(); + } else { + return false; + } + return true; +} - curlSetSSLOptions(curl); +bool jsonfile_to_stdmap(std::map& meta, std::string const& filename) +{ + // Read JSON from file + std::ifstream file(filename); + if (!file.is_open()) { + std::cerr << "Failed to open file for reading." << std::endl; + return false; + } - // Perform the request, res will get the return code + std::string jsonStr((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - long httpCode = 404; - CURLcode getCodeRes = CURL_LAST; - for (size_t hostIndex = 0; hostIndex < hostsPool.size() && (httpCode >= 400 || res > 0 || getCodeRes > 0); hostIndex++) { - curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str()); - res = CURL_perform(curl); - if (res != CURLE_OK && res != CURLE_UNSUPPORTED_PROTOCOL) { - // We take out the unsupported protocol error because we are only querying - // header info which is returned in any case. Unsupported protocol error - // occurs sometimes because of redirection to alien for blobs. - LOG(error) << "CURL_perform() failed: " << curl_easy_strerror(res); - } + // Parse JSON + rapidjson::Document document; + document.Parse(jsonStr.c_str()); + + if (document.HasParseError()) { + std::cerr << "Error parsing JSON" << std::endl; + return false; + } + + // Convert JSON to std::map + for (auto itr = document.MemberBegin(); itr != document.MemberEnd(); ++itr) { + meta[itr->name.GetString()] = itr->value.GetString(); + } + return true; +} + +std::map CcdbApi::retrieveHeaders(std::string const& path, std::map const& metadata, long timestamp) const +{ + // lambda that actually does the call to the CCDB server + auto do_remote_header_call = [this, &path, &metadata, timestamp]() -> std::map { + CURL* curl = curl_easy_init(); + CURLcode res = CURL_LAST; + std::string fullUrl = getFullUrlForRetrieval(curl, path, metadata, timestamp); + std::map headers; + + if (curl != nullptr) { + struct curl_slist* list = nullptr; + list = curl_slist_append(list, ("If-None-Match: " + std::to_string(timestamp)).c_str()); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); - getCodeRes = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); + /* get us the resource without a body! */ + curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_map_callback<>); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); + + curlSetSSLOptions(curl); + + // Perform the request, res will get the return code + long httpCode = 404; + CURLcode getCodeRes = CURL_LAST; + for (size_t hostIndex = 0; hostIndex < hostsPool.size() && (httpCode >= 400 || res > 0 || getCodeRes > 0); hostIndex++) { + curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str()); + res = CURL_perform(curl); + if (res != CURLE_OK && res != CURLE_UNSUPPORTED_PROTOCOL) { + // We take out the unsupported protocol error because we are only querying + // header info which is returned in any case. Unsupported protocol error + // occurs sometimes because of redirection to alien for blobs. + LOG(error) << "CURL_perform() failed: " << curl_easy_strerror(res); + } + getCodeRes = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); + } + if (httpCode == 404) { + headers.clear(); + } + curl_easy_cleanup(curl); } + return headers; + }; + + if (!mSnapshotCachePath.empty()) { + // protect this sensitive section by a multi-process named semaphore + auto semaphore_barrier = std::make_unique(mSnapshotCachePath + std::string("_headers"), path); - if (httpCode == 404) { - headers.clear(); + std::string logfile = mSnapshotCachePath + "/log"; + std::fstream out(logfile, ios_base::out | ios_base::app); + if (out.is_open()) { + out << "CCDB-header-access[" << getpid() << "] of " << mUniqueAgentID << " to " << path << " timestamp " << timestamp << "\n"; } + auto snapshotfile = getSnapshotFile(mSnapshotCachePath, path + "/" + std::to_string(timestamp), "header.json"); + if (!std::filesystem::exists(snapshotfile)) { + out << "CCDB-header-access[" << getpid() << "] ... " << mUniqueAgentID << " storing to snapshot " << snapshotfile << "\n"; - curl_easy_cleanup(curl); - } + // if file not already here and valid --> snapshot it + auto meta = do_remote_header_call(); - return headers; + // cache the result + if (!stdmap_to_jsonfile(meta, snapshotfile)) { + LOG(warn) << "Failed to cache the header information to disc"; + } + return meta; + } else { + out << "CCDB-header-access[" << getpid() << "] ... " << mUniqueAgentID << "serving from local snapshot " << snapshotfile << "\n"; + std::map meta; + if (!jsonfile_to_stdmap(meta, snapshotfile)) { + LOG(warn) << "Failed to read cached information from disc"; + return do_remote_header_call(); + } + return meta; + } + } + return do_remote_header_call(); } bool CcdbApi::getCCDBEntryHeaders(std::string const& url, std::string const& etag, std::vector& headers, const std::string& agentID) @@ -1375,9 +1624,11 @@ TClass* CcdbApi::tinfo2TClass(std::type_info const& tinfo) return cl; } -void CcdbApi::updateMetadata(std::string const& path, std::map const& metadata, long timestamp, std::string const& id, long newEOV) +int CcdbApi::updateMetadata(std::string const& path, std::map const& metadata, long timestamp, std::string const& id, long newEOV) { + int ret = -1; CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); if (curl != nullptr) { CURLcode res; stringstream fullUrl; @@ -1392,12 +1643,12 @@ void CcdbApi::updateMetadata(std::string const& path, std::map CcdbApi::splitString(const std::string& str, const char* delimiters) -{ - std::vector tokens; - char stringForStrTok[str.length() + 1]; - strcpy(stringForStrTok, str.c_str()); - char* token = strtok(stringForStrTok, delimiters); - while (token != nullptr) { - tokens.emplace_back(token); - token = strtok(nullptr, delimiters); - } - return tokens; + return ret; } void CcdbApi::initHostsPool(std::string hosts) { - hostsPool = splitString(hosts, ",;"); + hostsPool.clear(); + auto splitted = hosts | std::views::transform([](char c) { return (c == ';') ? ',' : c; }) | std::views::split(','); + for (auto&& part : splitted) { + hostsPool.emplace_back(part.begin(), part.end()); + } } std::string CcdbApi::getHostUrl(int hostIndex) const @@ -1444,215 +1690,332 @@ std::string CcdbApi::getHostUrl(int hostIndex) const return hostsPool.at(hostIndex); } -void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, std::string const& path, - std::map const& metadata, long timestamp, - std::map* headers, std::string const& etag, - const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot) const +void CcdbApi::scheduleDownload(RequestContext& requestContext, size_t* requestCounter) const { - LOGP(debug, "loadFileToMemory {} ETag=[{}]", path, etag); - - // if we are in snapshot mode we can simply open the file, unless the etag is non-empty: - // this would mean that the object was is already fetched and in this mode we don't to validity checks! - bool createSnapshot = considerSnapshot && !mSnapshotCachePath.empty(); // create snaphot if absent - int fromSnapshot = 0; - boost::interprocess::named_semaphore* sem = nullptr; - std::string semhashedstring{}, snapshotpath{}, logfile{}; - std::unique_ptr logStream; - auto sem_release = [&sem, &semhashedstring, path, this]() { - if (sem) { - sem->post(); - if (sem->try_wait()) { // if nobody else is waiting remove the semaphore resource - sem->post(); - boost::interprocess::named_semaphore::remove(semhashedstring.c_str()); - } - } + auto data = new DownloaderRequestData(); // Deleted in transferFinished of CCDBDownloader.cxx + data->hoPair.object = &requestContext.dest; + + std::function localContentCallback = [this, &requestContext](std::string url) { + return this->loadLocalContentToMemory(requestContext.dest, url); }; - if (createSnapshot) { // create named semaphore - std::hash hasher; - semhashedstring = "aliceccdb" + std::to_string(hasher(mSnapshotCachePath + path)).substr(0, 16); + auto writeCallback = [](void* contents, size_t size, size_t nmemb, void* chunkptr) { + auto& ho = *static_cast(chunkptr); + auto& chunk = *ho.object; + size_t realsize = size * nmemb, sz = 0; + ho.counter++; try { - sem = new boost::interprocess::named_semaphore(boost::interprocess::open_or_create_t{}, semhashedstring.c_str(), 1); + if (chunk.capacity() < chunk.size() + realsize) { + // estimate headers size when converted to annotated text string + const char hannot[] = "header"; + size_t hsize = getFlatHeaderSize(ho.header); + auto cl = ho.header.find("Content-Length"); + if (cl != ho.header.end()) { + size_t sizeFromHeader = std::stol(cl->second); + sz = hsize + std::max(chunk.size() * (sizeFromHeader ? 1 : 2) + realsize, sizeFromHeader); + } else { + sz = hsize + std::max(chunk.size() * 2, chunk.size() + realsize); + // LOGP(debug, "SIZE IS NOT IN HEADER, allocate {}", sz); + } + chunk.reserve(sz); + } + char* contC = (char*)contents; + chunk.insert(chunk.end(), contC, contC + realsize); } catch (std::exception e) { - LOG(warn) << "Exception occurred during CCDB (cache) semaphore setup; Continuing without"; - sem = nullptr; + // LOGP(alarm, "failed to reserve {} bytes in CURL write callback (realsize = {}): {}", sz, realsize, e.what()); + realsize = 0; + } + return realsize; + }; + + CURL* curl_handle = curl_easy_init(); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); + std::string fullUrl = getFullUrlForRetrieval(curl_handle, requestContext.path, requestContext.metadata, requestContext.timestamp); + curl_slist* options_list = nullptr; + initCurlHTTPHeaderOptionsForRetrieve(curl_handle, options_list, requestContext.timestamp, &requestContext.headers, + requestContext.etag, requestContext.createdNotAfter, requestContext.createdNotBefore); + + data->headers = &requestContext.headers; + data->hosts = hostsPool; + data->path = requestContext.path; + data->timestamp = requestContext.timestamp; + data->localContentCallback = localContentCallback; + data->userAgent = mUniqueAgentID; + data->optionsList = options_list; + + curl_easy_setopt(curl_handle, CURLOPT_URL, fullUrl.c_str()); + initCurlOptionsForRetrieve(curl_handle, (void*)(&data->hoPair), writeCallback, false); + curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callbackhoPair.header)>); + curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void*)&(data->hoPair.header)); + curl_easy_setopt(curl_handle, CURLOPT_PRIVATE, (void*)data); + curlSetSSLOptions(curl_handle); + + asynchPerform(curl_handle, requestCounter); +} + +std::string CcdbApi::determineSemaphoreName(std::string const& basedir, std::string const& ccdbpath) +{ + std::hash hasher; + std::string semhashedstring = "aliceccdb" + std::to_string(hasher(basedir + ccdbpath)).substr(0, 16); + return semhashedstring; +} + +boost::interprocess::named_semaphore* CcdbApi::createNamedSemaphore(std::string const& path) const +{ + std::string semhashedstring = determineSemaphoreName(mSnapshotCachePath, path); + // LOG(info) << "Creating named semaphore with name " << semhashedstring.c_str(); + try { + return new boost::interprocess::named_semaphore(boost::interprocess::open_or_create_t{}, semhashedstring.c_str(), 1); + } catch (std::exception e) { + LOG(warn) << "Exception occurred during CCDB (cache) semaphore setup; Continuing without"; + return nullptr; + } +} + +void CcdbApi::releaseNamedSemaphore(boost::interprocess::named_semaphore* sem, std::string const& path) const +{ + if (sem) { + sem->post(); + if (sem->try_wait()) { // if nobody else is waiting remove the semaphore resource + sem->post(); + boost::interprocess::named_semaphore::remove(determineSemaphoreName(mSnapshotCachePath, path).c_str()); } - if (sem) { - sem->wait(); // wait until we can enter (no one else there) + } +} + +bool CcdbApi::removeSemaphore(std::string const& semaname, bool remove) +{ + // removes a given named semaphore from the system + try { + boost::interprocess::named_semaphore semaphore(boost::interprocess::open_only, semaname.c_str()); + std::cout << "Found CCDB semaphore: " << semaname << "\n"; + if (remove) { + auto success = boost::interprocess::named_semaphore::remove(semaname.c_str()); + if (success) { + std::cout << "Removed CCDB semaphore: " << semaname << "\n"; + } + return success; } - logfile = mSnapshotCachePath + "/log"; - logStream = std::make_unique(logfile, ios_base::out | ios_base::app); - if (logStream->is_open()) { - *logStream.get() << "CCDB-access[" << getpid() << "] of " << mUniqueAgentID << " to " << path << " timestamp " << timestamp << " for load to memory\n"; + return true; + } catch (std::exception const& e) { + // no EXISTING under this name semaphore found + // nothing to be done + } + return false; +} + +// helper function checking for leaking semaphores associated to CCDB cache files and removing them +// walks a local CCDB snapshot tree and checks +void CcdbApi::removeLeakingSemaphores(std::string const& snapshotdir, bool remove) +{ + namespace fs = std::filesystem; + std::string fileName{"snapshot.root"}; + try { + auto absolutesnapshotdir = fs::weakly_canonical(fs::absolute(snapshotdir)); + for (const auto& entry : fs::recursive_directory_iterator(absolutesnapshotdir)) { + if (entry.is_directory()) { + const fs::path& currentDir = fs::canonical(fs::absolute(entry.path())); + fs::path filePath = currentDir / fileName; + if (fs::exists(filePath) && fs::is_regular_file(filePath)) { + std::cout << "Directory with file '" << fileName << "': " << currentDir << std::endl; + + // we need to obtain the path relative to snapshotdir + auto pathtokens = o2::utils::Str::tokenize(currentDir, '/', true); + auto numtokens = pathtokens.size(); + if (numtokens < 3) { + // cannot be a CCDB path + continue; + } + // path are last 3 entries + std::string path = pathtokens[numtokens - 3] + "/" + pathtokens[numtokens - 2] + "/" + pathtokens[numtokens - 1]; + auto semaname = o2::ccdb::CcdbApi::determineSemaphoreName(absolutesnapshotdir, path); + removeSemaphore(semaname, remove); + } + } } + } catch (std::exception const& e) { + LOG(info) << "Semaphore search had exception " << e.what(); } +} - if (mInSnapshotMode) { // file must be there, otherwise a fatal will be produced - loadFileToMemory(dest, getSnapshotFile(mSnapshotTopPath, path), headers); +void CcdbApi::getFromSnapshot(bool createSnapshot, std::string const& path, + long timestamp, std::map& headers, + std::string& snapshotpath, o2::pmr::vector& dest, int& fromSnapshot, std::string const& etag) const +{ + if (createSnapshot) { // create named semaphore + std::string logfile = mSnapshotCachePath + "/log"; + std::fstream logStream = std::fstream(logfile, ios_base::out | ios_base::app); + if (logStream.is_open()) { + logStream << "CCDB-access[" << getpid() << "] of " << mUniqueAgentID << " to " << path << " timestamp " << timestamp << " for load to memory\n"; + } + } + if (mInSnapshotMode) { // file must be there, otherwise a fatal will be produced; + if (etag.empty()) { + loadFileToMemory(dest, getSnapshotFile(mSnapshotTopPath, path), &headers); + } fromSnapshot = 1; - } else if (mPreferSnapshotCache && std::filesystem::exists(snapshotpath = getSnapshotFile(mSnapshotCachePath, path))) { + } else if (mPreferSnapshotCache && std::filesystem::exists(snapshotpath)) { // if file is available, use it, otherwise cache it below from the server. Do this only when etag is empty since otherwise the object was already fetched and cached if (etag.empty()) { - loadFileToMemory(dest, snapshotpath, headers); + loadFileToMemory(dest, snapshotpath, &headers); } fromSnapshot = 2; - } else { // look on the server - CURL* curl_handle = curl_easy_init(); - string fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp); - - curl_slist* options_list = nullptr; - initCurlHTTPHeaderOptionsForRetrieve(curl_handle, options_list, timestamp, headers, etag, createdNotAfter, createdNotBefore); - - navigateURLsAndLoadFileToMemory(dest, curl_handle, fullUrl, headers); - - for (size_t hostIndex = 1; hostIndex < hostsPool.size() && isMemoryFileInvalid(dest); hostIndex++) { - fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp, hostIndex); - loadFileToMemory(dest, fullUrl, headers); // headers loaded from the file in case of the snapshot reading only - } - curl_slist_free_all(options_list); - curl_easy_cleanup(curl_handle); } +} - if (dest.empty()) { - sem_release(); - return; // nothing was fetched: either cached value is good or error was produced - } - // !considerSnapshot means that the call was made by retrieve for snapshoting reasons - logReading(path, timestamp, headers, fmt::format("{}{}", considerSnapshot ? "load to memory" : "retrieve", fromSnapshot ? " from snapshot" : "")); +void CcdbApi::saveSnapshot(RequestContext& requestContext) const +{ + // Consider saving snapshot + if (!mSnapshotCachePath.empty() && !(mInSnapshotMode && mSnapshotTopPath == mSnapshotCachePath)) { // store in the snapshot only if the object was not read from the snapshot + auto semaphore_barrier = std::make_unique(mSnapshotCachePath, requestContext.path); - // are we asked to create a snapshot ? - if (createSnapshot && fromSnapshot != 2 && !(mInSnapshotMode && mSnapshotTopPath == mSnapshotCachePath)) { // store in the snapshot only if the object was not read from the snapshot - auto snapshotdir = getSnapshotDir(mSnapshotCachePath, path); - snapshotpath = getSnapshotFile(mSnapshotCachePath, path); + auto snapshotdir = getSnapshotDir(mSnapshotCachePath, requestContext.path); + std::string snapshotpath = getSnapshotFile(mSnapshotCachePath, requestContext.path); o2::utils::createDirectoriesIfAbsent(snapshotdir); - if (logStream->is_open()) { - *logStream.get() << "CCDB-access[" << getpid() << "] ... " << mUniqueAgentID << " downloading to snapshot " << snapshotpath << " from memory\n"; + std::fstream logStream; + if (logStream.is_open()) { + logStream << "CCDB-access[" << getpid() << "] ... " << mUniqueAgentID << " downloading to snapshot " << snapshotpath << " from memory\n"; } { // dump image to a file - LOGP(debug, "creating snapshot {} -> {}", path, snapshotpath); - CCDBQuery querysummary(path, metadata, timestamp); + LOGP(debug, "creating snapshot {} -> {}", requestContext.path, snapshotpath); + CCDBQuery querysummary(requestContext.path, requestContext.metadata, requestContext.timestamp); { std::ofstream objFile(snapshotpath, std::ios::out | std::ofstream::binary); - std::copy(dest.begin(), dest.end(), std::ostreambuf_iterator(objFile)); + std::copy(requestContext.dest.begin(), requestContext.dest.end(), std::ostreambuf_iterator(objFile)); } // now open the same file as root file and store metadata - updateMetaInformationInLocalFile(snapshotpath, headers, &querysummary); + updateMetaInformationInLocalFile(snapshotpath, &requestContext.headers, &querysummary); } } - sem_release(); } -// navigate sequence of URLs until TFile content is found; object is extracted and returned -void CcdbApi::navigateURLsAndLoadFileToMemory(o2::pmr::vector& dest, CURL* curl_handle, std::string const& url, std::map* headers) const +void CcdbApi::loadFileToMemory(std::vector& dest, std::string const& path, + std::map const& metadata, long timestamp, + std::map* headers, std::string const& etag, + const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot) const { - // a global internal data structure that can be filled with HTTP header information - // static --> to avoid frequent alloc/dealloc as optimization - // not sure if thread_local takes away that benefit - static thread_local std::multimap headerData; - - // let's see first of all if the url is something specific that curl cannot handle - if (url.find("alien:/", 0) != std::string::npos) { - return loadFileToMemory(dest, url, nullptr); // headers loaded from the file in case of the snapshot reading only + o2::pmr::vector destP; + destP.reserve(dest.size()); + loadFileToMemory(destP, path, metadata, timestamp, headers, etag, createdNotAfter, createdNotBefore, considerSnapshot); + dest.clear(); + dest.reserve(destP.size()); + for (const auto c : destP) { + dest.push_back(c); } - // otherwise make an HTTP/CURL request - bool errorflag = false; - auto signalError = [&chunk = dest, &errorflag]() { - chunk.clear(); - chunk.reserve(1); - errorflag = true; - }; - auto writeCallBack = [](void* contents, size_t size, size_t nmemb, void* chunkptr) { - o2::pmr::vector& chunk = *static_cast*>(chunkptr); - size_t realsize = size * nmemb; - try { - chunk.reserve(chunk.size() + realsize); - char* contC = (char*)contents; - chunk.insert(chunk.end(), contC, contC + realsize); - } catch (std::exception e) { - LOGP(info, "failed to expand by {} bytes chunk provided to CURL: {}", realsize, e.what()); - realsize = 0; +} + +void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, std::string const& path, + std::map const& metadata, long timestamp, + std::map* headers, std::string const& etag, + const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot) const +{ + RequestContext requestContext(dest, metadata, *headers); + requestContext.path = path; + // std::map metadataCopy = metadata; // Create a copy because metadata will be passed as a pointer so it cannot be constant. The const in definition is for backwards compatability. + // requestContext.metadata = metadataCopy; + requestContext.timestamp = timestamp; + requestContext.etag = etag; + requestContext.createdNotAfter = createdNotAfter; + requestContext.createdNotBefore = createdNotBefore; + requestContext.considerSnapshot = considerSnapshot; + std::vector contexts = {requestContext}; + vectoredLoadFileToMemory(contexts); +} + +void CcdbApi::appendFlatHeader(o2::pmr::vector& dest, const std::map& headers) +{ + size_t hsize = getFlatHeaderSize(headers), cnt = dest.size(); + dest.resize(cnt + hsize); + auto addString = [&dest, &cnt](const std::string& s) { + for (char c : s) { + dest[cnt++] = c; } - return realsize; + dest[cnt++] = 0; }; - // specify URL to get - curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str()); - initCurlOptionsForRetrieve(curl_handle, (void*)&dest, writeCallBack, false); - curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callback); - headerData.clear(); - curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void*)&headerData); - curlSetSSLOptions(curl_handle); + for (auto& h : headers) { + addString(h.first); + addString(h.second); + } + *reinterpret_cast(&dest[cnt]) = hsize; // store size + std::memcpy(&dest[cnt + sizeof(int)], FlatHeaderAnnot, sizeof(FlatHeaderAnnot)); // annotate the flattened headers map +} - auto res = CURL_perform(curl_handle); - long response_code = -1; - if (res == CURLE_OK && curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK) { - if (headers) { - for (auto& p : headerData) { - (*headers)[p.first] = p.second; +void CcdbApi::navigateSourcesAndLoadFile(RequestContext& requestContext, int& fromSnapshot, size_t* requestCounter) const +{ + LOGP(debug, "loadFileToMemory {} ETag=[{}]", requestContext.path, requestContext.etag); + bool createSnapshot = requestContext.considerSnapshot && !mSnapshotCachePath.empty(); // create snaphot if absent + + std::string snapshotpath; + if (mInSnapshotMode || std::filesystem::exists(snapshotpath = getSnapshotFile(mSnapshotCachePath, requestContext.path))) { + auto semaphore_barrier = std::make_unique(mSnapshotCachePath, requestContext.path); + // if we are in snapshot mode we can simply open the file, unless the etag is non-empty: + // this would mean that the object was is already fetched and in this mode we don't to validity checks! + getFromSnapshot(createSnapshot, requestContext.path, requestContext.timestamp, requestContext.headers, snapshotpath, requestContext.dest, fromSnapshot, requestContext.etag); + } else { // look on the server + scheduleDownload(requestContext, requestCounter); + } +} + +void CcdbApi::vectoredLoadFileToMemory(std::vector& requestContexts) const +{ + std::vector fromSnapshots(requestContexts.size()); + size_t requestCounter = 0; + + // Get files from snapshots and schedule downloads + for (int i = 0; i < requestContexts.size(); i++) { + // navigateSourcesAndLoadFile either retrieves file from snapshot immediately, or schedules it to be downloaded when mDownloader->runLoop is ran at a later time + auto& requestContext = requestContexts.at(i); + navigateSourcesAndLoadFile(requestContext, fromSnapshots.at(i), &requestCounter); + } + + // Download the rest + while (requestCounter > 0) { + mDownloader->runLoop(0); + } + + // Save snapshots + for (int i = 0; i < requestContexts.size(); i++) { + auto& requestContext = requestContexts.at(i); + if (!requestContext.dest.empty()) { + logReading(requestContext.path, requestContext.timestamp, &requestContext.headers, + fmt::format("{}{}", requestContext.considerSnapshot ? "load to memory" : "retrieve", fromSnapshots.at(i) ? " from snapshot" : "")); + if (requestContext.considerSnapshot && fromSnapshots.at(i) != 2) { + saveSnapshot(requestContext); } } - if (200 <= response_code && response_code < 300) { - // good response and the content is directly provided and should have been dumped into "chunk" - } else if (response_code == 304) { - LOGP(debug, "Object exists but I am not serving it since it's already in your possession"); - } - // this is a more general redirection - else if (300 <= response_code && response_code < 400) { - // we try content locations in order of appearance until one succeeds - // 1st: The "Location" field - // 2nd: Possible "Content-Location" fields - Location field - // some locations are relative to the main server so we need to fix/complement them - auto complement_Location = [this](std::string const& loc) { - if (loc[0] == '/') { - // if it's just a path (noticed by trailing '/' we prepend the server url - return getURL() + loc; - } - return loc; - }; + } +} - std::vector locs; - auto iter = headerData.find("Location"); - if (iter != headerData.end()) { - locs.push_back(complement_Location(iter->second)); - } - // add alternative locations (not yet included) - auto iter2 = headerData.find("Content-Location"); - if (iter2 != headerData.end()) { - auto range = headerData.equal_range("Content-Location"); - for (auto it = range.first; it != range.second; ++it) { - if (std::find(locs.begin(), locs.end(), it->second) == locs.end()) { - locs.push_back(complement_Location(it->second)); - } - } - } - for (auto& l : locs) { - if (l.size() > 0) { - LOG(debug) << "Trying content location " << l; - navigateURLsAndLoadFileToMemory(dest, curl_handle, l, headers); - if (dest.size()) { /* or other success marker in future */ - break; - } - } - } - } else if (response_code == 404) { - LOG(error) << "Requested resource does not exist: " << url; - signalError(); +bool CcdbApi::loadLocalContentToMemory(o2::pmr::vector& dest, std::string& url) const +{ + if (url.find("alien:/", 0) != std::string::npos) { + std::map localHeaders; + loadFileToMemory(dest, url, &localHeaders, false); + auto it = localHeaders.find("Error"); + if (it != localHeaders.end() && it->second == "An error occurred during retrieval") { + return false; } else { - LOG(error) << "Error in fetching object " << url << ", curl response code:" << response_code; - signalError(); + return true; } - } else { - LOGP(alarm, "Curl request to {} failed with result {}, response code: {}", url, int(res), response_code); - signalError(); } - // indicate that an error occurred ---> used by caching layers (such as CCDBManager) - if (errorflag && headers) { - (*headers)["Error"] = "An error occurred during retrieval"; + if ((url.find("file:/", 0) != std::string::npos)) { + std::string path = url.substr(7); + if (std::filesystem::exists(path)) { + std::map localHeaders; + loadFileToMemory(dest, url, &localHeaders, o2::utils::Str::endsWith(path, ".root")); + auto it = localHeaders.find("Error"); + if (it != localHeaders.end() && it->second == "An error occurred during retrieval") { + return false; + } else { + return true; + } + } } - return; + return false; } -void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, const std::string& path, std::map* localHeaders) const +void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, const std::string& path, std::map* localHeaders, bool fetchLocalMetaData) const { // Read file to memory as vector. For special case of the locally cached file retriev metadata stored directly in the file constexpr size_t MaxCopySize = 0x1L << 25; @@ -1700,7 +2063,7 @@ void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, const std::string& p totalread += nread; } while (nread == (long)MaxCopySize); - if (localHeaders) { + if (localHeaders && fetchLocalMetaData) { TMemFile memFile("name", const_cast(dest.data()), dest.size(), "READ"); auto storedmeta = (std::map*)extractFromTFile(memFile, TClass::GetClass("std::map"), CCDBMETA_ENTRY); if (storedmeta) { @@ -1710,6 +2073,9 @@ void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, const std::string& p if ((isSnapshotMode() || mPreferSnapshotCache) && localHeaders->find("ETag") == localHeaders->end()) { // generate dummy ETag to profit from the caching (*localHeaders)["ETag"] = path; } + if (localHeaders->find("fileSize") == localHeaders->end()) { + (*localHeaders)["fileSize"] = fmt::format("{}", memFile.GetEND()); + } } return; } @@ -1756,12 +2122,74 @@ void CcdbApi::logReading(const std::string& path, long ts, const std::mapasynchSchedule(handle, requestCounter); +} + CURLcode CcdbApi::CURL_perform(CURL* handle) const { - if (mIsCCDBDownloaderEnabled) { + if (mIsCCDBDownloaderPreferred) { return mDownloader->perform(handle); } - return curl_easy_perform(handle); + CURLcode result; + for (int i = 1; i <= mCurlRetries && (result = curl_easy_perform(handle)) != CURLE_OK; i++) { + usleep(mCurlDelayRetries * i); + } + return result; +} + +/** + * Object, encapsulating a semaphore, regulating + * concurrent (multi-process) access to CCDB snapshot files. + */ +CCDBSemaphore::CCDBSemaphore(std::string const& snapshotpath, std::string const& path) +{ + LOG(debug) << "Entering semaphore barrier"; + mSemName = CcdbApi::determineSemaphoreName(snapshotpath, path); + try { + mSem = new boost::interprocess::named_semaphore(boost::interprocess::open_or_create_t{}, mSemName.c_str(), 1); + } catch (std::exception e) { + LOG(warn) << "Exception occurred during CCDB (cache) semaphore setup; Continuing without"; + mSem = nullptr; + } + // automatically wait + if (mSem) { + gSemaRegistry.add(this); + mSem->wait(); + } +} + +CCDBSemaphore::~CCDBSemaphore() +{ + LOG(debug) << "Ending semaphore barrier"; + if (mSem) { + mSem->post(); + if (mSem->try_wait()) { // if nobody else is waiting remove the semaphore resource + mSem->post(); + boost::interprocess::named_semaphore::remove(mSemName.c_str()); + } + gSemaRegistry.remove(this); + } +} + +SemaphoreRegistry::~SemaphoreRegistry() +{ + LOG(debug) << "Cleaning up semaphore registry with count " << mStore.size(); + for (auto& s : mStore) { + delete s; + mStore.erase(s); + } +} + +void SemaphoreRegistry::add(CCDBSemaphore const* ptr) +{ + mStore.insert(ptr); +} + +void SemaphoreRegistry::remove(CCDBSemaphore const* ptr) +{ + mStore.erase(ptr); } -} // namespace o2 +} // namespace o2::ccdb diff --git a/CCDB/src/CleanCCDBSemaphores.cxx b/CCDB/src/CleanCCDBSemaphores.cxx new file mode 100644 index 0000000000000..f107cde21a051 --- /dev/null +++ b/CCDB/src/CleanCCDBSemaphores.cxx @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CCDB/CcdbApi.h" +#include +#include + +namespace bpo = boost::program_options; + +bool initOptionsAndParse(bpo::options_description& options, int argc, char* argv[], bpo::variables_map& vm) +{ + options.add_options()( + "cachepath,p", bpo::value()->default_value("ccdb"), "path to whole CCDB cache dir as a basis for semaphore search")( + "sema,s", bpo::value()->default_value(""), "Specific named semaphore to be remove")( + "help,h", "Produce help message."); + + try { + bpo::store(parse_command_line(argc, argv, options), vm); + // help + if (vm.count("help")) { + std::cout << options << std::endl; + return false; + } + bpo::notify(vm); + } catch (const bpo::error& e) { + std::cerr << e.what() << "\n\n"; + std::cerr << "Error parsing command line arguments; Available options:\n"; + + std::cerr << options << std::endl; + return false; + } + return true; +} + +// A simple tool to clean CCDB related semaphores +int main(int argc, char* argv[]) +{ + bpo::options_description options("Tool to find and remove leaking CCDB semaphore from the system"); + bpo::variables_map vm; + if (!initOptionsAndParse(options, argc, argv, vm)) { + return 1; + } + + std::string sema = vm["sema"].as(); + if (sema.size() > 0) { + if (o2::ccdb::CcdbApi::removeSemaphore(sema, true)) { + std::cout << "Successfully removed " << sema << "\n"; + } + } + + std::string path = vm["cachepath"].as(); + o2::ccdb::CcdbApi::removeLeakingSemaphores(path, true); + return 0; +} diff --git a/CCDB/src/UploadTool.cxx b/CCDB/src/UploadTool.cxx index 44b8d8e20bc7d..9aba417b4f4a9 100644 --- a/CCDB/src/UploadTool.cxx +++ b/CCDB/src/UploadTool.cxx @@ -147,33 +147,44 @@ int main(int argc, char* argv[]) meta[p.first] = p.second; } - TFile f(filename.c_str()); - auto key = f.GetKey(keyname.c_str()); - if (key) { - // get type of key - auto classname = key->GetClassName(); - auto tcl = TClass::GetClass(classname); - auto object = f.Get(keyname.c_str()); - if (tcl->InheritsFrom("TTree")) { - auto tree = static_cast(object); - tree->LoadBaskets(0x1L << 32); // make tree memory based - tree->SetDirectory(nullptr); - } - // convert classname to typeinfo - // typeinfo - auto ti = tcl->GetTypeInfo(); - - std::cout << " Uploading an object of type " << key->GetClassName() - << " to path " << path << " with timestamp validity from " << starttimestamp - << " to " << endtimestamp << "\n"; - - api.storeAsTFile_impl(object, *ti, path, meta, starttimestamp, endtimestamp); + if (filename == "headersOnly") { + auto ent = meta.find("Redirect"); + std::cout << " Uploading a headers-only object to path " << path << " with timestamp validity from " << starttimestamp << " to " << endtimestamp + << " Redirection to: " << ((ent != meta.end()) ? ent->second : std::string{"none"}) << "\n"; + api.storeAsBinaryFile(nullptr, 0, "ignored", "", path, meta, starttimestamp, endtimestamp); if (!api.isSnapshotMode() && meta.find("adjustableEOV") != meta.end() && meta.find("default") == meta.end()) { - o2::ccdb::CcdbObjectInfo oi(path, classname, filename, meta, starttimestamp, endtimestamp); + o2::ccdb::CcdbObjectInfo oi(path, "", "", meta, starttimestamp, endtimestamp); o2::ccdb::adjustOverriddenEOV(api, oi); } } else { - std::cerr << "Key " << keyname << " does not exist\n"; + TFile f(filename.c_str()); + auto key = f.GetKey(keyname.c_str()); + if (key) { + // get type of key + auto classname = key->GetClassName(); + auto tcl = TClass::GetClass(classname); + auto object = f.Get(keyname.c_str()); + if (tcl->InheritsFrom("TTree")) { + auto tree = static_cast(object); + tree->LoadBaskets(0x1L << 32); // make tree memory based + tree->SetDirectory(nullptr); + } + // convert classname to typeinfo + // typeinfo + auto ti = tcl->GetTypeInfo(); + + std::cout << " Uploading an object of type " << key->GetClassName() + << " to path " << path << " with timestamp validity from " << starttimestamp + << " to " << endtimestamp << "\n"; + + api.storeAsTFile_impl(object, *ti, path, meta, starttimestamp, endtimestamp); + if (!api.isSnapshotMode() && meta.find("adjustableEOV") != meta.end() && meta.find("default") == meta.end()) { + o2::ccdb::CcdbObjectInfo oi(path, classname, filename, meta, starttimestamp, endtimestamp); + o2::ccdb::adjustOverriddenEOV(api, oi); + } + } else { + std::cerr << "Key " << keyname << " does not exist\n"; + } } return 0; diff --git a/CCDB/test/testBasicCCDBManager.cxx b/CCDB/test/testBasicCCDBManager.cxx index 7cd143f655547..6359bf2f5ccf4 100644 --- a/CCDB/test/testBasicCCDBManager.cxx +++ b/CCDB/test/testBasicCCDBManager.cxx @@ -26,7 +26,7 @@ using namespace o2::ccdb; -static string basePath; +static std::string basePath; std::string ccdbUrl = "http://ccdb-test.cern.ch:8080"; bool hostReachable = false; @@ -43,7 +43,7 @@ struct Fixture { std::cout << "Is host reachable ? --> " << hostReachable << std::endl; char hostname[_POSIX_HOST_NAME_MAX]; gethostname(hostname, _POSIX_HOST_NAME_MAX); - basePath = string("Test/") + hostname + "/pid" + getpid() + "/BasicCCDBManager/"; + basePath = std::string("Test/") + hostname + "/pid" + getpid() + "/BasicCCDBManager/"; std::cout << "Path we will use in this test suite : " + basePath << std::endl; } ~Fixture() diff --git a/CCDB/test/testCcdbApi.cxx b/CCDB/test/testCcdbApi.cxx index 9d04392cc550d..1b6a5d6f0967a 100644 --- a/CCDB/test/testCcdbApi.cxx +++ b/CCDB/test/testCcdbApi.cxx @@ -14,6 +14,7 @@ /// \author Barthelemy von Haller /// +#define BOOST_BIND_GLOBAL_PLACEHOLDERS #define BOOST_TEST_MODULE CCDB #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK @@ -44,8 +45,8 @@ using namespace o2::ccdb; namespace utf = boost::unit_test; namespace tt = boost::test_tools; -static string ccdbUrl; -static string basePath; +static std::string ccdbUrl; +static std::string basePath; bool hostReachable = false; /** @@ -62,7 +63,7 @@ struct Fixture { cout << "Is host reachable ? --> " << hostReachable << endl; char hostname[_POSIX_HOST_NAME_MAX]; gethostname(hostname, _POSIX_HOST_NAME_MAX); - basePath = string("Test/TestCcdbApi/") + hostname + "/pid" + getpid() + "/"; + basePath = std::string("Test/TestCcdbApi/") + hostname + "/pid" + getpid() + "/"; // Replace dashes by underscores to avoid problems in the creation of local directories std::replace(basePath.begin(), basePath.end(), '-','_'); cout << "Path we will use in this test suite : " + basePath << endl; @@ -71,7 +72,7 @@ struct Fixture { { if (hostReachable) { CcdbApi api; - map metadata; + std::map metadata; api.init(ccdbUrl); api.truncate(basePath + "*"); cout << "Test data truncated (" << basePath << ")" << endl; @@ -103,7 +104,7 @@ struct test_fixture { ~test_fixture() = default; CcdbApi api; - map metadata; + std::map metadata; }; BOOST_AUTO_TEST_CASE(storeTMemFile_test, *utf::precondition(if_reachable())) @@ -152,7 +153,7 @@ BOOST_AUTO_TEST_CASE(store_retrieve_TMemFile_templated_test, *utf::precondition( BOOST_CHECK(f.api.retrieveFromTFileAny(basePath + "CCDBPath", f.metadata) == nullptr); // try to get the headers back and to find the metadata - map md; + std::map md; path2 = f.api.retrieveFromTFileAny(basePath + "CCDBPath", f.metadata, -1, &md); BOOST_CHECK_EQUAL(md.count("Hello"), 1); BOOST_CHECK_EQUAL(md["Hello"], "World"); @@ -344,7 +345,7 @@ BOOST_AUTO_TEST_CASE(delete_test, *utf::precondition(if_reachable())) BOOST_CHECK(h2 == nullptr); } -void countItems(const string& s, int& countObjects, int& countSubfolders) +void countItems(const std::string& s, int& countObjects, int& countSubfolders) { countObjects = 0; countSubfolders = 0; @@ -367,7 +368,7 @@ BOOST_AUTO_TEST_CASE(list_test, *utf::precondition(if_reachable())) test_fixture f; // test non-empty top dir - string s = f.api.list("", "application/json"); // top dir + std::string s = f.api.list("", "application/json"); // top dir long nbLines = std::count(s.begin(), s.end(), '\n') + 1; BOOST_CHECK(nbLines > 5); @@ -435,7 +436,7 @@ BOOST_AUTO_TEST_CASE(TestHeaderParsing) BOOST_AUTO_TEST_CASE(TestFetchingHeaders, *utf::precondition(if_reachable())) { // first store the object - string objectPath = basePath + "objectETag"; + std::string objectPath = basePath + "objectETag"; test_fixture f; TH1F h1("objectETag", "objectETag", 100, 0, 99); f.api.storeAsTFile(&h1, objectPath, f.metadata); @@ -444,7 +445,7 @@ BOOST_AUTO_TEST_CASE(TestFetchingHeaders, *utf::precondition(if_reachable())) std::string etag; std::vector headers; std::vector pfns; - string path = objectPath + "/" + std::to_string(getCurrentTimestamp()); + std::string path = objectPath + "/" + std::to_string(getCurrentTimestamp()); auto updated = CcdbApi::getCCDBEntryHeaders("http://ccdb-test.cern.ch:8080/" + path, etag, headers); BOOST_CHECK_EQUAL(updated, true); BOOST_REQUIRE(headers.size() != 0); @@ -461,7 +462,7 @@ BOOST_AUTO_TEST_CASE(TestRetrieveHeaders, *utf::precondition(if_reachable())) TH1F h1("object1", "object1", 100, 0, 99); cout << "storing object 1 in " << basePath << "Test" << endl; - map metadata; + std::map metadata; metadata["custom"] = "whatever"; f.api.storeAsTFile(&h1, basePath + "Test", metadata); @@ -497,7 +498,7 @@ BOOST_AUTO_TEST_CASE(TestUpdateMetadata, *utf::precondition(if_reachable())) // upload an object TH1F h1("object1", "object1", 100, 0, 99); cout << "storing object 1 in " << basePath << "Test" << endl; - map metadata; + std::map metadata; metadata["custom"] = "whatever"; metadata["id"] = "first"; f.api.storeAsTFile(&h1, basePath + "Test", metadata); @@ -506,10 +507,10 @@ BOOST_AUTO_TEST_CASE(TestUpdateMetadata, *utf::precondition(if_reachable())) std::map headers = f.api.retrieveHeaders(basePath + "Test", metadata); BOOST_CHECK(headers.count("custom") > 0); BOOST_CHECK(headers.at("custom") == "whatever"); - string firstID = headers.at("ETag"); + std::string firstID = headers.at("ETag"); firstID.erase(std::remove(firstID.begin(), firstID.end(), '"'), firstID.end()); - map newMetadata; + std::map newMetadata; newMetadata["custom"] = "somethingelse"; // update the metadata and check @@ -528,7 +529,7 @@ BOOST_AUTO_TEST_CASE(TestUpdateMetadata, *utf::precondition(if_reachable())) // get id cout << "get id" << endl; headers = f.api.retrieveHeaders(basePath + "Test", metadata); - string secondID = headers.at("ETag"); + std::string secondID = headers.at("ETag"); secondID.erase(std::remove(secondID.begin(), secondID.end(), '"'), secondID.end()); // update the metadata by id @@ -552,3 +553,47 @@ BOOST_AUTO_TEST_CASE(TestUpdateMetadata, *utf::precondition(if_reachable())) BOOST_CHECK(headers.count("custom") > 0); BOOST_CHECK(headers.at("custom") == "second"); } + +BOOST_AUTO_TEST_CASE(multi_host_test) +{ + CcdbApi api; + api.init("http://bogus-host.cern.ch,http://ccdb-test.cern.ch:8080"); + std::map metadata; + std::map headers; + o2::pmr::vector dst; + std::string url = "Analysis/ALICE3/Centrality"; + api.loadFileToMemory(dst, url, metadata, 1645780010602, &headers, "", "", "", true); + BOOST_CHECK(dst.size() != 0); +} + +BOOST_AUTO_TEST_CASE(vectored) +{ + CcdbApi api; + api.init("http://ccdb-test.cern.ch:8080"); + + int TEST_SAMPLE_SIZE = 5; + std::vector> dests(TEST_SAMPLE_SIZE); + std::vector> metadatas(TEST_SAMPLE_SIZE); + std::vector> headers(TEST_SAMPLE_SIZE); + + std::vector contexts; + for (int i = 0; i < TEST_SAMPLE_SIZE; i++) { + contexts.push_back(CcdbApi::RequestContext(dests.at(i), metadatas.at(i), headers.at(i))); + contexts.at(i).path = "Analysis/ALICE3/Centrality"; + contexts.at(i).timestamp = 1645780010602; + contexts.at(i).considerSnapshot = true; + } + + api.vectoredLoadFileToMemory(contexts); + + for (auto context : contexts) { + BOOST_CHECK(context.dest.size() != 0); + } +} + +BOOST_AUTO_TEST_CASE(empty_url) +{ + CcdbApi api; + string url = ""; + BOOST_CHECK_EXCEPTION(api.init(url), invalid_argument, [](std::invalid_argument const&) -> bool { return true; }); +} diff --git a/CCDB/test/testCcdbApiDownloader.cxx b/CCDB/test/testCcdbApiDownloader.cxx index 80e2826209bb4..76686f5ee1c00 100644 --- a/CCDB/test/testCcdbApiDownloader.cxx +++ b/CCDB/test/testCcdbApiDownloader.cxx @@ -13,6 +13,8 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include "CommonUtils/StringUtils.h" +#include "CCDB/CCDBTimeStampUtils.h" #include #include #include @@ -21,9 +23,12 @@ #include #include -#include +#include #include +#include +#include "MemoryResources/MemoryResources.h" + using namespace std; namespace o2 @@ -46,15 +51,137 @@ size_t CurlWrite_CallbackFunc_StdString2(void* contents, size_t size, size_t nme return size * nmemb; } +std::string uniqueAgentID() +{ + std::string host = boost::asio::ip::host_name(); + char const* jobID = getenv("ALIEN_PROC_ID"); + if (jobID) { + return fmt::format("{}-{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6), jobID); + } else { + return fmt::format("{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6)); + } +} + CURL* createTestHandle(std::string* dst) { CURL* handle = curl_easy_init(); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString2); curl_easy_setopt(handle, CURLOPT_WRITEDATA, dst); - curl_easy_setopt(handle, CURLOPT_URL, "http://ccdb-test.cern.ch:8080/latest/"); + curl_easy_setopt(handle, CURLOPT_URL, "http://ccdb-test.cern.ch:8080/"); + auto userAgent = uniqueAgentID(); + curl_easy_setopt(handle, CURLOPT_USERAGENT, userAgent.c_str()); return handle; } +namespace +{ +template > +size_t header_map_callback(char* buffer, size_t size, size_t nitems, void* userdata) +{ + auto* headers = static_cast(userdata); + auto header = std::string(buffer, size * nitems); + std::string::size_type index = header.find(':', 0); + if (index != std::string::npos) { + const auto key = boost::algorithm::trim_copy(header.substr(0, index)); + const auto value = boost::algorithm::trim_copy(header.substr(index + 1)); + headers->insert(std::make_pair(key, value)); + } + return size * nitems; +} +} // namespace + +size_t writeCallbackNoLambda(void* contents, size_t size, size_t nmemb, void* chunkptr) +{ + auto& ho = *static_cast(chunkptr); + auto& chunk = *ho.object; + size_t realsize = size * nmemb, sz = 0; + ho.counter++; + try { + if (chunk.capacity() < chunk.size() + realsize) { + auto cl = ho.header.find("Content-Length"); + if (cl != ho.header.end()) { + sz = std::max(chunk.size() + realsize, (size_t)std::stol(cl->second)); + } else { + sz = chunk.size() + realsize; + // LOGP(debug, "SIZE IS NOT IN HEADER, allocate {}", sz); + } + chunk.reserve(sz); + } + char* contC = (char*)contents; + chunk.insert(chunk.end(), contC, contC + realsize); + } catch (std::exception e) { + // LOGP(alarm, "failed to reserve {} bytes in CURL write callback (realsize = {}): {}", sz, realsize, e.what()); + realsize = 0; + } + return realsize; +} + +std::vector prepareAsyncHandles(size_t num, std::vector*>& dests) +{ + std::vector handles; + + for (int i = 0; i < num; i++) { + auto dest = new o2::pmr::vector(); + dests.push_back(dest); + CURL* curl_handle = curl_easy_init(); + handles.push_back(curl_handle); + + auto data = new DownloaderRequestData(); + data->hoPair.object = dest; + data->hosts.emplace_back("http://ccdb-test.cern.ch:8080"); + data->path = "Analysis/ALICE3/Centrality"; + data->timestamp = 1646729604010; + data->localContentCallback = nullptr; + + curl_easy_setopt(curl_handle, CURLOPT_URL, "http://ccdb-test.cern.ch:8080/Analysis/ALICE3/Centrality/1646729604010"); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeCallbackNoLambda); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)&(data->hoPair)); + + curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callbackhoPair.header)>); + curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void*)&(data->hoPair.header)); + curl_easy_setopt(curl_handle, CURLOPT_PRIVATE, (void*)data); + } + return handles; +} + +BOOST_AUTO_TEST_CASE(asynch_schedule_test) +{ + int TRANSFERS = 5; + + if (curl_global_init(CURL_GLOBAL_ALL)) { + fprintf(stderr, "Could not init curl\n"); + return; + } + + CCDBDownloader downloader; + std::vector*> dests; + auto handles = prepareAsyncHandles(TRANSFERS, dests); + size_t transfersLeft = 0; + + for (auto handle : handles) { + downloader.asynchSchedule(handle, &transfersLeft); + } + + while (transfersLeft > 0) { + downloader.runLoop(false); + } + + for (int i = 0; i < TRANSFERS; i++) { + // I would claim that accessing the handles after they are complete + // is actually not supported by the current API, because it was + // previously relying on leaking the handles. Disabling the whole + // thing until we verify that's actually the case. + // + // long httpCode; + // curl_easy_getinfo(handles[i], CURLINFO_HTTP_CODE, &httpCode); + // BOOST_CHECK_EQUAL(httpCode, 200); + // BOOST_CHECK_NE(dests[i]->size(), 0); + // curl_easy_cleanup(handles[i]); + delete dests[i]; + } + curl_global_cleanup(); +} + BOOST_AUTO_TEST_CASE(perform_test) { if (curl_global_init(CURL_GLOBAL_ALL)) { @@ -68,13 +195,11 @@ BOOST_AUTO_TEST_CASE(perform_test) CURLcode curlCode = downloader.perform(handle); - BOOST_CHECK(curlCode == CURLE_OK); - std::cout << "CURL code: " << curlCode << "\n"; + BOOST_CHECK_EQUAL(curlCode, CURLE_OK); long httpCode; curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - std::cout << "HTTP code: " << httpCode << "\n"; + BOOST_CHECK_EQUAL(httpCode, 200); curl_easy_cleanup(handle); curl_global_cleanup(); @@ -97,19 +222,13 @@ BOOST_AUTO_TEST_CASE(blocking_batch_test) auto curlCodes = downloader.batchBlockingPerform(handleVector); for (CURLcode code : curlCodes) { - BOOST_CHECK(code == CURLE_OK); - if (code != CURLE_OK) { - std::cout << "CURL Code: " << code << "\n"; - } + BOOST_CHECK_EQUAL(code, CURLE_OK); } for (CURL* handle : handleVector) { long httpCode; curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - if (httpCode != 200) { - std::cout << "HTTP Code: " << httpCode << "\n"; - } + BOOST_CHECK_EQUAL(httpCode, 200); curl_easy_cleanup(handle); } @@ -120,7 +239,7 @@ BOOST_AUTO_TEST_CASE(blocking_batch_test) curl_global_cleanup(); } -BOOST_AUTO_TEST_CASE(asynch_batch_test) +BOOST_AUTO_TEST_CASE(test_with_break) { if (curl_global_init(CURL_GLOBAL_ALL)) { fprintf(stderr, "Could not init curl\n"); @@ -130,31 +249,21 @@ BOOST_AUTO_TEST_CASE(asynch_batch_test) CCDBDownloader downloader; std::vector handleVector; std::vector destinations; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 100; i++) { destinations.push_back(new std::string()); handleVector.push_back(createTestHandle(destinations.back())); } - bool flag = false; - auto curlCodes = downloader.batchAsynchPerform(handleVector, &flag); - while (!flag) { - sleep(1); - } + auto curlCodes = downloader.batchBlockingPerform(handleVector); for (CURLcode code : curlCodes) { - BOOST_CHECK(code == CURLE_OK); - if (code != CURLE_OK) { - std::cout << "CURL Code: " << code << "\n"; - } + BOOST_CHECK_EQUAL(code, CURLE_OK); } for (CURL* handle : handleVector) { long httpCode; curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - if (httpCode != 200) { - std::cout << "HTTP Code: " << httpCode << "\n"; - } + BOOST_CHECK_EQUAL(httpCode, 200); curl_easy_cleanup(handle); } @@ -162,29 +271,6 @@ BOOST_AUTO_TEST_CASE(asynch_batch_test) delete dst; } - curl_global_cleanup(); -} - -BOOST_AUTO_TEST_CASE(test_with_break) -{ - if (curl_global_init(CURL_GLOBAL_ALL)) { - fprintf(stderr, "Could not init curl\n"); - return; - } - - CCDBDownloader downloader; - std::vector handleVector; - std::vector destinations; - for (int i = 0; i < 100; i++) { - destinations.push_back(new std::string()); - handleVector.push_back(createTestHandle(destinations.back())); - } - - auto curlCodes = downloader.batchBlockingPerform(handleVector); - for (std::string* dst : destinations) { - delete dst; - } - sleep(10); std::vector handleVector2; @@ -196,19 +282,13 @@ BOOST_AUTO_TEST_CASE(test_with_break) auto curlCodes2 = downloader.batchBlockingPerform(handleVector2); for (CURLcode code : curlCodes2) { - BOOST_CHECK(code == CURLE_OK); - if (code != CURLE_OK) { - std::cout << "CURL Code: " << code << "\n"; - } + BOOST_CHECK_EQUAL(code, CURLE_OK); } for (CURL* handle : handleVector2) { long httpCode; curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - if (httpCode != 200) { - std::cout << "HTTP Code: " << httpCode << "\n"; - } + BOOST_CHECK_EQUAL(httpCode, 200); curl_easy_cleanup(handle); } @@ -219,87 +299,80 @@ BOOST_AUTO_TEST_CASE(test_with_break) curl_global_cleanup(); } -void testCallback(void* ptr) +void onUVClose(uv_handle_t* handle) { - int* intPtr = (int*)ptr; - *intPtr = 46; + if (handle != nullptr) { + free(handle); + } } -BOOST_AUTO_TEST_CASE(asynch_batch_callback) +void closeAllHandles(uv_handle_t* handle, void* arg) { - if (curl_global_init(CURL_GLOBAL_ALL)) { - fprintf(stderr, "Could not init curl\n"); - return; - } - - CCDBDownloader downloader; - std::vector handleVector; - std::vector destinations; - for (int i = 0; i < 10; i++) { - destinations.push_back(new std::string()); - handleVector.push_back(createTestHandle(destinations.back())); - } - - int testValue = 0; - - bool flag = false; - auto curlCodes = downloader.asynchBatchPerformWithCallback(handleVector, &flag, testCallback, &testValue); - while (!flag) { - sleep(1); - } - - BOOST_CHECK(testValue == 46); - - for (CURLcode code : curlCodes) { - BOOST_CHECK(code == CURLE_OK); - if (code != CURLE_OK) { - std::cout << "CURL Code: " << code << "\n"; - } - } - - for (CURL* handle : handleVector) { - long httpCode; - curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - if (httpCode != 200) { - std::cout << "HTTP Code: " << httpCode << "\n"; - } - curl_easy_cleanup(handle); - } - - for (std::string* dst : destinations) { - delete dst; + if (!uv_is_closing(handle)) { + uv_close(handle, onUVClose); } +} - curl_global_cleanup(); +void testTimerCB(uv_timer_t* handle) +{ + // Mock function to be used by tested timer } BOOST_AUTO_TEST_CASE(external_loop_test) { + // Prepare uv_loop to be provided to the downloader + auto uvLoop = new uv_loop_t(); + uv_loop_init(uvLoop); + + // Prepare test timer. It will be used to check whether the downloader affects external handles. + auto testTimer = (uv_timer_t*)malloc(sizeof(uv_timer_t)); + uv_timer_init(uvLoop, testTimer); + uv_timer_start(testTimer, testTimerCB, 10, 10); + if (curl_global_init(CURL_GLOBAL_ALL)) { fprintf(stderr, "Could not init curl\n"); return; } - uv_loop_t loop; - - CCDBDownloader downloader(&loop); + // Regular downloader test + auto downloader = new o2::ccdb::CCDBDownloader(uvLoop); std::string dst = ""; CURL* handle = createTestHandle(&dst); - CURLcode curlCode = downloader.perform(handle); + CURLcode curlCode = downloader->perform(handle); - BOOST_CHECK(curlCode == CURLE_OK); - std::cout << "CURL code: " << curlCode << "\n"; + BOOST_CHECK_EQUAL(curlCode, CURLE_OK); long httpCode; curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - std::cout << "HTTP code: " << httpCode << "\n"; + BOOST_CHECK_EQUAL(httpCode, 200); curl_easy_cleanup(handle); - curl_global_cleanup(); + + // Check if test timer and external loop are still alive + BOOST_CHECK_NE(uv_is_active((uv_handle_t*)testTimer), 0); + BOOST_CHECK_NE(uv_loop_alive(uvLoop), 0); + + // Downloader must be closed before uv_loop. + // The reason for that are the uv_poll handles attached to the curl multi handle. + // The multi handle must be cleaned (via destuctor) before poll handles attached to them are removed (via walking and closing). + delete downloader; + while (uv_loop_alive(uvLoop) || uv_loop_close(uvLoop) == UV_EBUSY) { + uv_walk(uvLoop, closeAllHandles, nullptr); + uv_run(uvLoop, UV_RUN_ONCE); + } + delete uvLoop; +} + +BOOST_AUTO_TEST_CASE(trim_host_url_test) +{ + CCDBDownloader downloader; + BOOST_CHECK_EQUAL(downloader.trimHostUrl("http://localhost:8080"), "http://localhost:8080"); + BOOST_CHECK_EQUAL(downloader.trimHostUrl("http://localhost"), "http://localhost"); + BOOST_CHECK_EQUAL(downloader.trimHostUrl("http://localhost:8080/some/path"), "http://localhost:8080"); + BOOST_CHECK_EQUAL(downloader.trimHostUrl("http://localhost/some/path"), "http://localhost"); + BOOST_CHECK_EQUAL(downloader.trimHostUrl("http://localhost:8080/Task/Detector/1?HTTPOnly=true"), "http://localhost:8080"); } } // namespace ccdb diff --git a/CCDB/test/testCcdbApiHeaders.cxx b/CCDB/test/testCcdbApiHeaders.cxx new file mode 100644 index 0000000000000..bcfa2a5b44bc2 --- /dev/null +++ b/CCDB/test/testCcdbApiHeaders.cxx @@ -0,0 +1,413 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file testCcdbApiHeaders.cxx +/// \brief Test BasicCCDBManager header/metadata information functionality with caching +/// \author martin.oines.eide@cern.ch + +#define BOOST_TEST_MODULE CCDB +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include "CCDB/BasicCCDBManager.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "CCDB/CcdbApi.h" +#include + +static std::string basePath; +// std::string ccdbUrl = "http://localhost:8080"; +std::string ccdbUrl = "http://ccdb-test.cern.ch:8080"; +bool hostReachable = false; + +/** + * Global fixture, ie general setup and teardown + * Copied from testBasicCCDBManager.cxx + */ +struct Fixture { + Fixture() + { + auto& ccdbManager = o2::ccdb::BasicCCDBManager::instance(); + if (std::getenv("ALICEO2_CCDB_HOST")) { + ccdbUrl = std::string(std::getenv("ALICEO2_CCDB_HOST")); + } + ccdbManager.setURL(ccdbUrl); + hostReachable = ccdbManager.getCCDBAccessor().isHostReachable(); + char hostname[_POSIX_HOST_NAME_MAX]; + gethostname(hostname, _POSIX_HOST_NAME_MAX); + basePath = std::string("Users/m/meide/Tests/") + hostname + "/pid-" + getpid() + "/BasicCCDBManager/"; + + LOG(info) << "Path we will use in this test suite : " + basePath << std::endl; + LOG(info) << "ccdb url: " << ccdbUrl << std::endl; + LOG(info) << "Is host reachable ? --> " << hostReachable << std::endl; + } + ~Fixture() + { + if (hostReachable) { + o2::ccdb::BasicCCDBManager::instance().getCCDBAccessor().truncate(basePath + "*"); // This deletes the data after test is run, disable if you want to inspect the data + LOG(info) << "Test data truncated/deleted (" << basePath << ")" << std::endl; + } + } +}; +BOOST_GLOBAL_FIXTURE(Fixture); +/** + * Just an accessor to the hostReachable variable to be used to determine whether tests can be ran or not. + * Copied from testCcdbApi.cxx + */ +struct if_reachable { + boost::test_tools::assertion_result operator()(boost::unit_test::test_unit_id) + { + return hostReachable; + } +}; + +// Only compare known and stable keys (avoid volatile ones like Date) +static const std::set sStableKeys = { + "ETag", + "Valid-From", + "Valid-Until", + "Created", + "Last-Modified", + "Content-Disposition", + "Content-Location", + "path", + "partName", + "Content-MD5", + "Hello" // TODO find other headers to compare to +}; + +// Test that we get back the same header header keys as we put in (for stable keys) + +BOOST_AUTO_TEST_CASE(testCachedHeaders, *boost::unit_test::precondition(if_reachable())) +{ + /// ━━━━━━━ ARRANGE ━━━━━━━━━ + // First store objects to test with + auto& ccdbManager = o2::ccdb::BasicCCDBManager::instance(); + std::string pathA = basePath + "CachingA"; + std::string pathB = basePath + "CachingB"; + std::string pathC = basePath + "CachingC"; + std::string ccdbObjO = "testObjectO"; + std::string ccdbObjN = "testObjectN"; + std::string ccdbObjX = "testObjectX"; + std::map md = { + {"Hello", "World"}, + {"Key1", "Value1"}, + {"Key2", "Value2"}, + }; + long start = 1000, stop = 3000; + ccdbManager.getCCDBAccessor().storeAsTFileAny(&ccdbObjO, pathA, md, start, stop); + ccdbManager.getCCDBAccessor().storeAsTFileAny(&ccdbObjN, pathB, md, start, stop); + ccdbManager.getCCDBAccessor().storeAsTFileAny(&ccdbObjX, pathC, md, start, stop); + // initilize the BasicCCDBManager + ccdbManager.clearCache(); + ccdbManager.setCaching(true); // This is what we want to test. + + /// ━━━━━━━━━━━ ACT ━━━━━━━━━━━━ + // Plan: get one object, then another, then the first again and check the headers are the same + std::map headers1, headers2, headers3; + + auto* obj1 = ccdbManager.getForTimeStamp(pathA, (start + stop) / 2, &headers1); + auto* obj2 = ccdbManager.getForTimeStamp(pathB, (start + stop) / 2, &headers2); + auto* obj3 = ccdbManager.getForTimeStamp(pathA, (start + stop) / 2, &headers3); // Should lead to a cache hit! + + /// ━━━━━━━━━━━ ASSERT ━━━━━━━━━━━━ + /// Check that we got something + BOOST_REQUIRE(obj1 != nullptr); + BOOST_REQUIRE(obj2 != nullptr); + BOOST_REQUIRE(obj3 != nullptr); + + LOG(debug) << "obj1: " << *obj1; + LOG(debug) << "obj2: " << *obj2; + LOG(debug) << "obj3: " << *obj3; + + // Sanity check + /// Check that the objects are correct + BOOST_TEST(*obj1 == ccdbObjO); + BOOST_TEST(*obj3 == ccdbObjO); + BOOST_TEST(obj3 == obj1); // should be the same object in memory since it is cached + + BOOST_TEST(obj2 != obj1); + + (*obj1) = "ModifiedObject"; + BOOST_TEST(*obj1 == "ModifiedObject"); + BOOST_TEST(*obj3 == "ModifiedObject"); // obj3 and obj1 are the same object in memory + + // Check that the headers are the same for the two retrievals of the same object + BOOST_REQUIRE(headers1.size() != 0); + BOOST_REQUIRE(headers3.size() != 0); + + LOG(debug) << "Headers1 size: " << headers1.size(); + for (const auto& h : headers1) { + LOG(debug) << " " << h.first << " -> " << h.second; + } + LOG(debug) << "Headers3 size: " << headers3.size(); + for (const auto& h : headers3) { + LOG(debug) << " " << h.first << " -> " << h.second; + } + + for (const auto& stableKey : sStableKeys) { + LOG(info) << "Checking key: " << stableKey; + + BOOST_REQUIRE(headers1.count(stableKey) > 0); + BOOST_REQUIRE(headers3.count(stableKey) > 0); + BOOST_TEST(headers1.at(stableKey) == headers3.at(stableKey)); + } + BOOST_TEST(headers1 != headers2, "The headers for different objects should be different"); + + // Test that we can change the map and the two headers are not affected + headers1["NewKey"] = "NewValue"; + headers3["NewKey"] = "DifferentValue"; + BOOST_TEST(headers1["NewKey"] != headers3["NewKey"]); // This tests that we have a deep copy of the headers +} + +BOOST_AUTO_TEST_CASE(testNonCachedHeaders, *boost::unit_test::precondition(if_reachable())) +{ + /// ━━━━━━━ ARRANGE ━━━━━━━━━ + // First store objects to test with + auto& ccdbManager = o2::ccdb::BasicCCDBManager::instance(); + std::string pathA = basePath + "NonCachingA"; + std::string pathB = basePath + "NonCachingB"; + std::string ccdbObjO = "testObjectO"; + std::string ccdbObjN = "testObjectN"; + std::map md = { + {"Hello", "World"}, + {"Key1", "Value1"}, + {"Key2", "Value2"}, + }; + long start = 1000, stop = 2000; + ccdbManager.getCCDBAccessor().storeAsTFileAny(&ccdbObjO, pathA, md, start, stop); + ccdbManager.getCCDBAccessor().storeAsTFileAny(&ccdbObjN, pathB, md, start, stop); + // initilize the BasicCCDBManager + ccdbManager.clearCache(); + ccdbManager.setCaching(false); // This is what we want to test, no caching + + /// ━━━━━━━━━━━ ACT ━━━━━━━━━━━━ + // Plan: get one object, then another, then the first again. Then check that the contents is the same but not the object in memory + std::map headers1, headers2, headers3; + + auto* obj1 = ccdbManager.getForTimeStamp(pathA, (start + stop) / 2, &headers1); + auto* obj2 = ccdbManager.getForTimeStamp(pathB, (start + stop) / 2, &headers2); + auto* obj3 = ccdbManager.getForTimeStamp(pathA, (start + stop) / 2, &headers3); // Should not be cached since explicitly disabled + + ccdbManager.setCaching(true); // Restore default state + /// ━━━━━━━━━━━ ASSERT ━━━━━━━━━━━ + /// Check that we got something + BOOST_REQUIRE(obj1 != nullptr); + BOOST_REQUIRE(obj2 != nullptr); + BOOST_REQUIRE(obj3 != nullptr); + + LOG(debug) << "obj1: " << *obj1; + LOG(debug) << "obj2: " << *obj2; + LOG(debug) << "obj3: " << *obj3; + + // Sanity check + /// Check that the objects are correct + BOOST_TEST(*obj1 == ccdbObjO); + BOOST_TEST(*obj3 == ccdbObjO); + BOOST_TEST(obj2 != obj1); + BOOST_TEST(obj3 != obj1); // should NOT be the same object in memory + (*obj1) = "ModifiedObject"; + BOOST_TEST(*obj1 == "ModifiedObject"); + BOOST_TEST(*obj3 != "ModifiedObject"); // obj3 and obj1 are NOT the same object in memory + + BOOST_TEST(headers1.size() == headers3.size()); + + // Remove the date header since it may be different even for the same object since we might have asked in different seconds + headers1.erase("Date"); + headers3.erase("Date"); + BOOST_TEST(headers1 == headers3, "The headers for the same object should be the same even if not cached"); + + BOOST_TEST(headers1 != headers2, "The headers for different objects should be different"); + BOOST_TEST(headers1.size() != 0); + BOOST_TEST(headers3.size() != 0); + BOOST_TEST(headers2.size() != 0); + BOOST_TEST(headers1 != headers2, "The headers for different objects should be different"); + + // cleanup + delete obj1; + delete obj2; + delete obj3; +} + +BOOST_AUTO_TEST_CASE(CacheFirstRetrievalAndHeadersPersistence, *boost::unit_test::precondition(if_reachable())) +{ + /// ━━━━━━━ ARRANGE ━━━━━━━━━ + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + // Prepare two validity slots for same path to test ETag change later + std::string path = basePath + "ObjA"; + std::string objV1 = "ObjectVersion1"; + std::string objV2 = "ObjectVersion2"; + std::map meta1{ + {"UserKey1", "UValue1"}, + {"UserKey2", "UValue2"}}; + long v1start = 10'000; + long v1stop = 20'000; + long v2start = v1stop; // contiguous slot + long v2stop = v2start + (v1stop - v1start); + long mid1 = (v1start + v1stop) / 2; + // Store 2 versions + mgr.getCCDBAccessor().storeAsTFileAny(&objV1, path, meta1, v1start, v1stop); + mgr.getCCDBAccessor().storeAsTFileAny(&objV2, path, meta1, v2start, v2stop); + + mgr.clearCache(); + mgr.setCaching(true); + mgr.setFatalWhenNull(true); + mgr.setTimestamp(mid1); + + /// ━━━━━━━ACT━━━━━━━━━ + std::map headers1, headers2, headers4, headers5; + + // 1) First retrieval WITH headers inside 1st slot + auto* p1 = mgr.getForTimeStamp(path, mid1, &headers1); + size_t fetchedSizeAfterFirst = mgr.getFetchedSize(); + // 2) Second retrieval (cache hit) + auto* p2 = mgr.getForTimeStamp(path, mid1, &headers2); + size_t fetchedSizeAfterSecond = mgr.getFetchedSize(); + // 3) Third retrieval (cache hit) WITHOUT passing headers + auto* p3 = mgr.getForTimeStamp(path, mid1); + // 4) Fourth retrieval with headers again -> should still produce same headers + auto* p4 = mgr.getForTimeStamp(path, mid1, &headers4); + // 5) Fifth retrieval with headers again to check persistence + auto* p5 = mgr.getForTimeStamp(path, mid1, &headers5); + + mgr.setFatalWhenNull(false); // restore default + + /// ━━━━━━━ASSERT━━━━━━━━━ + + BOOST_TEST(p1 != nullptr); + BOOST_TEST(*p1 == objV1); + + BOOST_TEST(headers1.count("UserKey1") == 1); + BOOST_TEST(headers1.count("UserKey2") == 1); + BOOST_TEST(headers1["UserKey1"] == "UValue1"); + BOOST_TEST(headers1["UserKey2"] == "UValue2"); + BOOST_TEST(headers1.count("Valid-From") == 1); + BOOST_TEST(headers1.count("Valid-Until") == 1); + BOOST_TEST(headers1.count("ETag") == 1); + + /* Need to manually amend the headers1 to have cache valid until for comparison sake, + * the header is not set in the first request. + * It is only set if the internal cache of CCDB has seen this object before, apperently. + * This will never happen in this test since it was just created and not asked for before. + */ + headers1["Cache-Valid-Until"] = std::to_string(v1stop); + + /* In rare cases the header date might be different, if the second has ticked over between the requests + */ + headers1.erase("Date"); + headers2.erase("Date"); + headers4.erase("Date"); + headers5.erase("Date"); + + BOOST_TEST(p2 == p1); // same pointer for cached scenario + BOOST_TEST(headers2 == headers1); // identical header map + BOOST_TEST(fetchedSizeAfterSecond == fetchedSizeAfterFirst); // no new fetch + + BOOST_TEST(p3 == p1); + + BOOST_TEST(p4 == p1); + BOOST_TEST(headers4 == headers1); + + // Mutate the returned header map locally and ensure it does not corrupt internal cache + headers4["UserKey1"] = "Tampered"; + BOOST_TEST(p5 == p1); + BOOST_TEST(headers5["UserKey1"] == "UValue1"); // internal unchanged +} + +BOOST_AUTO_TEST_CASE(FailedFetchDoesNotGiveMetadata, *boost::unit_test::precondition(if_reachable())) +{ + + /// ━━━━━━━ ARRANGE ━━━━━━━━━ + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + std::string path = basePath + "FailThenRecover"; + std::string content = "ContentX"; + std::map meta{{"Alpha", "Beta"}}; + long s = 300'000, e = 310'000; + mgr.getCCDBAccessor().storeAsTFileAny(&content, path, meta, s, e); + mgr.clearCache(); + mgr.setCaching(true); + mgr.setFatalWhenNull(false); + + /// ━━━━━━━ ACT ━━━━━━━━━ + // Intentionally pick a timestamp outside validity to fail first + long badTS = s - 1000; + long goodTS = (s + e) / 2; + std::map hFail, hGood; + auto* badObj = mgr.getForTimeStamp(path, badTS, &hFail); + auto* goodObj = mgr.getForTimeStamp(path, goodTS, &hGood); + + /// ━━━━━━━ ASSERT ━━━━━━━━━ + BOOST_TEST(!hFail.empty()); // Should have some headers + BOOST_TEST(hFail["Alpha"] != "Beta"); // But not the metadata + BOOST_TEST(hGood.count("Alpha") == 1); + BOOST_TEST(hGood["Alpha"] == "Beta"); + + mgr.setFatalWhenNull(true); +} + +BOOST_AUTO_TEST_CASE(FirstCallWithoutHeadersThenWithHeaders, *boost::unit_test::precondition(if_reachable())) +{ + + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + std::string path = basePath + "LateHeaders"; + std::string body = "Late"; + std::map meta{{"LateKey", "LateVal"}}; + long s = 400'000, e = 410'000; + mgr.getCCDBAccessor().storeAsTFileAny(&body, path, meta, s, e); + + mgr.clearCache(); + mgr.setCaching(true); + long ts = (s + e) / 2; + + // 1) First call with nullptr headers + auto* first = mgr.getForTimeStamp(path, ts); + BOOST_TEST(first != nullptr); + BOOST_TEST(*first == body); + + // 2) Second call asking for headers - should return the full set + std::map h2; + auto* second = mgr.getForTimeStamp(path, ts, &h2); + BOOST_TEST(second == first); + BOOST_TEST(h2.count("LateKey") == 1); + BOOST_TEST(h2["LateKey"] == "LateVal"); + BOOST_TEST(h2.count("Valid-From") == 1); + BOOST_TEST(h2.count("Valid-Until") == 1); +} + +BOOST_AUTO_TEST_CASE(HeadersAreStableAcrossMultipleHits, *boost::unit_test::precondition(if_reachable())) +{ + + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + std::string path = basePath + "StableHeaders"; + std::string body = "Stable"; + std::map meta{{"HK", "HV"}}; + long s = 500'000, e = 510'000; + mgr.getCCDBAccessor().storeAsTFileAny(&body, path, meta, s, e); + + mgr.clearCache(); + mgr.setCaching(true); + long ts = (s + e) / 2; + + std::map h1; + auto* o1 = mgr.getForTimeStamp(path, ts, &h1); + BOOST_TEST(o1 != nullptr); + BOOST_TEST(h1.count("HK") == 1); + + std::string etag = h1["ETag"]; + for (int i = 0; i < 15; ++i) { + std::map hi; + auto* oi = mgr.getForTimeStamp(path, ts, &hi); + BOOST_TEST(oi == o1); + BOOST_TEST(hi.count("HK") == 1); + BOOST_TEST(hi["ETag"] == etag); + } +} diff --git a/CCDB/test/testCcdbApiMultipleUrls.cxx b/CCDB/test/testCcdbApiMultipleUrls.cxx index 331d0553c3aec..07ab0ddcb4dcf 100644 --- a/CCDB/test/testCcdbApiMultipleUrls.cxx +++ b/CCDB/test/testCcdbApiMultipleUrls.cxx @@ -24,8 +24,8 @@ using namespace o2::ccdb; namespace utf = boost::unit_test; namespace tt = boost::test_tools; -static string ccdbUrl; -static string basePath; +static std::string ccdbUrl; +static std::string basePath; bool hostReachable = false; /** @@ -40,14 +40,14 @@ struct Fixture { cout << "ccdb url: " << ccdbUrl << endl; hostReachable = api.isHostReachable(); cout << "Is host reachable ? --> " << hostReachable << endl; - basePath = string("Test/pid") + getpid() + "/"; + basePath = std::string("Test/pid") + getpid() + "/"; cout << "Path we will use in this test suite : " + basePath << endl; } ~Fixture() { if (hostReachable) { CcdbApi api; - map metadata; + std::map metadata; api.init(ccdbUrl); api.truncate(basePath + "*"); cout << "Test data truncated (" << basePath << ")" << endl; @@ -79,7 +79,7 @@ struct test_fixture { ~test_fixture() = default; CcdbApi api; - map metadata; + std::map metadata; }; BOOST_AUTO_TEST_CASE(storeAndRetrieve, *utf::precondition(if_reachable())) diff --git a/CCDB/test/testCcdbApi_ConfigParam.cxx b/CCDB/test/testCcdbApi_ConfigParam.cxx index 30892adf5194a..568669d05978f 100644 --- a/CCDB/test/testCcdbApi_ConfigParam.cxx +++ b/CCDB/test/testCcdbApi_ConfigParam.cxx @@ -17,6 +17,7 @@ #define BOOST_TEST_MODULE CCDB #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#define BOOST_BIND_GLOBAL_PLACEHOLDERS #include "CCDB/CcdbApi.h" #include "CommonUtils/ConfigurableParam.h" @@ -46,8 +47,8 @@ using namespace o2::ccdb; namespace utf = boost::unit_test; namespace tt = boost::test_tools; -static string ccdbUrl; -static string basePath; +static std::string ccdbUrl; +static std::string basePath; bool hostReachable = false; /** @@ -63,14 +64,14 @@ struct Fixture { hostReachable = api.isHostReachable(); char hostname[_POSIX_HOST_NAME_MAX]; gethostname(hostname, _POSIX_HOST_NAME_MAX); - basePath = string("Test/") + hostname + "/pid" + getpid() + "/"; + basePath = std::string("Test/") + hostname + "/pid" + getpid() + "/"; cout << "Path we will use in this test suite : " + basePath << endl; } ~Fixture() { if (hostReachable) { CcdbApi api; - map metadata; + std::map metadata; api.init(ccdbUrl); api.truncate(basePath + "*"); cout << "Test data truncated (" << basePath << ")" << endl; @@ -102,7 +103,7 @@ struct test_fixture { ~test_fixture() = default; CcdbApi api; - map metadata; + std::map metadata; }; BOOST_AUTO_TEST_CASE(testConfigParamRetrieval, *utf::precondition(if_reachable())) diff --git a/CCDB/test/testCcdbApi_alien.cxx b/CCDB/test/testCcdbApi_alien.cxx index f11f579346524..c50f83466fe06 100644 --- a/CCDB/test/testCcdbApi_alien.cxx +++ b/CCDB/test/testCcdbApi_alien.cxx @@ -29,7 +29,7 @@ using namespace o2::ccdb; namespace utf = boost::unit_test; namespace tt = boost::test_tools; -static string ccdbUrl; +static std::string ccdbUrl; bool hostReachable = false; /** @@ -71,7 +71,7 @@ struct test_fixture { ~test_fixture() = default; CcdbApi api; - map metadata; + std::map metadata; }; // handle the case where the object comes from alien and redirect does not work with curl diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000000..9d8753a362a56 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +This is the main repository of the ALICE Experiment Simulation, Reconstruction and Analysis Framework. + +The skills specific to developing code for this repository can be found in +the `.skills/` folder of this project. + diff --git a/CMakeLists.txt b/CMakeLists.txt index a5ff713f8d05b..adecffc0f4dbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ # Preamble -cmake_minimum_required(VERSION 3.19 FATAL_ERROR) +cmake_minimum_required(VERSION 3.27.1 FATAL_ERROR) # it's important to specify accurately the list of languages. for instance C and # C++ as we _do_ have some C files to compile explicitely as C (e.g. gl3w.c) @@ -25,6 +25,7 @@ include(CTest) set(ALIGPU_BUILD_TYPE "O2") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPER) set_property(GLOBAL PROPERTY REPORT_UNDEFINED_PROPERTIES) cmake_host_system_information(RESULT _totalmem QUERY TOTAL_PHYSICAL_MEMORY) @@ -37,14 +38,9 @@ set_property(GLOBAL PROPERTY JOB_POOLS analysis=${ANALYSIS_COMPILE_POOL}) include(O2BuildSanityChecks) o2_build_sanity_checks() -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -find_package(ONNXRuntime::ONNXRuntime CONFIG) -if (ONNXRuntime::ONNXRuntime_FOUND) - add_definitions(-DZDC_FASTSIM_ONNX) -endif() - include(O2CheckCXXFeatures) o2_check_cxx_features() @@ -90,9 +86,10 @@ include(O2AddTestRootMacro) include(O2ReportNonTestedMacros) include(O2TargetRootDictionary) include(O2DataFile) -include(O2TargetManPage) include(O2AddWorkflow) include(O2SetROOTPCMDependencies) +include(O2AddHipifiedExecutable) +include(O2AddHipifiedLibrary) # Main targets of the project in various subdirectories. Order matters. add_subdirectory(Common) @@ -119,10 +116,6 @@ endif() add_subdirectory(config) -add_custom_target(man ALL) -o2_target_man_page(man NAME o2) -o2_target_man_page(man NAME FairMQDevice) - # Testing and packaging only needed if we are the top level directory if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) # Documentation @@ -138,4 +131,3 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() set_root_pcm_dependencies() - diff --git a/CODEOWNERS b/CODEOWNERS index 1c286e12a48c8..e28661e28e0bf 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -18,7 +18,7 @@ /Common/Constants @shahor02 /Common/Field @shahor02 /Common/MathUtils @shahor02 -/Common/SimConfig @sawenzel @benedikt-voelkel +/Common/SimConfig @sawenzel #/Common/Topologies #/Common/Types /Common/Utils @sawenzel @@ -28,51 +28,54 @@ /DataFormats/Detectors/Common @shahor02 /DataFormats/Detectors/CPV @peressounko @kharlov /DataFormats/Detectors/CTP @lietava -/DataFormats/Detectors/EMCAL @mfasDa @jokonig -/DataFormats/Detectors/FIT @jotwinow @afurs @andreasmolander @arvindkhuntia @mslupeck +/DataFormats/Detectors/EMCAL @nstrangm @jokonig +/DataFormats/Detectors/FIT @jotwinow @afurs @andreasmolander @sahilupadhyaya92 +/DataFormats/Detectors/FOCAL @maxrauch @iarsene @matthiasrichter /DataFormats/Detectors/GlobalTracking @shahor02 /DataFormats/Detectors/GlobalTrackingWorkflow @shahor02 /DataFormats/Detectors/HMPID @gvolpe79 -/DataFormats/Detectors/ITSMFT @iouribelikov @robincaron13 @mconcas @shahor02 -/DataFormats/Detectors/MUON @AliceO2Group/muon-experts +/DataFormats/Detectors/ITSMFT @f3sch @fprino @mcoquet642 @shahor02 +/DataFormats/Detectors/MUON @AliceO2Group/muon-experts @shahor02 /DataFormats/Detectors/PHOS @peressounko @kharlov -/DataFormats/Detectors/Passive @sawenzel @benedikt-voelkel +/DataFormats/Detectors/Passive @sawenzel /DataFormats/Detectors/TOF @noferini /DataFormats/Detectors/TPC @davidrohr @wiechula @shahor02 -/DataFormats/Detectors/TRD @f3sch @bazinski @tdietel @martenole @pachmay -/DataFormats/Detectors/Upgrades @qgp @marcovanleeuwen @mconcas +/DataFormats/Detectors/TRD @f3sch @bazinski @wille10 +/DataFormats/Detectors/Upgrades @mconcas /DataFormats/Detectors/Upgrades/ITS3 @fgrosa @arossi81 -/DataFormats/Detectors/ZDC @coppedis +/DataFormats/Detectors/ZDC @coppedis @cortesep #/DataFormats/Headers #/DataFormats/Legacy #/DataFormats/MemoryResources /DataFormats/Parameters @shahor02 -/DataFormats/QualityControl @knopers8 @Barthelemy @chiarazampolli +/DataFormats/QualityControl @knopers8 @Barthelemy @justonedev1 @chiarazampolli /DataFormats/Reconstruction @shahor02 #/DataFormats/TimeFrame /DataFormats/common @shahor02 -/DataFormats/simulation @sawenzel @benedikt-voelkel +/DataFormats/simulation @sawenzel -/Detectors/Base @sawenzel @shahor02 @benedikt-voelkel +/Detectors/Base @sawenzel @shahor02 /Detectors/Calibration @chiarazampolli @shahor02 /Detectors/CPV @peressounko @kharlov -/Detectors/EMCAL @mfasDa @jokonig -/Detectors/FIT @jotwinow @afurs @andreasmolander @arvindkhuntia @mslupeck -/Detectors/Geometry @sawenzel @shahor02 @benedikt-voelkel +/Detectors/EMCAL @nstrangm @jokonig +/Detectors/FIT @jotwinow @afurs @andreasmolander @sahilupadhyaya92 +/Detectors/FOCAL @maxrauch @iarsene @matthiasrichter +/Detectors/Geometry @sawenzel @shahor02 /Detectors/GlobalTracking @shahor02 /Detectors/GlobalTrackingWorkflow @shahor02 /Detectors/HMPID @gvolpe79 -/Detectors/ITSMFT @iouribelikov @robincaron13 @mconcas @shahor02 -/Detectors/MUON @AliceO2Group/muon-experts +/Detectors/ITSMFT @f3sch @fprino @mcoquet642 @mconcas @shahor02 +/Detectors/MUON @AliceO2Group/muon-experts @shahor02 /Detectors/PHOS @peressounko @kharlov -/Detectors/Passive @sawenzel @benedikt-voelkel +/Detectors/Passive @sawenzel /Detectors/TOF @noferini /Detectors/TPC @davidrohr @wiechula @shahor02 -/Detectors/TRD @f3sch @bazinski @tdietel @martenole @pachmay -/Detectors/Upgrades @qgp @mconcas -/Detectors/Upgrades/ITS3 @fgrosa @arossi81 @mconcas -/Detectors/ZDC @coppedis +/Detectors/TRD @f3sch @bazinski @wille10 +/Detectors/Upgrades @mconcas +/Detectors/Upgrades/ALICE3 @mconcas @njacazio @fcolamar @pbutti +/Detectors/Upgrades/ITS3 @fgrosa @arossi81 @mconcas @f3sch +/Detectors/ZDC @coppedis @cortesep /Detectors/CTF @shahor02 /Detectors/Raw @shahor02 /Detectors/StrangenessTracking @mconcas @mpuccio @fmazzasc @@ -108,19 +111,19 @@ /GPU @davidrohr #/GPU/Common #/GPU/GPUTracking -/GPU/GPUTracking/TRDTracking @davidrohr @martenole +/GPU/GPUTracking/TRDTracking @davidrohr /GPU/TPCFastTransformation @davidrohr @sgorbuno /GPU/TPCSpaceChargeBase @davidrohr @ehellbar -/Generators @sawenzel @benedikt-voelkel +/Generators @sawenzel @jackal1-66 -/Steer @sawenzel @shahor02 @benedikt-voelkel +/Steer @sawenzel @shahor02 /Testing /Utilities @AliceO2Group/framework-admins -/Utilities/Mergers @Barthelemy @knopers8 -/Utilities/DataSampling @Barthelemy @knopers8 +/Utilities/Mergers @Barthelemy @knopers8 @justonedev1 +/Utilities/DataSampling @Barthelemy @knopers8 @justonedev1 #/Utilities/DataCompression #/Utilities/DataFlow #/Utilities/MCStepLogger diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt index f435e269575aa..0b92758e45f43 100644 --- a/Common/CMakeLists.txt +++ b/Common/CMakeLists.txt @@ -16,5 +16,6 @@ add_subdirectory(Types) add_subdirectory(Utils) add_subdirectory(SimConfig) add_subdirectory(DCAFitter) +add_subdirectory(ML) o2_data_file(COPY maps DESTINATION Common) diff --git a/Common/Constants/include/CommonConstants/MathConstants.h b/Common/Constants/include/CommonConstants/MathConstants.h index 03368bb6f8bf9..9ef3b4dba5ae0 100644 --- a/Common/Constants/include/CommonConstants/MathConstants.h +++ b/Common/Constants/include/CommonConstants/MathConstants.h @@ -22,8 +22,9 @@ namespace constants { namespace math { -constexpr float Almost0 = 1.17549e-38; -constexpr float Almost1 = 1.f - Almost0; +constexpr float Almost0 = 0x1.0p-126f; // smallest non-denormal float +constexpr float Epsilon = 0x0.000002p0f; // smallest float such that 1 != 1 + Epsilon +constexpr float Almost1 = 1.f - 1.0e-6f; constexpr float VeryBig = 1.f / Almost0; constexpr float PI = 3.14159274101257324e+00f; diff --git a/Common/Constants/include/CommonConstants/PhysicsConstants.h b/Common/Constants/include/CommonConstants/PhysicsConstants.h index d3342f1df6f64..051fb6d2a6e89 100644 --- a/Common/Constants/include/CommonConstants/PhysicsConstants.h +++ b/Common/Constants/include/CommonConstants/PhysicsConstants.h @@ -12,39 +12,244 @@ /// \file PhysicsConstants.h /// \brief Header to collect physics constants /// \author ruben.shahoyan@cern.ch +/// \author Vít Kučera , Inha University +/// \note Use the make_pdg_header.py script to generate the enums and mass declarations. #ifndef ALICEO2_PHYSICSCONSTANTS_H_ #define ALICEO2_PHYSICSCONSTANTS_H_ -namespace o2 -{ -namespace constants -{ -namespace physics +namespace o2::constants::physics { // particles masses -constexpr float MassPhoton = 0.0; -constexpr float MassElectron = 0.000511; -constexpr float MassMuon = 0.105658; -constexpr float MassPionCharged = 0.139570; -constexpr float MassPionNeutral = 0.134976; -constexpr float MassKaonCharged = 0.493677; -constexpr float MassKaonNeutral = 0.497648; -constexpr float MassProton = 0.938272; -constexpr float MassLambda = 1.115683; -constexpr float MassDeuteron = 1.8756129; -constexpr float MassTriton = 2.8089211; -constexpr float MassHelium3 = 2.8083916; -constexpr float MassAlpha = 3.7273794; -constexpr float MassHyperTriton = 2.992; -constexpr float MassHyperhydrog4 = 3.931; -constexpr float MassXiMinus = 1.32171; -constexpr float MassOmegaMinus = 1.67245; - -constexpr float LightSpeedCm2S = 299792458.e2; // C in cm/s -constexpr float LightSpeedCm2NS = LightSpeedCm2S * 1e-9; // C in cm/ns -} // namespace physics -} // namespace constants -} // namespace o2 + +// BEGINNING OF THE GENERATED BLOCK. +// DO NOT EDIT THIS BLOCK DIRECTLY! +// It has been generated by the make_pdg_header.py script. +// For modifications, edit the script and generate this block again. + +/// \brief Declarations of named PDG codes of particles missing in ROOT PDG_t +/// \note Follow kCamelCase naming convention +/// \link https://root.cern/doc/master/TPDGCode_8h.html +enum Pdg { + kEta = 221, + kOmega = 223, + kEtaPrime = 331, + kB0 = 511, + kB0Bar = -511, + kBPlus = 521, + kBCPlus = 541, + kBS = 531, + kBSBar = -531, + kD0 = 421, + kD0Bar = -421, + kD0StarPlus = 10411, + kD0Star0 = 10421, + kD1Plus = 20413, + kD10 = 20423, + kD2StarPlus = 415, + kD2Star0 = 425, + kDMinus = -411, + kDPlus = 411, + kDS = 431, + kDSBar = -431, + kDSStar = 433, + kDS1 = 10433, + kDS1Star2700 = 30433, + kDS1Star2860 = 40433, + kDS2Star = 435, + kDS3Star2860 = 437, + kDStar = 413, + kDStar0 = 423, + kChiC1 = 20443, + kJPsi = 443, + kLambdaB0 = 5122, + kLambdaCPlus = 4122, + kLambdaCPlus2860 = 24124, + kLambdaCPlus2880 = 24126, + kLambdaCPlus2940 = 4125, + kOmegaC0 = 4332, + kK0Star892 = 313, + kKPlusStar892 = 323, + kPhi = 333, + kSigmaC0 = 4112, + kSigmaCPlusPlus = 4222, + kSigmaCStar0 = 4114, + kSigmaCStarPlusPlus = 4224, + kX3872 = 9920443, + kXi0 = 3322, + kXiB0 = 5232, + kXiCCPlusPlus = 4422, + kXiCPlus = 4232, + kXiC0 = 4132, + kXiC3055Plus = 4325, + kXiC3080Plus = 4326, + kXiC3055_0 = 4315, + kXiC3080_0 = 4316, + kDeuteron = 1000010020, + kTriton = 1000010030, + kHelium3 = 1000020030, + kAlpha = 1000020040, + kLithium4 = 1000030040, + kHyperTriton = 1010010030, + kHyperHydrogen4 = 1010010040, + kHyperHelium4 = 1010020040, + kHyperHelium5 = 1010020050, + kHyperHelium4Sigma = 1110020040, + kLambda1520_Py = 102134, + kK1_1270_0 = 10313, + kK1_1270Plus = 10323, + kCDeuteron = 2010010020 +}; + +/// \brief Declarations of masses for additional particles +constexpr double MassEta = 0.547862; +constexpr double MassOmega = 0.78266; +constexpr double MassEtaPrime = 0.95778; +constexpr double MassB0 = 5.27966; +constexpr double MassB0Bar = 5.27966; +constexpr double MassBPlus = 5.27934; +constexpr double MassBCPlus = 6.27447; +constexpr double MassBS = 5.36692; +constexpr double MassBSBar = 5.36692; +constexpr double MassD0 = 1.86484; +constexpr double MassD0Bar = 1.86484; +constexpr double MassD0StarPlus = 2.272; +constexpr double MassD0Star0 = 2.343; +constexpr double MassD1Plus = 2.372; +constexpr double MassD10 = 2.412; +constexpr double MassD2StarPlus = 2.4601; +constexpr double MassD2Star0 = 2.4611; +constexpr double MassDMinus = 1.86966; +constexpr double MassDPlus = 1.86966; +constexpr double MassDS = 1.96835; +constexpr double MassDSBar = 1.96835; +constexpr double MassDSStar = 2.1122; +constexpr double MassDS1 = 2.53511; +constexpr double MassDS1Star2700 = 2.714; +constexpr double MassDS1Star2860 = 2.859; +constexpr double MassDS2Star = 2.5691; +constexpr double MassDS3Star2860 = 2.86; +constexpr double MassDStar = 2.01026; +constexpr double MassDStar0 = 2.00685; +constexpr double MassChiC1 = 3.51067; +constexpr double MassJPsi = 3.0969; +constexpr double MassLambdaB0 = 5.6196; +constexpr double MassLambdaCPlus = 2.28646; +constexpr double MassLambdaCPlus2860 = 2.8561; +constexpr double MassLambdaCPlus2880 = 2.8816; +constexpr double MassLambdaCPlus2940 = 2.9396; +constexpr double MassOmegaC0 = 2.6952; +constexpr double MassK0Star892 = 0.89555; +constexpr double MassKPlusStar892 = 0.89167; +constexpr double MassPhi = 1.019461; +constexpr double MassSigmaC0 = 2.45375; +constexpr double MassSigmaCPlusPlus = 2.45397; +constexpr double MassSigmaCStar0 = 2.51848; +constexpr double MassSigmaCStarPlusPlus = 2.51841; +constexpr double MassX3872 = 3.87165; +constexpr double MassXi0 = 1.31486; +constexpr double MassXiB0 = 5.7919; +constexpr double MassXiCCPlusPlus = 3.62155; +constexpr double MassXiCPlus = 2.46771; +constexpr double MassXiC0 = 2.47044; +constexpr double MassXiC3055Plus = 3.0559; +constexpr double MassXiC3080Plus = 3.0772; +constexpr double MassXiC3055_0 = 3.059; +constexpr double MassXiC3080_0 = 3.0799; +constexpr double MassDeuteron = 1.87561294257; +constexpr double MassTriton = 2.80892113298; +constexpr double MassHelium3 = 2.80839160743; +constexpr double MassAlpha = 3.7273794066; +constexpr double MassLithium4 = 3.7513; +constexpr double MassHyperTriton = 2.991134; +constexpr double MassHyperHydrogen4 = 3.922434; +constexpr double MassHyperHelium4 = 3.921728; +constexpr double MassHyperHelium5 = 4.839961; +constexpr double MassHyperHelium4Sigma = 3.995; +constexpr double MassLambda1520_Py = 1.5195; +constexpr double MassK1_1270_0 = 1.253; +constexpr double MassK1_1270Plus = 1.272; +constexpr double MassCDeuteron = 3.226; + +/// \brief Declarations of masses for particles in ROOT PDG_t +constexpr double MassDown = 0.00467; +constexpr double MassDownBar = 0.00467; +constexpr double MassUp = 0.00216; +constexpr double MassUpBar = 0.00216; +constexpr double MassStrange = 0.0934; +constexpr double MassStrangeBar = 0.0934; +constexpr double MassCharm = 1.27; +constexpr double MassCharmBar = 1.27; +constexpr double MassBottom = 4.18; +constexpr double MassBottomBar = 4.18; +constexpr double MassTop = 172.5; +constexpr double MassTopBar = 172.5; +constexpr double MassGluon = 0.0; +constexpr double MassElectron = 0.000510999; +constexpr double MassPositron = 0.000510999; +constexpr double MassNuE = 0.0; +constexpr double MassNuEBar = 0.0; +constexpr double MassMuonMinus = 0.1056584; +constexpr double MassMuonPlus = 0.1056584; +constexpr double MassNuMu = 0.0; +constexpr double MassNuMuBar = 0.0; +constexpr double MassTauMinus = 1.77686; +constexpr double MassTauPlus = 1.77686; +constexpr double MassNuTau = 0.0; +constexpr double MassNuTauBar = 0.0; +constexpr double MassGamma = 0.0; +constexpr double MassZ0 = 91.1876; +constexpr double MassWPlus = 80.377; +constexpr double MassWMinus = 80.377; +constexpr double MassPi0 = 0.1349768; +constexpr double MassK0Long = 0.497611; +constexpr double MassPiPlus = 0.1395704; +constexpr double MassPiMinus = 0.1395704; +constexpr double MassProton = 0.9382721; +constexpr double MassProtonBar = 0.9382721; +constexpr double MassNeutron = 0.9395654; +constexpr double MassNeutronBar = 0.9395654; +constexpr double MassK0Short = 0.497611; +constexpr double MassK0 = 0.497611; +constexpr double MassK0Bar = 0.497611; +constexpr double MassKPlus = 0.493677; +constexpr double MassKMinus = 0.493677; +constexpr double MassLambda0 = 1.115683; +constexpr double MassLambda0Bar = 1.115683; +constexpr double MassLambda1520 = 1.519; +constexpr double MassSigmaMinus = 1.197449; +constexpr double MassSigmaBarPlus = 1.197449; +constexpr double MassSigmaPlus = 1.18937; +constexpr double MassSigmaBarMinus = 1.18937; +constexpr double MassSigma0 = 1.192642; +constexpr double MassSigma0Bar = 1.192642; +constexpr double MassXiMinus = 1.32171; +constexpr double MassXiPlusBar = 1.32171; +constexpr double MassOmegaMinus = 1.67245; +constexpr double MassOmegaPlusBar = 1.67245; + +// END OF THE GENERATED BLOCK + +// legacy names +constexpr double MassPhoton = MassGamma; +constexpr double MassMuon = MassMuonMinus; +constexpr double MassPionCharged = MassPiPlus; +constexpr double MassPionNeutral = MassPi0; +constexpr double MassKaonCharged = MassKPlus; +constexpr double MassKaonNeutral = MassK0; +constexpr double MassLambda = MassLambda0; +constexpr double MassHyperhydrog4 = MassHyperHydrogen4; +constexpr double MassHyperhelium4 = MassHyperHelium4; +constexpr double MassHyperhelium4sigma = MassHyperHelium4Sigma; + +// Light speed +constexpr float LightSpeedCm2S = 299792458.e2; // C in cm/s +constexpr float LightSpeedCm2NS = LightSpeedCm2S * 1e-9; // C in cm/ns +constexpr float LightSpeedCm2PS = LightSpeedCm2S * 1e-12; // C in cm/ps + +// Light speed inverse +constexpr float invLightSpeedCm2PS = 1. / LightSpeedCm2PS; // 1/C in ps/cm + +} // namespace o2::constants::physics #endif diff --git a/Common/Constants/include/CommonConstants/ZDCConstants.h b/Common/Constants/include/CommonConstants/ZDCConstants.h new file mode 100644 index 0000000000000..a07de17abff65 --- /dev/null +++ b/Common/Constants/include/CommonConstants/ZDCConstants.h @@ -0,0 +1,63 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ZDCConstants.h +/// @brief Some ZDC constants shared between O2 and O2Physics + +/// @author pietro.cortese@cern.ch + +#ifndef ALICEO2_ZDCCONSTANTS_H_ +#define ALICEO2_ZDCCONSTANTS_H_ + +namespace o2 +{ +namespace zdc +{ + +//< map detector/tower to continuous channel Id +constexpr int IdDummy = -1; +constexpr int IdVoid = -2; + +constexpr int IdZNAC = 0; +constexpr int IdZNA1 = 1; +constexpr int IdZNA2 = 2; +constexpr int IdZNA3 = 3; +constexpr int IdZNA4 = 4; +constexpr int IdZNASum = 5; +// +constexpr int IdZPAC = 6; +constexpr int IdZPA1 = 7; +constexpr int IdZPA2 = 8; +constexpr int IdZPA3 = 9; +constexpr int IdZPA4 = 10; +constexpr int IdZPASum = 11; +// +constexpr int IdZEM1 = 12; +constexpr int IdZEM2 = 13; +// +constexpr int IdZNCC = 14; +constexpr int IdZNC1 = 15; +constexpr int IdZNC2 = 16; +constexpr int IdZNC3 = 17; +constexpr int IdZNC4 = 18; +constexpr int IdZNCSum = 19; +// +constexpr int IdZPCC = 20; +constexpr int IdZPC1 = 21; +constexpr int IdZPC2 = 22; +constexpr int IdZPC3 = 23; +constexpr int IdZPC4 = 24; +constexpr int IdZPCSum = 25; + +} // namespace zdc +} // namespace o2 + +#endif diff --git a/Common/Constants/include/CommonConstants/make_pdg_header.py b/Common/Constants/include/CommonConstants/make_pdg_header.py new file mode 100755 index 0000000000000..b2dac688fd098 --- /dev/null +++ b/Common/Constants/include/CommonConstants/make_pdg_header.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +"""! +@brief Generates and updates the body of a C++ header with PDG codes and particle masses. +@author Vít Kučera , Inha University +@date 2023-09-21 +""" + +import os +from ctypes import c_bool +from enum import Enum + +try: + import ROOT # pylint: disable=import-error + from ROOT import o2 +except (ModuleNotFoundError, ImportError) as exc: + raise OSError("O2 environment is not loaded.") from exc + + +# Enum of PDG_t particles +class PdgROOT(Enum): + kDown = ROOT.kDown + kDownBar = ROOT.kDownBar + kUp = ROOT.kUp + kUpBar = ROOT.kUpBar + kStrange = ROOT.kStrange + kStrangeBar = ROOT.kStrangeBar + kCharm = ROOT.kCharm + kCharmBar = ROOT.kCharmBar + kBottom = ROOT.kBottom + kBottomBar = ROOT.kBottomBar + kTop = ROOT.kTop + kTopBar = ROOT.kTopBar + kGluon = ROOT.kGluon + kElectron = ROOT.kElectron + kPositron = ROOT.kPositron + kNuE = ROOT.kNuE + kNuEBar = ROOT.kNuEBar + kMuonMinus = ROOT.kMuonMinus + kMuonPlus = ROOT.kMuonPlus + kNuMu = ROOT.kNuMu + kNuMuBar = ROOT.kNuMuBar + kTauMinus = ROOT.kTauMinus + kTauPlus = ROOT.kTauPlus + kNuTau = ROOT.kNuTau + kNuTauBar = ROOT.kNuTauBar + kGamma = ROOT.kGamma + kZ0 = ROOT.kZ0 + kWPlus = ROOT.kWPlus + kWMinus = ROOT.kWMinus + kPi0 = ROOT.kPi0 + kK0Long = ROOT.kK0Long + kPiPlus = ROOT.kPiPlus + kPiMinus = ROOT.kPiMinus + kProton = ROOT.kProton + kProtonBar = ROOT.kProtonBar + kNeutron = ROOT.kNeutron + kNeutronBar = ROOT.kNeutronBar + kK0Short = ROOT.kK0Short + kK0 = ROOT.kK0 + kK0Bar = ROOT.kK0Bar + kKPlus = ROOT.kKPlus + kKMinus = ROOT.kKMinus + kLambda0 = ROOT.kLambda0 + kLambda0Bar = ROOT.kLambda0Bar + kLambda1520 = ROOT.kLambda1520 + kSigmaMinus = ROOT.kSigmaMinus + kSigmaBarPlus = ROOT.kSigmaBarPlus + kSigmaPlus = ROOT.kSigmaPlus + kSigmaBarMinus = ROOT.kSigmaBarMinus + kSigma0 = ROOT.kSigma0 + kSigma0Bar = ROOT.kSigma0Bar + kXiMinus = ROOT.kXiMinus + kXiPlusBar = ROOT.kXiPlusBar + kOmegaMinus = ROOT.kOmegaMinus + kOmegaPlusBar = ROOT.kOmegaPlusBar + + +# Enum of additional particles +class Pdg(Enum): + kEta = 221 + kOmega = 223 + kEtaPrime = 331 + kB0 = 511 + kB0Bar = -511 + kBPlus = 521 + kBCPlus = 541 + kBS = 531 + kBSBar = -531 + kD0 = 421 + kD0Bar = -421 + kD0StarPlus = 10411 + kD0Star0 = 10421 + kD1Plus = 20413 + kD10 = 20423 + kD2StarPlus = 415 + kD2Star0 = 425 + kDMinus = -411 + kDPlus = 411 + kDS = 431 + kDSBar = -431 + kDSStar = 433 + kDS1 = 10433 + kDS1Star2700 = 30433 + kDS1Star2860 = 40433 + kDS2Star = 435 + kDS3Star2860 = 437 + kDStar = 413 + kDStar0 = 423 + kChiC1 = 20443 + kJPsi = 443 + kLambdaB0 = 5122 + kLambdaCPlus = 4122 + kLambdaCPlus2860 = 24124 + kLambdaCPlus2880 = 24126 + kLambdaCPlus2940 = 4125 + kOmegaC0 = 4332 + kK0Star892 = 313 + kKPlusStar892 = 323 + kPhi = 333 + kSigmaC0 = 4112 + kSigmaCPlusPlus = 4222 + kSigmaCStar0 = 4114 + kSigmaCStarPlusPlus = 4224 + kX3872 = 9920443 + kXi0 = 3322 + kXiB0 = 5232 + kXiCCPlusPlus = 4422 + kXiCPlus = 4232 + kXiC0 = 4132 + kXiC3055Plus = 4325 + kXiC3080Plus = 4326 + kXiC3055_0 = 4315 + kXiC3080_0 = 4316 + kDeuteron = 1000010020 + kTriton = 1000010030 + kHelium3 = 1000020030 + kAlpha = 1000020040 + kLithium4 = 1000030040 + kHyperTriton = 1010010030 + kHyperHydrogen4 = 1010010040 + kHyperHelium4 = 1010020040 + kHyperHelium5 = 1010020050 + kHyperHelium4Sigma = 1110020040 + kLambda1520_Py = 102134 # PYTHIA code different from PDG + kK1_1270_0 = 10313 + kK1_1270Plus = 10323 + kCDeuteron = 2010010020 + + +dbPdg = o2.O2DatabasePDG + + +def mass(code): + """Returns particle mass from o2::O2DatabasePDG.""" + # Missing particles should be added in O2DatabasePDG.h. + success = c_bool(True) + return dbPdg.Mass(code, success) + + +def declare_mass(pdg, mass_type="double") -> str: + """Returns a C++ declaration of a particle mass constant.""" + return f"constexpr {mass_type} Mass{pdg.name[1:]} = {mass(pdg.value)};" + + +def main(): + """Main function""" + + path_header = "PhysicsConstants.h" + name_script = os.path.basename(__file__) + + # Comment at the beginning of the output + block_begin = "// BEGINNING OF THE GENERATED BLOCK." + # Comment at the end of the output + block_end = "// END OF THE GENERATED BLOCK" + # Preamble with instructions + block_preamble = ( + "// DO NOT EDIT THIS BLOCK DIRECTLY!" + f"\n// It has been generated by the {name_script} script." + "\n// For modifications, edit the script and generate this block again." + ) + # Start of enum declarations of additional particles + enum_o2_head = ( + "/// \\brief Declarations of named PDG codes of particles missing in ROOT PDG_t" + "\n/// \\note Follow kCamelCase naming convention" + "\n/// \\link https://root.cern/doc/master/TPDGCode_8h.html" + "\nenum Pdg {" + ) + # End of enum declarations of additional particles + enum_o2_foot = "};" + # Documentation string for mass declarations of additional particles + mass_o2_head = "/// \\brief Declarations of masses for additional particles" + # Documentation string for mass declarations of PDG_t particles + mass_root_head = "/// \\brief Declarations of masses for particles in ROOT PDG_t" + + # Get header content before and after the generated block. + print(f'File "{path_header}" will be updated.') + try: + with open(path_header, encoding="utf-8") as file: + content_old = file.readlines() + except OSError as exc: + raise OSError(f'Failed to open file "{path_header}".') from exc + lines_header_before: list[str] = [] + lines_header_after: list[str] = [] + got_block_begin = False + got_block_end = False + for line in content_old: + line = line.strip() + if line == block_begin: + got_block_begin = True + if not got_block_begin: + lines_header_before.append(line) + if got_block_end: + lines_header_after.append(line) + if line == block_end: + got_block_end = True + if not got_block_begin: + raise ValueError("Did not find the beginning of the block.") + if not got_block_end: + raise ValueError("Did not find the end of the block.") + + # Additional particles + lines_enum_o2: list[str] = [enum_o2_head] + lines_mass_o2: list[str] = [mass_o2_head] + for pdg_o2 in Pdg: + lines_enum_o2.append(f" {pdg_o2.name} = {pdg_o2.value},") + lines_mass_o2.append(declare_mass(pdg_o2)) + lines_enum_o2[-1] = lines_enum_o2[-1][:-1] # Remove the last comma. + lines_enum_o2.append(enum_o2_foot) + + # PDG_t particles + lines_mass_root: list[str] = [mass_root_head] + for pdg_root in PdgROOT: + lines_mass_root.append(declare_mass(pdg_root)) + + # Header body + content_new = "\n".join( + ( + *lines_header_before, + block_begin, + block_preamble, + "", + *lines_enum_o2, + "", + *lines_mass_o2, + "", + *lines_mass_root, + "", + block_end, + *lines_header_after, + "", + ) + ) + # print(content_new) + + # Overwrite the input file. + try: + with open(path_header, "w", encoding="utf-8") as file: + file.write(content_new) + print(f'File "{path_header}" has been overwritten.') + except OSError as exc: + raise OSError(f'Failed to write to file "{path_header}".') from exc + + +if __name__ == "__main__": + main() diff --git a/Common/DCAFitter/CMakeLists.txt b/Common/DCAFitter/CMakeLists.txt index e15c2885f2259..c0b2d0dca1026 100644 --- a/Common/DCAFitter/CMakeLists.txt +++ b/Common/DCAFitter/CMakeLists.txt @@ -9,6 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# add_compile_options(-O0 -g -fPIC) + o2_add_library(DCAFitter TARGETVARNAME targetName SOURCES src/DCAFitterN.cxx @@ -20,8 +22,7 @@ o2_add_library(DCAFitter O2::DetectorsBase) o2_target_root_dictionary(DCAFitter - HEADERS include/DCAFitter/HelixHelper.h - include/DCAFitter/DCAFitterN.h + HEADERS include/DCAFitter/DCAFitterN.h include/DCAFitter/FwdDCAFitterN.h) if (OpenMP_CXX_FOUND) @@ -38,3 +39,5 @@ o2_add_test( LABELS vertexing ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/${CMAKE_INSTALL_DATADIR}) + +add_subdirectory(GPU) diff --git a/Common/DCAFitter/DCAFitterN_derivation.md b/Common/DCAFitter/DCAFitterN_derivation.md new file mode 100644 index 0000000000000..eb1c7e36c1fe2 --- /dev/null +++ b/Common/DCAFitter/DCAFitterN_derivation.md @@ -0,0 +1,552 @@ +# DCAFitterN derivation notes + +This file combines the extracted content of +`~/Downloads/DCAFitterN_derivation_for_codex.md` with the O2-specific covariance +updates made in `include/DCAFitter/DCAFitterN.h`. + +## 1. Convention check from the extracted scan + +The extracted scan starts with a matrix named `M_i` and states that it maps local +track coordinates to global coordinates: + +```text +p_i^g = M_i p_i +``` + +but the printed 2D block + +```text +[ cos(alpha_i) sin(alpha_i) ] +[ -sin(alpha_i) cos(alpha_i) ] +``` + +is the inverse of the O2 local-to-global rotation. O2 uses + +```text +R_i = +[ cos(alpha_i) -sin(alpha_i) 0 ] +[ sin(alpha_i) cos(alpha_i) 0 ] +[ 0 0 1 ] +``` + +with + +```text +p_i^g = R_i p_i, p_i = R_i^T p_i^g . +``` + +The later extracted formulas note this possible sign reversal. The code in +`DCAFitterN.h` matches the O2 convention above. In the rest of this document +`R_i` is always the O2 local-to-global rotation. + +## 2. Weighted N-prong vertex fit + +For track `i`, let the current point in its local frame be + +```text +p_i = (x_i, y_i, z_i)^T . +``` + +Let `B_i` be the inverse covariance, or information matrix, for this local +point. The fitted vertex `V` is in the global frame. Its representation in +track `i`'s local frame is `R_i^T V`, so the residual is + +```text +Delta_i = p_i - R_i^T V . +``` + +The weighted objective is + +```text +chi2 = 1/2 sum_i Delta_i^T B_i Delta_i . +``` + +For fixed track points, differentiating with respect to `V` gives + +```text +A V = sum_i R_i B_i p_i +``` + +where + +```text +A = sum_i R_i B_i R_i^T . +``` + +Therefore + +```text +V = A^{-1} sum_i R_i B_i p_i . +``` + +Define + +```text +T_i = A^{-1} R_i B_i . +``` + +Then + +```text +V = sum_i T_i p_i . +``` + +This is the `calcInverseWeight`, `calcPCACoefs`, and `calcPCA` structure in the +code. The useful identity is + +```text +sum_i T_i R_i^T = I . +``` + +## 3. Residuals after eliminating the vertex + +Substituting `V = sum_k T_k p_k` into the residual gives + +```text +Delta_i = sum_k D_ik p_k +``` + +with + +```text +D_ik = delta_ik I - R_i^T T_k . +``` + +The fit parameters are the local running coordinates `x_k`. During one Newton +linearization, `R_i`, `B_i`, `A`, and `T_i` are treated as fixed. Each track +point depends only on its own parameter: + +```text +d p_i / d x_k = delta_ik p_i' +``` + +so + +```text +d Delta_i / d x_k = D_ik p_k' +``` + +and + +```text +d^2 Delta_i / d x_k d x_l = delta_kl D_ik p_k'' . +``` + +## 4. Track derivatives in the O2 local frame + +O2 central-barrel parameters are + +```text +(Y, Z, snp, tgl, q/pt) +``` + +where + +```text +snp = sin(phi_local), csp = sqrt(1 - snp^2), +tgl = tan(lambda), kappa = curvature = (q/pt) Bz B2C . +``` + +For the fast constant-`Bz` helix model: + +```text +d snp / dX = kappa +dY / dX = snp / csp +dZ / dX = tgl / csp +``` + +and + +```text +d2Y / dX2 = kappa / csp^3 +d2Z / dX2 = kappa tgl snp / csp^3 . +``` + +Thus + +```text +p_i' = (1, dY/dX, dZ/dX)^T +p_i'' = (0, d2Y/dX2, d2Z/dX2)^T . +``` + +This matches `TrackDeriv::set`. + +## 5. Gradient and Hessian + +With symmetric `B_i`, + +```text +g_k = d chi2 / d x_k + = sum_i Delta_i^T B_i D_ik p_k' . +``` + +The exact Hessian is + +```text +H_kl = + sum_i (D_il p_l')^T B_i (D_ik p_k') + + delta_kl sum_i Delta_i^T B_i D_ik p_k'' . +``` + +The first term is the Gauss-Newton term. It contributes to diagonal and mixed +Hessian elements. The second term is the residual-curvature term. Since each +trajectory has an intrinsic second derivative only with respect to its own +running coordinate, this term contributes only when `k == l`. This is the +reason for the code condition + +```cpp +if (i == j) { + ... +} +``` + +when computing the Hessian element `H_ij`. + +The implementation solves + +```text +H dX = g +``` + +and then applies + +```text +X_new = X_old - dX . +``` + +This is equivalent to the more usual Newton notation `deltaX = -H^{-1} g`. + +## 6. No-error special case + +For the absolute-distance fit, take all information matrices as identity: + +```text +B_i = I . +``` + +Then + +```text +A = N I, A^{-1} = (1/N) I, +T_i = (1/N) R_i . +``` + +The vertex is the average of global track points: + +```text +V = (1/N) sum_i R_i p_i . +``` + +The residual is + +```text +Delta_i = p_i - (1/N) sum_j R_i^T R_j p_j . +``` + +Define + +```text +R_ij = (1/N) R_i^T R_j . +``` + +With the O2 convention, + +```text +R_i^T R_j = +[ cos(ai-aj) sin(ai-aj) 0 ] +[ -sin(ai-aj) cos(ai-aj) 0 ] +[ 0 0 1 ] . +``` + +This matches the code definitions + +```cpp +mCosDif[i][j] = (ci*cj + si*sj) / N; +mSinDif[i][j] = (si*cj - ci*sj) / N; +``` + +and the residual derivative components in `calcResidDerivativesNoErr`. + +## 7. Track information matrix involving the local X axis + +The old DCAFitterN code assigned a dummy uncertainty to local `X`, derived from +the local `Y` uncertainty. The corrected treatment derives longitudinal vertex +information from the track geometry. + +At fixed local `X`, the track measures `(Y,Z)` with covariance + +```text +C_YZ = +[ C_YY C_YZ ] +[ C_YZ C_ZZ ] . +``` + +Let + +```text +D = C_YY C_ZZ - C_YZ^2 +``` + +and + +```text +W = C_YZ^{-1} + = 1/D [ C_ZZ -C_YZ ] + [ -C_YZ C_YY ] . +``` + +Writing + +```text +wYY = C_ZZ / D +wYZ = -C_YZ / D +wZZ = C_YY / D +``` + +and + +```text +y' = dY/dX = snp/csp +z' = dZ/dX = tgl/csp +``` + +the vertex measurement matrix in local `(X,Y,Z)` coordinates is + +```text +H = +[ -y' 1 0 ] +[ -z' 0 1 ] . +``` + +The local 3D information matrix is + +```text +I = H^T W H . +``` + +Its independent elements are + +```text +I_YY = wYY +I_YZ = wYZ +I_ZZ = wZZ +I_XY = -(wYY y' + wYZ z') +I_XZ = -(wYZ y' + wZZ z') +I_XX = y'^2 wYY + 2 y' z' wYZ + z'^2 wZZ . +``` + +These are the six members of `TrackCovI`: + +```text +sxx, sxy, sxz, syy, syz, szz . +``` + +To combine tracks in the global frame, rotate the local information: + +```text +I_i^g = R_i I_i R_i^T . +``` + +The vertex covariance is then + +```text +C_V = (sum_i I_i^g)^{-1} . +``` + +## 8. Parent momentum covariance + +The parent momentum is the sum of independent daughter momenta: + +```text +P = sum_i p_i, C_P = sum_i C_{p_i} . +``` + +For one O2 daughter track, + +```text +px = pt (csp cos(alpha) - snp sin(alpha)) +py = pt (snp cos(alpha) + csp sin(alpha)) +pz = pt tgl +``` + +with `pt = |q|/|q/pt|`. The charge is treated as exact and discrete. The +derivatives with respect to native momentum parameters `(snp, tgl, q/pt)` are + +```text +dpx/dsnp = -pt (snp cos(alpha)/csp + sin(alpha)) +dpy/dsnp = pt (cos(alpha) - snp sin(alpha)/csp) +dpz/dtgl = pt + +dpx/d(q/pt) = -px / (q/pt) +dpy/d(q/pt) = -py / (q/pt) +dpz/d(q/pt) = -pz / (q/pt) +``` + +and the omitted mixed derivatives in this Jacobian are zero: + +```text +dpx/dtgl = 0, dpy/dtgl = 0, +dpz/dsnp = 0 . +``` + +Let `A` be this 3 by 3 Jacobian and let `C_a` be the daughter covariance +submatrix in the native momentum-parameter order `(snp,tgl,q/pt)`. Then + +```text +C_p = A C_a A^T . +``` + +The six independent elements of `C_p` are accumulated into the O2 lab covariance +slots + +```text +cov[9], cov[13], cov[14], cov[18], cov[19], cov[20] +``` + +which correspond to + +```text +Cov(px,px), Cov(py,px), Cov(py,py), +Cov(pz,px), Cov(pz,py), Cov(pz,pz). +``` + +## 9. Parent track covariance + +The final lab covariance passed to the O2 parent constructor contains the fitted +vertex covariance and the summed parent momentum covariance: + +```text +C_lab = +[ C_V 0 ] +[ 0 C_P ] . +``` + +Position-momentum cross-covariances are not included in this approximation. +The existing O2 constructor + +```cpp +TrackParCov(xyz, pxpypz, cov, charge, sectorAlpha) +``` + +then transforms this lab covariance to the selected parent track frame, +including the parent `alpha` convention. + +## 10. Regularization of singular or weakly constrained covariance matrices + +### Single-track `TrackCovI` + +The matrix + +```text +I = H^T W H +``` + +has rank at most two for one track, because one track measures only the two +coordinates transverse to its own trajectory. The null direction is the local +track tangent + +```text +t = (1, y_prime, z_prime)^T . +``` + +Indeed + +```text +H t = 0, I t = 0 . +``` + +Therefore a single-track `TrackCovI` is positive semidefinite, not positive +definite. Inverting this 3D matrix by itself is not meaningful; the apparent +negative diagonal elements seen in such an inverse are numerical symptoms of +trying to invert a rank-deficient information matrix. The physically meaningful +inverse at single-track level is only the original 2 by 2 `(Y,Z)` covariance. + +For the multi-track vertex fit, different track directions usually make + +```text +A = sum_i R_i I_i R_i^T +``` + +full rank. However, nearly parallel or otherwise weak geometries can still make +the longitudinal eigenvalue very small. To avoid an exactly singular +single-track contribution while preserving the measured `(Y,Z)` block, the code +may add a weak positive longitudinal prior only to `I_XX`: + +```text +I_XX -> I_XX + 1/sigma_X,prior^2 . +``` + +In code this is implemented as + +```cpp +constexpr float XRegErrFactor = 10.f; +const float sigmaX2 = C_YY * XRegErrFactor; +sxx += 1.f / sigmaX2; +``` + +This is different from multiplying `I_XX` by a number below one. A reduction of +`I_XX` can make the matrix indefinite, while adding a positive diagonal term +keeps the information matrix positive definite in the tangent direction and does +not alter `I_YY`, `I_YZ`, or `I_ZZ`. + +The regularization should remain weak. It is a numerical stabilizer for badly +conditioned geometries, not an additional detector measurement of the local +track `X` coordinate. + +### `calcPCACovMatrix()` + +The vertex covariance is + +```text +C_V = A^-1, A = sum_i R_i I_i R_i^T . +``` + +If `A` is singular or ill-conditioned, returning a small fallback covariance +would incorrectly shrink the uncertainty along the weakly constrained direction. +The conservative behavior is: + +1. Check that `A` is compatible with a positive-definite information matrix. +2. Reject cases where the determinant is too small compared with the diagonal + scale. +3. Invert only when these checks pass. +4. If inversion fails, or if the inverted covariance has non-positive diagonal + elements, return a deliberately loose fallback covariance. + +The code uses the leading-minor checks + +```text +A_XX > 0, +det(A_XY block) > 0, +det(A) > epsilon max(diag(A))^3 +``` + +with + +```text +epsilon = 1e-12 . +``` + +On failure, it returns a diagonal covariance with + +```text +sigma^2 = 1e6 cm^2 . +``` + +This corresponds to a 10 m uncertainty in each coordinate. It is intentionally +loose: the fallback marks the parent vertex as poorly constrained rather than +creating an artificially precise parent covariance. + +## 11. Code changes summarized + +1. `TrackCovI` now stores a full symmetric local 3D information matrix. +2. The dummy `XerrFactor` approximation was removed. +3. PCA weights and chi2 derivatives now use `sxx,sxy,sxz,syy,syz,szz`. +4. PCA covariance is the inverse of the summed global information matrix. +5. The Hessian residual-curvature term is restricted to diagonal Hessian + elements. +6. `correctTracks()` now propagates the actual candidate track state with O2's + analytic constant-`Bz` transport, keeping `mCandTr` and `mTrPos` + synchronized. +7. `createParentTrackParCov()` now propagates daughter momentum covariance from + native O2 `(snp,tgl,q/pt)` parameters to lab `(px,py,pz)` before constructing + the parent `TrackParCov`. diff --git a/Common/DCAFitter/GPU/CMakeLists.txt b/Common/DCAFitter/GPU/CMakeLists.txt new file mode 100644 index 0000000000000..faf15f8aab3df --- /dev/null +++ b/Common/DCAFitter/GPU/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +if (CUDA_ENABLED) +add_subdirectory(cuda) +endif() + +if (HIP_ENABLED) +add_subdirectory(hip) +endif() \ No newline at end of file diff --git a/Common/DCAFitter/GPU/DeviceInterface/GPUInterface.h b/Common/DCAFitter/GPU/DeviceInterface/GPUInterface.h new file mode 100644 index 0000000000000..3aa5ead805acd --- /dev/null +++ b/Common/DCAFitter/GPU/DeviceInterface/GPUInterface.h @@ -0,0 +1,71 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \brief Helper interface to the GPU device, meant to be compatible with manual allocation/streams and GPUReconstruction ones. +/// \author matteo.concas@cern.ch + +#ifndef DCAFITTER_GPU_INTERFACE +#define DCAFITTER_GPU_INTERFACE + +#include +#include +#include + +namespace o2 +{ +namespace vertexing +{ +namespace device +{ + +#if !defined(__HIPCC__) && !defined(__CUDACC__) +typedef struct _dummyStream { +} Stream; +#else +#ifdef __HIPCC__ +typedef hipStream_t Stream; +#else +typedef cudaStream_t Stream; +#endif +#endif + +class GPUInterface +{ + public: + GPUInterface(GPUInterface& other) = delete; + void operator=(const GPUInterface&) = delete; + + static GPUInterface* Instance(); + + // APIs + void registerBuffer(void*, size_t); + void unregisterBuffer(void* addr); + void allocDevice(void**, size_t); + void freeDevice(void*); + Stream& getStream(unsigned short N = 0); + Stream& getNextStream(); + + protected: + GPUInterface(size_t N = 1); + ~GPUInterface(); + + void resize(size_t); + + std::atomic mLastUsedStream{0}; + static GPUInterface* sGPUInterface; + std::vector mPool{}; + std::vector mStreams{}; +}; + +} // namespace device +} // namespace vertexing +} // namespace o2 +#endif diff --git a/Common/DCAFitter/GPU/cuda/CMakeLists.txt b/Common/DCAFitter/GPU/cuda/CMakeLists.txt new file mode 100644 index 0000000000000..6b89207279fe0 --- /dev/null +++ b/Common/DCAFitter/GPU/cuda/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(DCAFitterCUDA + TARGETVARNAME targetName + SOURCES DCAFitterN.cu + GPUInterface.cu + PUBLIC_INCLUDE_DIRECTORIES ../../include + PUBLIC_INCLUDE_DIRECTORIES ../ + PUBLIC_LINK_LIBRARIES O2::MathUtils + O2::ReconstructionDataFormats + O2::DetectorsBase + PRIVATE_LINK_LIBRARIES O2::GPUTrackingCUDAExternalProvider) +set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) +# add_compile_options(-lineinfo) + +#o2_add_test(DCAFitterNCUDA +# SOURCES test/testDCAFitterNGPU.cxx +# PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats +# O2::DCAFitterCUDA +# O2::DCAFitter +# ROOT::Core +# ROOT::Physics +# COMPONENT_NAME gpu +# LABELS vertexing +# ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage +# VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/${CMAKE_INSTALL_DATADIR}) diff --git a/Common/DCAFitter/GPU/cuda/DCAFitterN.cu b/Common/DCAFitter/GPU/cuda/DCAFitterN.cu new file mode 100644 index 0000000000000..ed7e8acba5397 --- /dev/null +++ b/Common/DCAFitter/GPU/cuda/DCAFitterN.cu @@ -0,0 +1,280 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __HIPCC__ +#include "hip/hip_runtime.h" +#else +#include +#endif + +#include + +#include "GPUCommonDef.h" +#include "DCAFitter/DCAFitterN.h" +#include "DeviceInterface/GPUInterface.h" + +#define gpuCheckError(x) \ + { \ + gpuAssert((x), __FILE__, __LINE__); \ + } +inline void gpuAssert(cudaError_t code, const char* file, int line, bool abort = true) +{ + if (code != cudaSuccess) { + std::cout << "GPUassert: " << cudaGetErrorString(code) << " " << file << " " << line << std::endl; + if (abort) { + throw std::runtime_error("GPU assert failed."); + } + } +} +namespace o2::vertexing::device +{ +namespace kernel +{ +GPUg() void warmUpGpuKernel() +{ + unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x; + float ia, ib; + ia = ib = 0.0f; + ib += ia + tid; +} + +template +GPUg() void printKernel(Fitter* fitter) +{ + if (threadIdx.x == 0) { + printf(" =============== GPU DCA Fitter %d prongs =================\n", Fitter::getNProngs()); + fitter->print(); + printf(" =========================================================\n"); + } +} + +template +GPUg() void initFitters(Fitter* fitters, unsigned int off, unsigned int N) +{ + for (auto iThread{blockIdx.x * blockDim.x + threadIdx.x + 1}; iThread < N; iThread += blockDim.x * gridDim.x) { + fitters[iThread + off] = fitters[off]; + } +} + +template +GPUg() void processKernel(Fitter* fitter, int* res, Tr*... tracks) +{ + *res = fitter->process(*tracks...); +} + +template +GPUg() void processBatchKernel(Fitter* fitters, int* results, unsigned int off, unsigned int N, Tr*... tracks) +{ + for (auto iThread{blockIdx.x * blockDim.x + threadIdx.x}; iThread < N; iThread += blockDim.x * gridDim.x) { + results[iThread + off] = fitters[iThread + off].process(tracks[iThread + off]...); + } +} + +} // namespace kernel + +/// CPU handlers +template +void print(const int nBlocks, + const int nThreads, + Fitter& fitter) +{ + Fitter* fitter_device; + gpuCheckError(cudaMalloc(reinterpret_cast(&fitter_device), sizeof(Fitter))); + gpuCheckError(cudaMemcpy(fitter_device, &fitter, sizeof(Fitter), cudaMemcpyHostToDevice)); + + kernel::printKernel<<>>(fitter_device); + + gpuCheckError(cudaPeekAtLastError()); + gpuCheckError(cudaDeviceSynchronize()); +} + +template +int process(const int nBlocks, + const int nThreads, + Fitter& fitter, + Tr&... args) +{ + Fitter* fitter_device; + std::array tracks_device; + int result, *result_device; + + gpuCheckError(cudaMalloc(reinterpret_cast(&fitter_device), sizeof(Fitter))); + gpuCheckError(cudaMalloc(reinterpret_cast(&result_device), sizeof(int))); + + int iArg{0}; + ([&] { + gpuCheckError(cudaMalloc(reinterpret_cast(&(tracks_device[iArg])), sizeof(o2::track::TrackParCov))); + gpuCheckError(cudaMemcpy(tracks_device[iArg], &args, sizeof(o2::track::TrackParCov), cudaMemcpyHostToDevice)); + ++iArg; + }(), + ...); + + gpuCheckError(cudaMemcpy(fitter_device, &fitter, sizeof(Fitter), cudaMemcpyHostToDevice)); + + std::apply([&](auto&&... args) { kernel::processKernel<<>>(fitter_device, result_device, args...); }, tracks_device); + + gpuCheckError(cudaPeekAtLastError()); + gpuCheckError(cudaDeviceSynchronize()); + + gpuCheckError(cudaMemcpy(&result, result_device, sizeof(int), cudaMemcpyDeviceToHost)); + gpuCheckError(cudaMemcpy(&fitter, fitter_device, sizeof(Fitter), cudaMemcpyDeviceToHost)); + iArg = 0; + ([&] { + gpuCheckError(cudaMemcpy(&args, tracks_device[iArg], sizeof(o2::track::TrackParCov), cudaMemcpyDeviceToHost)); + gpuCheckError(cudaFree(tracks_device[iArg])); + ++iArg; + }(), + ...); + + gpuCheckError(cudaFree(fitter_device)); + gpuCheckError(cudaFree(result_device)); + + return result; +} + +template +void processBulk(const int nBlocks, + const int nThreads, + const int nBatches, + std::vector& fitters, + std::vector& results, + std::vector&... args) +{ + auto* gpuInterface = GPUInterface::Instance(); + kernel::warmUpGpuKernel<<<1, 1, 0, gpuInterface->getNextStream()>>>(); + + // Benchmarking events + std::vector ioUp(nBatches), ioDown(nBatches), kerElapsed(nBatches); + std::vector startIOUp(nBatches), endIOUp(nBatches), startIODown(nBatches), endIODown(nBatches), startKer(nBatches), endKer(nBatches); + for (int iBatch{0}; iBatch < nBatches; ++iBatch) { + gpuCheckError(cudaEventCreate(&startIOUp[iBatch])); + gpuCheckError(cudaEventCreate(&endIOUp[iBatch])); + gpuCheckError(cudaEventCreate(&startIODown[iBatch])); + gpuCheckError(cudaEventCreate(&endIODown[iBatch])); + gpuCheckError(cudaEventCreate(&startKer[iBatch])); + gpuCheckError(cudaEventCreate(&endKer[iBatch])); + } + + // Tracks + std::array tracks_device; + int iArg{0}; + ([&] { + gpuInterface->registerBuffer(reinterpret_cast(args.data()), sizeof(Tr) * args.size()); + gpuInterface->allocDevice(reinterpret_cast(&(tracks_device[iArg])), sizeof(Tr) * args.size()); + ++iArg; + }(), + ...); + + // Fitters + gpuInterface->registerBuffer(reinterpret_cast(fitters.data()), sizeof(Fitter) * fitters.size()); + Fitter* fitters_device; + gpuInterface->allocDevice(reinterpret_cast(&fitters_device), sizeof(Fitter) * fitters.size()); + + // Results + gpuInterface->registerBuffer(reinterpret_cast(results.data()), sizeof(int) * fitters.size()); + int* results_device; + gpuInterface->allocDevice(reinterpret_cast(&results_device), sizeof(int) * fitters.size()); + + // R.R. Computation + int totalSize = fitters.size(); + int batchSize = totalSize / nBatches; + int remainder = totalSize % nBatches; + + for (int iBatch{0}; iBatch < nBatches; ++iBatch) { + auto& stream = gpuInterface->getNextStream(); + auto offset = iBatch * batchSize + std::min(iBatch, remainder); + auto nFits = batchSize + (iBatch < remainder ? 1 : 0); + + gpuCheckError(cudaEventRecord(startIOUp[iBatch], stream)); + gpuCheckError(cudaMemcpyAsync(fitters_device + offset, fitters.data() + offset, sizeof(Fitter) /* * nFits */, cudaMemcpyHostToDevice, stream)); // copying just the first element of the buffer + iArg = 0; + ([&] { + gpuCheckError(cudaMemcpyAsync(tracks_device[iArg] + offset, args.data() + offset, sizeof(Tr) * nFits, cudaMemcpyHostToDevice, stream)); + ++iArg; + }(), + ...); + gpuCheckError(cudaEventRecord(endIOUp[iBatch], stream)); + + gpuCheckError(cudaEventRecord(startKer[iBatch], stream)); + kernel::initFitters<<>>(fitters_device, offset, nFits); + std::apply([&](auto&&... args) { kernel::processBatchKernel<<>>(fitters_device, results_device, offset, nFits, args...); }, tracks_device); + gpuCheckError(cudaEventRecord(endKer[iBatch], stream)); + + gpuCheckError(cudaPeekAtLastError()); + iArg = 0; + gpuCheckError(cudaEventRecord(startIODown[iBatch], stream)); + ([&] { + gpuCheckError(cudaMemcpyAsync(args.data() + offset, tracks_device[iArg] + offset, sizeof(Tr) * nFits, cudaMemcpyDeviceToHost, stream)); + ++iArg; + }(), + ...); + + gpuCheckError(cudaMemcpyAsync(fitters.data() + offset, fitters_device + offset, sizeof(Fitter) * nFits, cudaMemcpyDeviceToHost, stream)); + gpuCheckError(cudaMemcpyAsync(results.data() + offset, results_device + offset, sizeof(int) * nFits, cudaMemcpyDeviceToHost, stream)); + gpuCheckError(cudaEventRecord(endIODown[iBatch], stream)); + } + + ([&] { gpuInterface->unregisterBuffer(args.data()); }(), ...); + + for (auto* tracksD : tracks_device) { + gpuInterface->freeDevice(tracksD); + } + + gpuInterface->freeDevice(fitters_device); + gpuInterface->freeDevice(results_device); + gpuInterface->unregisterBuffer(fitters.data()); + gpuInterface->unregisterBuffer(results.data()); + + // Do benchmarks + gpuCheckError(cudaDeviceSynchronize()); + for (int iBatch{0}; iBatch < nBatches; ++iBatch) { + gpuCheckError(cudaEventElapsedTime(&ioUp[iBatch], startIOUp[iBatch], endIOUp[iBatch])); + gpuCheckError(cudaEventElapsedTime(&kerElapsed[iBatch], startKer[iBatch], endKer[iBatch])); + gpuCheckError(cudaEventElapsedTime(&ioDown[iBatch], startIODown[iBatch], endIODown[iBatch])); + } + + float totalUp = std::accumulate(ioUp.begin(), ioUp.end(), 0.f); + float totalDown = std::accumulate(ioDown.begin(), ioDown.end(), 0.f); + float totalKernels = std::accumulate(kerElapsed.begin(), kerElapsed.end(), 0.f); + LOGP(info, "Config: {} batches, {} blocks, {} threads", nBatches, nBlocks, nThreads); + LOGP(info, "Total I/O time: Up {} ms Avg {} ms, Down {} ms Avg {} ms", totalUp, totalUp / float(nBatches), totalDown, totalDown / (float)nBatches); + LOGP(info, "Total Kernel time: {} ms Avg {} ms", totalKernels, totalKernels / (float)nBatches); + + for (int iBatch{0}; iBatch < nBatches; ++iBatch) { + gpuCheckError(cudaEventDestroy(startIOUp[iBatch])); + gpuCheckError(cudaEventDestroy(endIOUp[iBatch])); + gpuCheckError(cudaEventDestroy(startIODown[iBatch])); + gpuCheckError(cudaEventDestroy(endIODown[iBatch])); + gpuCheckError(cudaEventDestroy(startKer[iBatch])); + gpuCheckError(cudaEventDestroy(endKer[iBatch])); + } +} + +template void processBulk(const int, + const int, + const int, + std::vector>&, + std::vector&, + std::vector&, + std::vector&); +template void processBulk(const int, + const int, + const int, + std::vector>&, + std::vector&, + std::vector&, + std::vector&, + std::vector&); +template int process(const int, const int, o2::vertexing::DCAFitterN<2>&, o2::track::TrackParCov&, o2::track::TrackParCov&); +template int process(const int, const int, o2::vertexing::DCAFitterN<3>&, o2::track::TrackParCov&, o2::track::TrackParCov&, o2::track::TrackParCov&); +template void print(const int, const int, o2::vertexing::DCAFitterN<2>&); +template void print(const int, const int, o2::vertexing::DCAFitterN<3>&); +} // namespace o2::vertexing::device \ No newline at end of file diff --git a/Common/DCAFitter/GPU/cuda/GPUInterface.cu b/Common/DCAFitter/GPU/cuda/GPUInterface.cu new file mode 100644 index 0000000000000..09f9cdc595dcd --- /dev/null +++ b/Common/DCAFitter/GPU/cuda/GPUInterface.cu @@ -0,0 +1,108 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \brief Helper interface to the GPU device, meant to be compatible with manual allocation/streams and GPUReconstruction ones. +/// \author matteo.concas@cern.ch + +#ifdef __HIPCC__ +#include "hip/hip_runtime.h" +#else +#include +#endif + +#include +#include + +#include "DeviceInterface/GPUInterface.h" + +#define gpuCheckError(x) \ + { \ + gpuAssert((x), __FILE__, __LINE__); \ + } +#define gpuCheckErrorSoft(x) \ + { \ + gpuAssert((x), __FILE__, __LINE__, false); \ + } +inline void gpuAssert(cudaError_t code, const char* file, int line, bool abort = true) +{ + if (code != cudaSuccess) { + std::cerr << "GPUassert: " << cudaGetErrorString(code) << " " << file << " " << line << std::endl; + if (abort) { + throw std::runtime_error("GPU assert failed."); + } + } +} + +namespace o2::vertexing::device +{ + +GPUInterface::GPUInterface(size_t N) +{ + resize(N); + for (auto& st : mStreams) { + gpuCheckError(cudaStreamCreate(&st)); + } +} + +GPUInterface::~GPUInterface() +{ + for (auto& st : mStreams) { + gpuCheckError(cudaStreamDestroy(st)); + } +} + +void GPUInterface::resize(size_t N) +{ + mPool.resize(N); + mStreams.resize(N); +} + +void GPUInterface::registerBuffer(void* addr, size_t bufferSize) +{ + gpuCheckError(cudaHostRegister(addr, bufferSize, cudaHostRegisterDefault)); +} + +void GPUInterface::unregisterBuffer(void* addr) +{ + gpuCheckError(cudaHostUnregister(addr)); +} + +GPUInterface* GPUInterface::sGPUInterface = nullptr; +GPUInterface* GPUInterface::Instance() +{ + if (sGPUInterface == nullptr) { + const auto* envValue = std::getenv("GPUINTERFACE_NSTREAMS"); + sGPUInterface = new GPUInterface(envValue == nullptr ? 8 : std::stoi(envValue)); + } + return sGPUInterface; +} + +void GPUInterface::allocDevice(void** addrPtr, size_t bufferSize) +{ + gpuCheckError(cudaMalloc(addrPtr, bufferSize)); +} + +void GPUInterface::freeDevice(void* addr) +{ + gpuCheckError(cudaFree(addr)); +} + +Stream& GPUInterface::getStream(unsigned short N) +{ + return mStreams[N % mStreams.size()]; +} + +Stream& GPUInterface::getNextStream() +{ + unsigned short next = mLastUsedStream.fetch_add(1) % mStreams.size(); // wrap-around + automatic wrap-around beyond 65535 + return mStreams[next]; +} +} // namespace o2::vertexing::device \ No newline at end of file diff --git a/Common/DCAFitter/GPU/cuda/test/testDCAFitterNGPU.cxx b/Common/DCAFitter/GPU/cuda/test/testDCAFitterNGPU.cxx new file mode 100644 index 0000000000000..e3f882dc755cb --- /dev/null +++ b/Common/DCAFitter/GPU/cuda/test/testDCAFitterNGPU.cxx @@ -0,0 +1,1086 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test DCAFitterN class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include + +#include "DCAFitter/DCAFitterN.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace vertexing +{ + +using Vec3D = ROOT::Math::SVector; + +template +float checkResults(o2::utils::TreeStreamRedirector& outs, std::string& treeName, FITTER& fitter, + Vec3D& vgen, TLorentzVector& genPar, const std::vector& dtMass) +{ + int nCand = fitter.getNCandidates(); + std::array p; + float distMin = 1e9; + bool absDCA = fitter.getUseAbsDCA(); + bool useWghDCA = fitter.getWeightedFinalPCA(); + for (int ic = 0; ic < nCand; ic++) { + const auto& vtx = fitter.getPCACandidate(ic); + auto df = vgen; + df -= vtx; + + TLorentzVector moth, prong; + for (int i = 0; i < fitter.getNProngs(); i++) { + const auto& trc = fitter.getTrack(i, ic); + trc.getPxPyPzGlo(p); + prong.SetVectM({p[0], p[1], p[2]}, dtMass[i]); + moth += prong; + } + auto nIter = fitter.getNIterations(ic); + auto chi2 = fitter.getChi2AtPCACandidate(ic); + double dst = TMath::Sqrt(df[0] * df[0] + df[1] * df[1] + df[2] * df[2]); + distMin = dst < distMin ? dst : distMin; + auto parentTrack = fitter.createParentTrackParCov(ic); + outs << treeName.c_str() << "cand=" << ic << "ncand=" << nCand << "nIter=" << nIter << "chi2=" << chi2 + << "genPart=" << genPar << "recPart=" << moth + << "genX=" << vgen[0] << "genY=" << vgen[1] << "genZ=" << vgen[2] + << "dx=" << df[0] << "dy=" << df[1] << "dz=" << df[2] << "dst=" << dst + << "useAbsDCA=" << absDCA << "useWghDCA=" << useWghDCA << "parent=" << parentTrack; + for (int i = 0; i < fitter.getNProngs(); i++) { + outs << treeName.c_str() << fmt::format("prong{}=", i).c_str() << fitter.getTrack(i, ic); + } + outs << treeName.c_str() << "\n"; + } + return distMin; +} + +TLorentzVector generate(Vec3D& vtx, std::vector& vctr, float bz, + TGenPhaseSpace& genPHS, double parMass, const std::vector& dtMass, std::vector forceQ) +{ + const float errYZ = 1e-2, errSlp = 1e-3, errQPT = 2e-2; + std::array covm = { + errYZ * errYZ, + 0., errYZ * errYZ, + 0, 0., errSlp * errSlp, + 0., 0., 0., errSlp * errSlp, + 0., 0., 0., 0., errQPT * errQPT}; + bool accept = true; + TLorentzVector parent, d0, d1, d2; + do { + accept = true; + double y = gRandom->Rndm() - 0.5; + double pt = 0.1 + gRandom->Rndm() * 3; + double mt = TMath::Sqrt(parMass * parMass + pt * pt); + double pz = mt * TMath::SinH(y); + double phi = gRandom->Rndm() * TMath::Pi() * 2; + double en = mt * TMath::CosH(y); + double rdec = 10.; // radius of the decay + vtx[0] = rdec * TMath::Cos(phi); + vtx[1] = rdec * TMath::Sin(phi); + vtx[2] = rdec * pz / pt; + parent.SetPxPyPzE(pt * TMath::Cos(phi), pt * TMath::Sin(phi), pz, en); + int nd = dtMass.size(); + genPHS.SetDecay(parent, nd, dtMass.data()); + genPHS.Generate(); + vctr.clear(); + float p[4]; + for (int i = 0; i < nd; i++) { + auto* dt = genPHS.GetDecay(i); + if (dt->Pt() < 0.05) { + accept = false; + break; + } + dt->GetXYZT(p); + float s, c, x; + std::array params; + o2::math_utils::sincos(dt->Phi(), s, c); + o2::math_utils::rotateZInv(vtx[0], vtx[1], x, params[0], s, c); + + params[1] = vtx[2]; + params[2] = 0.; // since alpha = phi + params[3] = 1. / TMath::Tan(dt->Theta()); + params[4] = (i % 2 ? -1. : 1.) / dt->Pt(); + covm[14] = errQPT * errQPT * params[4] * params[4]; + // + // randomize + float r1, r2; + gRandom->Rannor(r1, r2); + params[0] += r1 * errYZ; + params[1] += r2 * errYZ; + gRandom->Rannor(r1, r2); + params[2] += r1 * errSlp; + params[3] += r2 * errSlp; + params[4] *= gRandom->Gaus(1., errQPT); + if (forceQ[i] == 0) { + params[4] = 0.; // impose straight track + } + auto& trc = vctr.emplace_back(x, dt->Phi(), params, covm); + float rad = forceQ[i] == 0 ? 600. : TMath::Abs(1. / trc.getCurvature(bz)); + if (!trc.propagateTo(trc.getX() + (gRandom->Rndm() - 0.5) * rad * 0.05, bz) || + !trc.rotate(trc.getAlpha() + (gRandom->Rndm() - 0.5) * 0.2)) { + printf("Failed to randomize "); + trc.print(); + } + } + } while (!accept); + + return parent; +} + +#ifdef DO_SINGLE_THREAD_TEST +BOOST_AUTO_TEST_CASE(DCAFitterNProngs) +{ + // gRandom->Delete(); + // gRandom = new TRandom(42); + o2::utils::TreeStreamRedirector outStream("dcafitterNTest.root"); + + TGenPhaseSpace genPHS; + constexpr double ele = 0.00051; + constexpr double gamma = 2 * ele + 1e-6; + constexpr double pion = 0.13957; + constexpr double k0 = 0.49761; + constexpr double kch = 0.49368; + constexpr double dch = 1.86965; + std::vector gammadec = {ele, ele}; + std::vector k0dec = {pion, pion}; + std::vector dchdec = {pion, kch, pion}; + std::vector vctracks; + Vec3D vtxGen; + + double bz = 5.0; + // 2 prongs vertices + { + LOG(info) << "Processing 2-prong Helix - Helix case"; + std::vector forceQ{1, 1}; + + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMaxDXYIni(4); // do not consider V0 seeds with tracks XY-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::string treeName2A = "pr2a", treeName2AW = "pr2aw", treeName2W = "pr2w"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + auto genParent = generate(vtxGen, vctracks, bz, genPHS, k0, k0dec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swA.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName2A, ft, vtxGen, genParent, k0dec); + meanDA += minD; + nfoundA++; + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName2AW, ft, vtxGen, genParent, k0dec); + meanDAW += minD; + nfoundAW++; + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName2W, ft, vtxGen, genParent, k0dec); + meanDW += minD; + nfoundW++; + } + } + + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundA : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 2-prong vertices Helix : Helix"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " Total time: " << swA.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " Total time: " << swAW.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " Total time: " << swW.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 0.1); + BOOST_CHECK(meanDAW < 0.1); + BOOST_CHECK(meanDW < 0.1); + } + + // 2 prongs vertices with collinear tracks (gamma conversion) + { + LOG(info) << "Processing 2-prong Helix - Helix case gamma conversion"; + std::vector forceQ{1, 1}; + + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMaxDXYIni(4); // do not consider V0 seeds with tracks XY-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::string treeName2A = "gpr2a", treeName2AW = "gpr2aw", treeName2W = "gpr2w"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + auto genParent = generate(vtxGen, vctracks, bz, genPHS, gamma, gammadec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = device::process(1, 1, ft, vctracks[0], vctracks[1]); + swA.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName2A, ft, vtxGen, genParent, gammadec); + meanDA += minD; + nfoundA++; + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName2AW, ft, vtxGen, genParent, gammadec); + meanDAW += minD; + nfoundAW++; + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName2W, ft, vtxGen, genParent, gammadec); + meanDW += minD; + nfoundW++; + } + } + + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundA ? nfoundA : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 2-prong vertices Helix : Helix from gamma conversion"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " Total time: " << swA.CpuTime(); + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " Total time: " << swAW.CpuTime(); + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " Total time: " << swW.CpuTime(); + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 2.1); + BOOST_CHECK(meanDAW < 2.1); + BOOST_CHECK(meanDW < 2.1); + } + + // 2 prongs vertices with one of charges set to 0: Helix : Line + { + std::vector forceQ{1, 1}; + LOG(info) << "Processing 2-prong Helix - Line case"; + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::string treeName2A = "pr2aHL", treeName2AW = "pr2awHL", treeName2W = "pr2wHL"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + forceQ[iev % 2] = 1; + forceQ[1 - iev % 2] = 0; + auto genParent = generate(vtxGen, vctracks, bz, genPHS, k0, k0dec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = device::process(1, 1, ft, vctracks[0], vctracks[1]); + swA.Stop(); + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName2A, ft, vtxGen, genParent, k0dec); + meanDA += minD; + nfoundA++; + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName2AW, ft, vtxGen, genParent, k0dec); + meanDAW += minD; + nfoundAW++; + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName2W, ft, vtxGen, genParent, k0dec); + meanDW += minD; + nfoundW++; + } + } + + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundAW : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 2-prong vertices: Helix : Line"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " Total time: " << swA.CpuTime(); + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " Total time: " << swAW.CpuTime(); + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " Total time: " << swW.CpuTime(); + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 0.1); + BOOST_CHECK(meanDAW < 0.1); + BOOST_CHECK(meanDW < 0.1); + } + + // 2 prongs vertices with both of charges set to 0: Line : Line + { + std::vector forceQ{0, 0}; + LOG(info) << "Processing 2-prong Line - Line case"; + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::string treeName2A = "pr2aLL", treeName2AW = "pr2awLL", treeName2W = "pr2wLL"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + forceQ[0] = forceQ[1] = 0; + auto genParent = generate(vtxGen, vctracks, bz, genPHS, k0, k0dec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swA.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName2A, ft, vtxGen, genParent, k0dec); + meanDA += minD; + nfoundA++; + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName2AW, ft, vtxGen, genParent, k0dec); + meanDAW += minD; + nfoundAW++; + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName2W, ft, vtxGen, genParent, k0dec); + meanDW += minD; + nfoundW++; + } + } + + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundAW : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 2-prong vertices: Line : Line"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " Total time: " << swA.CpuTime(); + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " Total time: " << swAW.CpuTime(); + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " Total time: " << swW.CpuTime(); + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 0.1); + BOOST_CHECK(meanDAW < 0.1); + BOOST_CHECK(meanDW < 0.1); + } + + // 3 prongs vertices + { + std::vector forceQ{1, 1, 1}; + + o2::vertexing::DCAFitterN<3> ft; // 3 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::string treeName3A = "pr3a", treeName3AW = "pr3aw", treeName3W = "pr3w"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + auto genParent = generate(vtxGen, vctracks, bz, genPHS, dch, dchdec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = device::process(1, 1, ft, vctracks[0], vctracks[1], vctracks[2]); + swA.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName3A, ft, vtxGen, genParent, dchdec); + meanDA += minD; + nfoundA++; + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = device::process(1, 1, ft, vctracks[0], vctracks[1], vctracks[2]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName3AW, ft, vtxGen, genParent, dchdec); + meanDAW += minD; + nfoundAW++; + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = device::process(1, 1, ft, vctracks[0], vctracks[1], vctracks[2]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName3W, ft, vtxGen, genParent, dchdec); + meanDW += minD; + nfoundW++; + } + } + + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundAW : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 3-prong vertices"; + LOG(info) << "3-prongs with abs.dist minimization: eff = " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " Total time: " << swA.CpuTime(); + LOG(info) << "3-prongs with abs.dist but wghPCA: eff = " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " Total time: " << swAW.CpuTime(); + LOG(info) << "3-prongs with wgh.dist minimization: eff = " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " Total time: " << swW.CpuTime(); + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 0.1); + BOOST_CHECK(meanDAW < 0.1); + BOOST_CHECK(meanDW < 0.1); + } + + outStream.Close(); +} +#endif + +BOOST_AUTO_TEST_CASE(DCAFitterNProngsBulk) +{ + const char* nThreadsEnvVarName = "DCAFITTERGPU_TEST_NTHREADS"; + const char* nBlocksEnvVarName = "DCAFITTERGPU_TEST_NBLOCKS"; + const char* nBatchesEnvVarName = "DCAFITTERGPU_TEST_NBATCHES"; + const char* nTestsEnvVarName = "DCAFITTERGPU_TEST_NTESTS"; + int nBlocks = std::getenv(nBlocksEnvVarName) == nullptr ? 30 : std::stoi(std::getenv(nBlocksEnvVarName)); + int nThreads = std::getenv(nThreadsEnvVarName) == nullptr ? 256 : std::stoi(std::getenv(nThreadsEnvVarName)); + int nBatches = std::getenv(nBatchesEnvVarName) == nullptr ? 8 : std::stoi(std::getenv(nBatchesEnvVarName)); + int NTest = std::getenv(nTestsEnvVarName) == nullptr ? 100001 : std::stoi(std::getenv(nTestsEnvVarName)); + + o2::utils::TreeStreamRedirector outStreamB("dcafitterNTestBulk.root"); + + TGenPhaseSpace genPHS; + constexpr double ele = 0.00051; + constexpr double gamma = 2 * ele + 1e-6; + constexpr double pion = 0.13957; + constexpr double k0 = 0.49761; + constexpr double kch = 0.49368; + constexpr double dch = 1.86965; + std::vector gammadec = {ele, ele}; + std::vector k0dec = {pion, pion}; + std::vector dchdec = {pion, kch, pion}; + std::vector> vctracks(3, std::vector(NTest)); + std::vector vtxGen(NTest); + + double bz = 5.0; + { // 2 prongs vertices bulk processing + LOG(info) << "\n\nBulk-processing 2-prong Helix - Helix case"; + std::vector forceQ{1, 1}; + + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMaxDXYIni(4); // do not consider V0 seeds with tracks XY-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::vector> fitters_host(NTest); + std::vector genParents(NTest); + + std::string treeName2Abulk = "pr2aBulk", treeName2AWbulk = "pr2awBulk", treeName2Wbulk = "pr2wBulk"; + TStopwatch swAb, swAWb, swWb; + int nfoundAb = 0, nfoundAWb = 0, nfoundWb = 0; + double meanDAb = 0, meanDAWb = 0, meanDWb = 0; + swAb.Stop(); + swAWb.Stop(); + swWb.Stop(); + + ft.setUseAbsDCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + for (int iev = 0; iev < NTest; iev++) { + std::vector vc(2); + genParents[iev] = generate(vtxGen[iev], vc, bz, genPHS, k0, k0dec, forceQ); + vctracks[0][iev] = vc[0]; + vctracks[1][iev] = vc[1]; + } + + swAb.Start(false); + std::vector ncAb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAb[iev] << " Chi2: " << (ncAb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Abulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAb += minDb; + nfoundAb++; + } + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swAWb.Start(false); + std::vector ncAWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAWb[iev] << " Chi2: " << (ncAWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2AWbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAWb += minDb; + nfoundAWb++; + } + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swWb.Start(false); + std::vector ncWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncWb[iev] << " Chi2: " << (ncWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Wbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDWb += minDb; + nfoundWb++; + } + } + // + meanDAb /= nfoundAb ? nfoundAb : 1; + meanDAWb /= nfoundAWb ? nfoundAWb : 1; + meanDWb /= nfoundWb ? nfoundWb : 1; + LOGP(info, "Bulk-processed {} 2-prong vertices Helix : Helix", NTest); + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundAb) / NTest + << " mean.dist to truth: " << meanDAb << " Total time: " << swAb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAWb) / NTest + << " mean.dist to truth: " << meanDAWb << " Total time: " << swAWb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundWb) / NTest + << " mean.dist to truth: " << meanDWb << " Total time: " << swWb.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundAb > 0.99 * NTest); + BOOST_CHECK(nfoundAWb > 0.99 * NTest); + BOOST_CHECK(nfoundWb > 0.99 * NTest); + BOOST_CHECK(meanDAb < 0.1); + BOOST_CHECK(meanDAWb < 0.1); + BOOST_CHECK(meanDWb < 0.1); + } + + { // 2 prongs vertices bulk processing for gamma conversion + LOG(info) << "\n\nBulk-processing 2-prong Helix - Helix case gamma conversion"; + std::vector forceQ{1, 1}; + + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMaxDXYIni(4); // do not consider V0 seeds with tracks XY-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::vector> fitters_host(NTest); + std::vector genParents(NTest); + + std::string treeName2Abulk = "gpr2aBulk", treeName2AWbulk = "gpr2awBulk", treeName2Wbulk = "gpr2wBulk"; + TStopwatch swAb, swAWb, swWb; + int nfoundAb = 0, nfoundAWb = 0, nfoundWb = 0; + double meanDAb = 0, meanDAWb = 0, meanDWb = 0; + swAb.Stop(); + swAWb.Stop(); + swWb.Stop(); + + ft.setUseAbsDCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + for (int iev = 0; iev < NTest; iev++) { + std::vector vc(2); + genParents[iev] = generate(vtxGen[iev], vc, bz, genPHS, gamma, gammadec, forceQ); + vctracks[0][iev] = vc[0]; + vctracks[1][iev] = vc[1]; + } + + swAb.Start(false); + std::vector ncAb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAb[iev] << " Chi2: " << (ncAb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Abulk, fitters_host[iev], vtxGen[iev], genParents[iev], gammadec); + meanDAb += minDb; + nfoundAb++; + } + } + // + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swAWb.Start(false); + std::vector ncAWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAWb[iev] << " Chi2: " << (ncAWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2AWbulk, fitters_host[iev], vtxGen[iev], genParents[iev], gammadec); + meanDAWb += minDb; + nfoundAWb++; + } + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swWb.Start(false); + std::vector ncWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncWb[iev] << " Chi2: " << (ncWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Wbulk, fitters_host[iev], vtxGen[iev], genParents[iev], gammadec); + meanDWb += minDb; + nfoundWb++; + } + } + // + + meanDAb /= nfoundAb ? nfoundAb : 1; + meanDAWb /= nfoundAWb ? nfoundAWb : 1; + meanDWb /= nfoundWb ? nfoundWb : 1; + LOGP(info, "Bulk-processed {} 2-prong vertices Helix : Helix from gamma conversion", NTest); + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundAb) / NTest + << " mean.dist to truth: " << meanDAb << " Total time: " << swAb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAWb) / NTest + << " mean.dist to truth: " << meanDAWb << " Total time: " << swAWb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundWb) / NTest + << " mean.dist to truth: " << meanDWb << " Total time: " << swWb.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundAb > 0.99 * NTest); + BOOST_CHECK(nfoundAWb > 0.99 * NTest); + BOOST_CHECK(nfoundWb > 0.99 * NTest); + BOOST_CHECK(meanDAb < 2.1); + BOOST_CHECK(meanDAWb < 2.1); + BOOST_CHECK(meanDWb < 2.1); + } + + // 2 prongs vertices bulk processing with one of charges set to 0: Helix : Line + { + std::vector forceQ{1, 1}; + LOG(info) << "\n\nBulk-processing 2-prong Helix - Line case"; + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::vector> fitters_host(NTest); + std::vector genParents(NTest); + + std::string treeName2Abulk = "pr2aHLb", treeName2AWbulk = "pr2awHLb", treeName2Wbulk = "pr2wHLb"; + TStopwatch swAb, swAWb, swWb; + int nfoundAb = 0, nfoundAWb = 0, nfoundWb = 0; + double meanDAb = 0, meanDAWb = 0, meanDWb = 0; + swAb.Stop(); + swAWb.Stop(); + swWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + forceQ[iev % 2] = 1; + forceQ[1 - iev % 2] = 0; + std::vector vc(2); + genParents[iev] = generate(vtxGen[iev], vc, bz, genPHS, k0, k0dec, forceQ); + vctracks[0][iev] = vc[0]; + vctracks[1][iev] = vc[1]; + } + ft.setUseAbsDCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + + swAb.Start(false); + std::vector ncAb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAb[iev] << " Chi2: " << (ncAb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Abulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAb += minDb; + nfoundAb++; + } + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swAWb.Start(false); + std::vector ncAWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAWb[iev] << " Chi2: " << (ncAWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2AWbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAWb += minDb; + nfoundAWb++; + } + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swWb.Start(false); + std::vector ncWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncWb[iev] << " Chi2: " << (ncWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Wbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDWb += minDb; + nfoundWb++; + } + } + + // + meanDAb /= nfoundAb ? nfoundAb : 1; + meanDAWb /= nfoundAWb ? nfoundAWb : 1; + meanDWb /= nfoundWb ? nfoundWb : 1; + LOG(info) << "Bulk-processed " << NTest << " 2-prong vertices: Helix : Line"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundAb) / NTest + << " mean.dist to truth: " << meanDAb << " Total time: " << swAb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAWb) / NTest + << " mean.dist to truth: " << meanDAWb << " Total time: " << swAWb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundWb) / NTest + << " mean.dist to truth: " << meanDWb << " Total time: " << swWb.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundAb > 0.99 * NTest); + BOOST_CHECK(nfoundAWb > 0.99 * NTest); + BOOST_CHECK(nfoundWb > 0.99 * NTest); + BOOST_CHECK(meanDAb < 0.1); + BOOST_CHECK(meanDAWb < 0.1); + BOOST_CHECK(meanDWb < 0.1); + } + + // 2 prongs vertices with both of charges set to 0: Line : Line + { + std::vector forceQ{0, 0}; + LOG(info) << "\n\nBulk-processing 2-prong Line - Line case"; + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::vector> fitters_host(NTest); + std::vector genParents(NTest); + + std::string treeName2Abulk = "pr2aLL", treeName2AWbulk = "pr2awLL", treeName2Wbulk = "pr2wLL"; + TStopwatch swAb, swAWb, swWb; + int nfoundAb = 0, nfoundAWb = 0, nfoundWb = 0; + double meanDAb = 0, meanDAWb = 0, meanDWb = 0; + swAb.Stop(); + swAWb.Stop(); + swWb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + forceQ[0] = forceQ[1] = 0; + std::vector vc(2); + genParents[iev] = generate(vtxGen[iev], vc, bz, genPHS, k0, k0dec, forceQ); + vctracks[0][iev] = vc[0]; + vctracks[1][iev] = vc[1]; + } + + ft.setUseAbsDCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + + swAb.Start(false); + std::vector ncAb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAb[iev] << " Chi2: " << (ncAb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Abulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAb += minDb; + nfoundAb++; + } + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swAWb.Start(false); + std::vector ncAWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAWb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAWb[iev] << " Chi2: " << (ncAWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2AWbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAWb += minDb; + nfoundAWb++; + } + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + + swWb.Start(false); + std::vector ncWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncWb[iev] << " Chi2: " << (ncWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Wbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDWb += minDb; + nfoundWb++; + } + } + // ft.print(); + meanDAb /= nfoundAb ? nfoundAb : 1; + meanDAWb /= nfoundAWb ? nfoundAWb : 1; + meanDWb /= nfoundWb ? nfoundWb : 1; + LOG(info) << "Bulk-processed " << NTest << " 2-prong vertices: Line : Line"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundAb) / NTest + << " mean.dist to truth: " << meanDAb << " Total time: " << swAb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAWb) / NTest + << " mean.dist to truth: " << meanDAWb << " Total time: " << swAWb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundWb) / NTest + << " mean.dist to truth: " << meanDWb << " Total time: " << swWb.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundAb > 0.99 * NTest); + BOOST_CHECK(nfoundAWb > 0.99 * NTest); + BOOST_CHECK(nfoundWb > 0.99 * NTest); + BOOST_CHECK(meanDAb < 0.1); + BOOST_CHECK(meanDAWb < 0.1); + BOOST_CHECK(meanDWb < 0.1); + } + + // Bulk-process 3 prongs vertices + { + LOG(info) << "\n\nBulk-processing 3-prongs"; + std::vector forceQ{1, 1, 1}; + + o2::vertexing::DCAFitterN<3> ft; // 3 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::vector> fitters_host(NTest); + std::vector genParents(NTest); + + std::string treeName3Abulk = "pr3a", treeName3AWbulk = "pr3aw", treeName3Wbulk = "pr3w"; + TStopwatch swAb, swAWb, swWb; + int nfoundAb = 0, nfoundAWb = 0, nfoundWb = 0; + double meanDAb = 0, meanDAWb = 0, meanDWb = 0; + swAb.Stop(); + swAWb.Stop(); + swWb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + std::vector vc(3); + genParents[iev] = generate(vtxGen[iev], vc, bz, genPHS, dch, dchdec, forceQ); + + vctracks[0][iev] = vc[0]; + vctracks[1][iev] = vc[1]; + vctracks[2][iev] = vc[2]; + } + + ft.setUseAbsDCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swAb.Start(false); + std::vector ncAb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAb, vctracks[0], vctracks[1], vctracks[2]); // HERE WE FIT THE VERTICES + swAb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAb[iev] << " Chi2: " << (ncAb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAb[iev]) { + auto minDb = checkResults(outStreamB, treeName3Abulk, fitters_host[iev], vtxGen[iev], genParents[iev], dchdec); + meanDAb += minDb; + nfoundAb++; + } + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + + swAWb.Start(false); + std::vector ncAWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAWb, vctracks[0], vctracks[1], vctracks[2]); // HERE WE FIT THE VERTICES + swAWb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAWb[iev] << " Chi2: " << (ncAWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAWb[iev]) { + auto minDb = checkResults(outStreamB, treeName3AWbulk, fitters_host[iev], vtxGen[iev], genParents[iev], dchdec); + meanDAWb += minDb; + nfoundAWb++; + } + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + + swWb.Start(false); + std::vector ncWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncWb, vctracks[0], vctracks[1], vctracks[2]); // HERE WE FIT THE VERTICES + swWb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncWb[iev] << " Chi2: " << (ncWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncWb[iev]) { + auto minDb = checkResults(outStreamB, treeName3Wbulk, fitters_host[iev], vtxGen[iev], genParents[iev], dchdec); + meanDWb += minDb; + nfoundWb++; + } + } + + // ft.print(); + meanDAb /= nfoundAb ? nfoundAb : 1; + meanDAWb /= nfoundAWb ? nfoundAWb : 1; + meanDWb /= nfoundWb ? nfoundWb : 1; + LOG(info) << "Bulk-processed " << NTest << " 3-prong vertices"; + LOG(info) << "3-prongs with abs.dist minimization: eff = " << float(nfoundAb) / NTest + << " mean.dist to truth: " << meanDAb << " Total time: " << swAb.CpuTime() * 1000 << " ms"; + LOG(info) << "3-prongs with abs.dist but wghPCA: eff = " << float(nfoundAWb) / NTest + << " mean.dist to truth: " << meanDAWb << " Total time: " << swAWb.CpuTime() * 1000 << " ms"; + LOG(info) << "3-prongs with wgh.dist minimization: eff = " << float(nfoundWb) / NTest + << " mean.dist to truth: " << meanDWb << " Total time: " << swWb.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundAb > 0.99 * NTest); + BOOST_CHECK(nfoundAWb > 0.99 * NTest); + BOOST_CHECK(nfoundWb > 0.99 * NTest); + BOOST_CHECK(meanDAb < 0.1); + BOOST_CHECK(meanDAWb < 0.1); + BOOST_CHECK(meanDWb < 0.1); + } + outStreamB.Close(); +} + +} // namespace vertexing +} // namespace o2 \ No newline at end of file diff --git a/Common/DCAFitter/GPU/hip/CMakeLists.txt b/Common/DCAFitter/GPU/hip/CMakeLists.txt new file mode 100644 index 0000000000000..5e7821a0b8946 --- /dev/null +++ b/Common/DCAFitter/GPU/hip/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -fgpu-rdc") +o2_add_hipified_library(DCAFitterHIP + SOURCES ../cuda/DCAFitterN.cu + ../cuda/GPUInterface.cu + PUBLIC_INCLUDE_DIRECTORIES ../../include ../ + PUBLIC_LINK_LIBRARIES O2::MathUtils + O2::ReconstructionDataFormats + O2::DetectorsBase + hip::host + PRIVATE_LINK_LIBRARIES O2::GPUTrackingHIPExternalProvider + TARGETVARNAME targetNAme) + +#o2_add_test(DCAFitterNHIP +# SOURCES ../cuda/test/testDCAFitterNGPU.cxx +# PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats +# O2::DCAFitterHIP +# O2::DCAFitter +# ROOT::Core +# ROOT::Physics +# HIPIFIED test +# COMPONENT_NAME gpu +# LABELS vertexing +# ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage +# VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/${CMAKE_INSTALL_DATADIR}) diff --git a/Common/DCAFitter/README.md b/Common/DCAFitter/README.md index c952a2e33124f..e385378d10caf 100644 --- a/Common/DCAFitter/README.md +++ b/Common/DCAFitter/README.md @@ -1,8 +1,8 @@ -## DCAFitterN +# DCAFitterN Templated class to fit the Point of Closest Approach (PCA) of secondary vertex with N prongs. Allows minimization of either absolute or weighted Distances of Closest Approach (DCA) of N tracks to their common PCA. @@ -74,7 +74,60 @@ Extra method `setWeightedFinalPCA(bool)` is provided for the "mixed" mode: if `s but the final V0 position will be calculated using weighted average. One can also recalculate the V0 position by the weighted average method by calling explicitly `ft.recalculatePCAWithErrors(int icand=0)`, w/o prior call of `setWeightedFinalPCA(true)`: this will update the position returned by the `getPCACandidate(int cand = 0)`. -The covariance matrix of the V0 position is calculated as an inversed sum of tracks inversed covariances at respective `X_dca` points. +The covariance matrix of the V0 position is calculated as an inverted sum of tracks inversed covariances at respective `X_dca` points. -See ``O2/Detectors/Base/test/testDCAFitterN.cxx`` for more extended example. +See ``O2/Common/DCAFitter/test/testDCAFitterN.cxx`` for more extended example. Currently only 2 and 3 prongs permitted, thought this can be changed by modifying ``DCAFitterN::NMax`` constant. + +## Error handling + +It may happen that the track propagation to the the proximity of the PCA fails at the various stage of the fit. In this case the fit is abandoned and the failure flag is set, it can be checked using +isPropagationFailure(int cand = 0)` method. + +Also, due to the linearization errors the covariance matrix of the track propagated to some point may become non-positive defined. +In this case the relevant correlation coefficient of the cov.matrix is redefined to cure the position part of the cov.matrix and further program flow depends on the user settings for `DCAFitterN::setBadCovPolicy(v)`: + +`DCAFitterN::setBadCovPolicy(DCAFitterN::Discard);` : abandon fit (default) + +`DCAFitterN::setBadCovPolicy(DCAFitterN::Override);` : continue fit with overridden cov.matrix + +`DCAFitterN::setBadCovPolicy(DCAFitterN::OverrideAnFlag);` continue fit with overridden cov.matrix but set the propagation failure flag (can be checked using the same `isPropagationFailure(int cand = 0)` method). + +## Fit status +The fitter provides a fit status for each candidate, which can be retrieved using: +``` +FitStatus status = ft.getFitStatus(int cand = 0); +``` +The possible values are: +``` +enum FitStatus : uint8_t { // part of the DCAFitterN class + None, // no status set (should not be possible!) + + /* Good Conditions */ + Converged, // fit converged + MaxIter, // max iterations reached before fit convergence (can still be a good vertex) + + /* Error Conditions */ + NoCrossing, // no reasonable crossing was found + RejRadius, // radius of crossing was not acceptable + RejTrackX, // one candidate track x was below the minimum required radius + RejTrackRoughZ, // rejected by rough cut on tracks Z difference + RejChi2Max, // rejected by maximum chi2 cut + FailProp, // propagation of at least prong to PCA failed + FailInvCov, // inversion of cov.-matrix failed + FailInvWeight, // inversion of Ti weight matrix failed + FailInv2ndDeriv, // inversion of 2nd derivatives failed + FailCorrTracks, // correction of tracks to updated x failed + FailCloserAlt, // alternative PCA is closer +}; +``` +This is allows to track where candiate fit was abondended. +``` +int nc = ft.process(tr0,tr1,tr2); +auto status = ft.getFitStatus(); +if (nc) { + // status can either be FitStatus::Converged or FitStatus::MaxIter +} +// status can be on of the error conditions +``` +A more thorough example is given in `testDCAFitterN.cxx`. diff --git a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h index 70b5d820423d3..d02efa68526db 100644 --- a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h @@ -12,44 +12,58 @@ /// \file DCAFitterN.h /// \brief Defintions for N-prongs secondary vertex fit /// \author ruben.shahoyan@cern.ch -/// For the formulae derivation see /afs/cern.ch/user/s/shahoian/public/O2/DCAFitter/DCAFitterN.pdf +/// For the original derivation see /afs/cern.ch/user/s/shahoian/public/O2/DCAFitter/DCAFitterN.pdf +/// The AI-assisted readme is in DCAFitterN_derivation.md #ifndef _ALICEO2_DCA_FITTERN_ #define _ALICEO2_DCA_FITTERN_ -#include + +#include "ReconstructionDataFormats/HelixHelper.h" +#include "DetectorsBase/Propagator.h" #include "MathUtils/Cartesian.h" #include "ReconstructionDataFormats/Track.h" -#include "DCAFitter/HelixHelper.h" -#include "DetectorsBase/Propagator.h" namespace o2 { namespace vertexing { + ///__________________________________________________________________________________ -///< Inverse cov matrix (augmented by a dummy X error) of the point defined by the track +///< Inverse covariance matrix of the point defined by the track struct TrackCovI { - float sxx, syy, syz, szz; - - TrackCovI(const o2::track::TrackParCov& trc, float xerrFactor = 1.) { set(trc, xerrFactor); } + // Independent elements of the symmetric 3D information matrix + // H^T Cyz^{-1} H. A track constrains Y and Z at a given X through + // H = {{-dY/dX, 1, 0}, {-dZ/dX, 0, 1}}. + float sxx, sxy, sxz, syy, syz, szz; - TrackCovI() = default; + GPUdDefault() TrackCovI() = default; - void set(const o2::track::TrackParCov& trc, float xerrFactor = 1) + GPUd() bool set(const o2::track::TrackParCov& trc) { - // we assign Y error to X for DCA calculation - // (otherwise for quazi-collinear tracks the X will not be constrained) - float cyy = trc.getSigmaY2(), czz = trc.getSigmaZ2(), cyz = trc.getSigmaZY(), cxx = cyy * xerrFactor; + // Invert the 2D covariance of the measured track position (Y,Z). + float cyy = trc.getSigmaY2(), czz = trc.getSigmaZ2(), cyz = trc.getSigmaZY(); float detYZ = cyy * czz - cyz * cyz; - if (detYZ > 0.) { - auto detYZI = 1. / detYZ; - sxx = 1. / cxx; - syy = czz * detYZI; - syz = -cyz * detYZI; - szz = cyy * detYZI; - } else { - throw std::runtime_error("invalid track covariance"); + bool res = true; + if (detYZ <= 0.) { + cyz = o2::gpu::GPUCommonMath::Sqrt(cyy * czz) * (cyz > 0 ? 0.98f : -0.98f); + detYZ = cyy * czz - cyz * cyz; + res = false; } + auto detYZI = 1. / detYZ; + syy = czz * detYZI; + syz = -cyz * detYZI; + szz = cyy * detYZI; + const float cspI = 1.f / trc.getCsp(); + const float dydx = trc.getSnp() * cspI; + const float dzdx = trc.getTgl() * cspI; + sxy = -(syy * dydx + syz * dzdx); + sxz = -(syz * dydx + szz * dzdx); + sxx = dydx * dydx * syy + 2.f * dydx * dzdx * syz + dzdx * dzdx * szz; + // The matrix is degenerate by construction, regularize sxx term to preserve the original YZ block exactly + constexpr float XRegErrFactor = 10.f; + const float sigmaX2 = cyy * XRegErrFactor; + sxx += 1.f / sigmaX2; + return res; } }; @@ -57,11 +71,11 @@ struct TrackCovI { ///< Derivative (up to 2) of the TrackParam position over its running param X struct TrackDeriv { float dydx, dzdx, d2ydx2, d2zdx2; - TrackDeriv() = default; - TrackDeriv(const o2::track::TrackPar& trc, float bz) { set(trc, bz); } - void set(const o2::track::TrackPar& trc, float bz) + GPUdDefault() TrackDeriv() = default; + GPUd() TrackDeriv(const o2::track::TrackPar& trc, float bz) { set(trc, bz); } + GPUd() void set(const o2::track::TrackPar& trc, float bz) { - float snp = trc.getSnp(), csp = std::sqrt((1. - snp) * (1. + snp)), cspI = 1. / csp, crv2c = trc.getCurvature(bz) * cspI; + float snp = trc.getSnp(), csp = o2::gpu::GPUCommonMath::Sqrt((1. - snp) * (1. + snp)), cspI = 1. / csp, crv2c = trc.getCurvature(bz) * cspI; dydx = snp * cspI; // = snp/csp dzdx = trc.getTgl() * cspI; // = tgl/csp d2ydx2 = crv2c * cspI * cspI; // = crv/csp^3 @@ -69,6 +83,26 @@ struct TrackDeriv { } }; +///__________________________________________________________________________ +///< Log log-throttling helper +struct LogLogThrottler { + size_t evCount{0}; + size_t nextLog{1}; + GPUdi() bool needToLog() + { + if (++evCount > nextLog) { + nextLog *= 2; + return true; + } + return false; + } + GPUdi() void clear() + { + evCount = 0; + nextLog = 1; + } +}; + template class DCAFitterN { @@ -76,17 +110,16 @@ class DCAFitterN static constexpr double NMax = 4; static constexpr double NInv = 1. / N; static constexpr int MAXHYP = 2; - static constexpr float XerrFactor = 5.; // factor for conversion of track covYY to dummy covXX using Track = o2::track::TrackParCov; using TrackAuxPar = o2::track::TrackAuxPar; using CrossInfo = o2::track::CrossInfo; - using Vec3D = ROOT::Math::SVector; - using VecND = ROOT::Math::SVector; - using MatSym3D = ROOT::Math::SMatrix>; - using MatStd3D = ROOT::Math::SMatrix>; - using MatSymND = ROOT::Math::SMatrix>; - using MatStdND = ROOT::Math::SMatrix>; + using Vec3D = o2::math_utils::SVector; + using VecND = o2::math_utils::SVector; + using MatSym3D = o2::math_utils::SMatrix>; + using MatStd3D = o2::math_utils::SMatrix>; + using MatSymND = o2::math_utils::SMatrix>; + using MatStdND = o2::math_utils::SMatrix>; using TrackCoefVtx = MatStd3D; using ArrTrack = std::array; // container for prongs (tracks) at single vertex cand. using ArrTrackCovI = std::array; // container for inv.cov.matrices at single vertex cand. @@ -95,6 +128,35 @@ class DCAFitterN using ArrTrPos = std::array; // container of Track positions public: + enum BadCovPolicy : uint8_t { // if encountering non-positive defined cov. matrix, the choice is: + Discard = 0, // stop evaluation + Override = 1, // override correlation coef. to have cov.matrix pos.def and continue + OverrideAndFlag = 2 // override correlation coef. to have cov.matrix pos.def, set mPropFailed flag of corresponding candidate to true and continue (up to the user to check the flag) + }; + + enum FitStatus : uint8_t { // fit status of crossing hypothesis + None, // no status set (should not be possible!) + + /* Good Conditions */ + Converged, // fit converged + MaxIter, // max iterations reached before fit convergence + + /* Error Conditions */ + NoCrossing, // no reasaonable crossing was found + RejRadius, // radius of crossing was not acceptable + RejTrackX, // one candidate track x was below the mimimum required radius + RejTrackRoughZ, // rejected by rough cut on tracks Z difference + RejChi2Max, // rejected by maximum chi2 cut + FailProp, // propagation of at least prong to PCA failed + FailInvCov, // inversion of cov.-matrix failed + FailInvWeight, // inversion of Ti weight matrix failed + FailInv2ndDeriv, // inversion of 2nd derivatives failed + FailCorrTracks, // correction of tracks to updated x failed + FailCloserAlt, // alternative PCA is closer + // + NStatusesDefined + }; + static constexpr int getNProngs() { return N; } DCAFitterN() = default; @@ -105,29 +167,37 @@ class DCAFitterN //========================================================================= ///< return PCA candidate, by default best on is provided (no check for the index validity) - const Vec3D& getPCACandidate(int cand = 0) const { return mPCA[mOrder[cand]]; } - const auto getPCACandidatePos(int cand = 0) const + GPUd() const Vec3D& getPCACandidate(int cand = 0) const { return mPCA[mOrder[cand]]; } + GPUd() const auto getPCACandidatePos(int cand = 0) const { const auto& vd = mPCA[mOrder[cand]]; - return std::array{float(vd[0]), float(vd[1]), float(vd[2])}; + return std::array{static_cast(vd[0]), static_cast(vd[1]), static_cast(vd[2])}; } + ///< return position of quality-ordered candidate in the internal structures + int getCandidatePosition(int cand = 0) const { return mOrder[cand]; } + ///< return Chi2 at PCA candidate (no check for its validity) float getChi2AtPCACandidate(int cand = 0) const { return mChi2[mOrder[cand]]; } ///< prepare copies of tracks at the V0 candidate (no check for the candidate validity) /// must be called before getTrack(i,cand) query - bool propagateTracksToVertex(int cand = 0); + GPUd() bool propagateTracksToVertex(int cand = 0); ///< check if propagation of tracks to candidate vertex was done - bool isPropagateTracksToVertexDone(int cand = 0) const { return mTrPropDone[mOrder[cand]]; } + GPUd() bool isPropagateTracksToVertexDone(int cand = 0) const { return mTrPropDone[mOrder[cand]]; } + + ///< check if propagation of tracks to candidate vertex failed + bool isPropagationFailure(int cand = 0) const { return mPropFailed[mOrder[cand]]; } ///< track param propagated to V0 candidate (no check for the candidate validity) /// propagateTracksToVertex must be called in advance Track& getTrack(int i, int cand = 0) { if (!mTrPropDone[mOrder[cand]]) { +#ifndef GPUCA_GPUCODE_DEVICE throw std::runtime_error("propagateTracksToVertex was not called yet"); +#endif } return mCandTr[mOrder[cand]][i]; } @@ -135,107 +205,117 @@ class DCAFitterN const Track& getTrack(int i, int cand = 0) const { if (!mTrPropDone[mOrder[cand]]) { +#ifndef GPUCA_GPUCODE_DEVICE throw std::runtime_error("propagateTracksToVertex was not called yet"); +#endif } return mCandTr[mOrder[cand]][i]; } ///< create parent track param with errors for decay vertex - o2::track::TrackParCov createParentTrackParCov(int cand = 0, bool sectorAlpha = true) const; + GPUd() o2::track::TrackParCov createParentTrackParCov(int cand = 0, bool sectorAlpha = true) const; ///< create parent track param w/o errors for decay vertex - o2::track::TrackPar createParentTrackPar(int cand = 0, bool sectorAlpha = true) const; + GPUd() o2::track::TrackPar createParentTrackPar(int cand = 0, bool sectorAlpha = true) const; ///< calculate on the fly track param (no cov mat) at candidate, check isValid to make sure propagation was successful - o2::track::TrackPar getTrackParamAtPCA(int i, int cand = 0) const; + GPUd() o2::track::TrackPar getTrackParamAtPCA(int i, int cand = 0); ///< recalculate PCA as a cov-matrix weighted mean, even if absDCA method was used - bool recalculatePCAWithErrors(int cand = 0); + GPUd() bool recalculatePCAWithErrors(int cand = 0); - MatSym3D calcPCACovMatrix(int cand = 0) const; + GPUd() double calcCollinearInflation(int cand) const; + GPUd() MatSym3D calcPCACovMatrix(int cand = 0) const; std::array calcPCACovMatrixFlat(int cand = 0) const { auto m = calcPCACovMatrix(cand); - return {float(m(0, 0)), float(m(1, 0)), float(m(1, 1)), float(m(2, 0)), float(m(2, 1)), float(m(2, 2))}; + return {static_cast(m(0, 0)), static_cast(m(1, 0)), static_cast(m(1, 1)), static_cast(m(2, 0)), static_cast(m(2, 1)), static_cast(m(2, 2))}; } const Track* getOrigTrackPtr(int i) const { return mOrigTrPtr[i]; } + GPUdi() FitStatus getFitStatus(int cand = 0) const noexcept { return mFitStatus[mOrder[cand]]; } + ///< return number of iterations during minimization (no check for its validity) - int getNIterations(int cand = 0) const { return mNIters[mOrder[cand]]; } - void setPropagateToPCA(bool v = true) { mPropagateToPCA = v; } - void setMaxIter(int n = 20) { mMaxIter = n > 2 ? n : 2; } - void setMaxR(float r = 200.) { mMaxR2 = r * r; } - void setMaxDZIni(float d = 4.) { mMaxDZIni = d; } - void setMaxDXYIni(float d = 4.) { mMaxDXYIni = d > 0 ? d : 1e9; } - void setMaxChi2(float chi2 = 999.) { mMaxChi2 = chi2; } - void setBz(float bz) { mBz = std::abs(bz) > o2::constants::math::Almost0 ? bz : 0.f; } - void setMinParamChange(float x = 1e-3) { mMinParamChange = x > 1e-4 ? x : 1.e-4; } - void setMinRelChi2Change(float r = 0.9) { mMinRelChi2Change = r > 0.1 ? r : 999.; } - void setUseAbsDCA(bool v) { mUseAbsDCA = v; } - void setWeightedFinalPCA(bool v) { mWeightedFinalPCA = v; } - void setMaxDistance2ToMerge(float v) { mMaxDist2ToMergeSeeds = v; } - void setMatCorrType(o2::base::Propagator::MatCorrType m = o2::base::Propagator::MatCorrType::USEMatCorrLUT) { mMatCorr = m; } - void setUsePropagator(bool v) { mUsePropagator = v; } - void setRefitWithMatCorr(bool v) { mRefitWithMatCorr = v; } - void setMaxSnp(float s) { mMaxSnp = s; } - void setMaxStep(float s) { mMaxStep = s; } - void setMinXSeed(float x) { mMinXSeed = x; } - - int getNCandidates() const { return mCurHyp; } - int getMaxIter() const { return mMaxIter; } - float getMaxR() const { return std::sqrt(mMaxR2); } - float getMaxDZIni() const { return mMaxDZIni; } - float getMaxDXYIni() const { return mMaxDXYIni; } - float getMaxChi2() const { return mMaxChi2; } - float getMinParamChange() const { return mMinParamChange; } - float getBz() const { return mBz; } - float getMaxDistance2ToMerge() const { return mMaxDist2ToMergeSeeds; } - bool getUseAbsDCA() const { return mUseAbsDCA; } - bool getWeightedFinalPCA() const { return mWeightedFinalPCA; } - bool getPropagateToPCA() const { return mPropagateToPCA; } - o2::base::Propagator::MatCorrType getMatCorrType() const { return mMatCorr; } - bool getUsePropagator() const { return mUsePropagator; } - bool getRefitWithMatCorr() const { return mRefitWithMatCorr; } - float getMaxSnp() const { return mMaxSnp; } - float getMasStep() const { return mMaxStep; } - float getMinXSeed() const { return mMinXSeed; } + GPUdi() int getNIterations(int cand = 0) const { return mNIters[mOrder[cand]]; } + GPUdi() void setPropagateToPCA(bool v = true) { mPropagateToPCA = v; } + GPUdi() void setMaxIter(int n = 20) { mMaxIter = n > 2 ? n : 2; } + GPUdi() void setMaxR(float r = 200.) { mMaxR2 = r * r; } + GPUdi() void setMaxDZIni(float d = 4.) { mMaxDZIni = d; } + GPUdi() void setMaxDXYIni(float d = 4.) { mMaxDXYIni = d > 0 ? d : 1e9; } + GPUdi() void setMaxChi2(float chi2 = 999.) { mMaxChi2 = chi2; } + GPUdi() void setBz(float bz) { mBz = o2::gpu::GPUCommonMath::Abs(bz) > o2::constants::math::Almost0 ? bz : 0.f; } + GPUdi() void setMinParamChange(float x = 1e-3) { mMinParamChange = x > 1e-4 ? x : 1.e-4; } + GPUdi() void setMinRelChi2Change(float r = 0.9) { mMinRelChi2Change = r > 0.1 ? r : 999.; } + GPUdi() void setUseAbsDCA(bool v) { mUseAbsDCA = v; } + GPUdi() void setWeightedFinalPCA(bool v) { mWeightedFinalPCA = v; } + GPUdi() void setMaxDistance2ToMerge(float v) { mMaxDist2ToMergeSeeds = v; } + GPUdi() void setMatCorrType(o2::base::Propagator::MatCorrType m = o2::base::Propagator::MatCorrType::USEMatCorrLUT) { mMatCorr = m; } + GPUdi() void setUsePropagator(bool v) { mUsePropagator = v; } + GPUdi() void setRefitWithMatCorr(bool v) { mRefitWithMatCorr = v; } + GPUdi() void setMaxSnp(float s) { mMaxSnp = s; } + GPUdi() void setMaxStep(float s) { mMaxStep = s; } + GPUdi() void setMinXSeed(float x) { mMinXSeed = x; } + GPUdi() void setCollinear(bool isCollinear) { mIsCollinear = isCollinear; } + + GPUdi() int getNCandidates() const { return mCurHyp; } + GPUdi() int getMaxIter() const { return mMaxIter; } + GPUdi() float getMaxR() const { return o2::gpu::GPUCommonMath::Sqrt(mMaxR2); } + GPUdi() float getMaxDZIni() const { return mMaxDZIni; } + GPUdi() float getMaxDXYIni() const { return mMaxDXYIni; } + GPUdi() float getMaxChi2() const { return mMaxChi2; } + GPUdi() float getMinParamChange() const { return mMinParamChange; } + GPUdi() float getBz() const { return mBz; } + GPUdi() float getMaxDistance2ToMerge() const { return mMaxDist2ToMergeSeeds; } + GPUdi() bool getUseAbsDCA() const { return mUseAbsDCA; } + GPUdi() bool getWeightedFinalPCA() const { return mWeightedFinalPCA; } + GPUdi() bool getPropagateToPCA() const { return mPropagateToPCA; } + GPUdi() o2::base::Propagator::MatCorrType getMatCorrType() const { return mMatCorr; } + GPUdi() bool getUsePropagator() const { return mUsePropagator; } + GPUdi() bool getRefitWithMatCorr() const { return mRefitWithMatCorr; } + GPUdi() float getMaxSnp() const { return mMaxSnp; } + GPUdi() float getMasStep() const { return mMaxStep; } + GPUdi() float getMinXSeed() const { return mMinXSeed; } template - int process(const Tr&... args); - void print() const; + GPUd() int process(const Tr&... args); + GPUd() void print() const; + + GPUdi() int getFitterID() const { return mFitterID; } + GPUdi() void setFitterID(int i) { mFitterID = i; } + GPUdi() size_t getCallID() const { return mCallID; } protected: - bool calcPCACoefs(); - bool calcInverseWeight(); - void calcResidDerivatives(); - void calcResidDerivativesNoErr(); - void calcRMatrices(); - void calcChi2Derivatives(); - void calcChi2DerivativesNoErr(); - void calcPCA(); - void calcPCANoErr(); - void calcTrackResiduals(); - void calcTrackDerivatives(); - double calcChi2() const; - double calcChi2NoErr() const; - bool correctTracks(const VecND& corrX); - bool minimizeChi2(); - bool minimizeChi2NoErr(); - bool roughDZCut() const; - bool closerToAlternative() const; - bool propagateToX(o2::track::TrackParCov& t, float x) const; - bool propagateParamToX(o2::track::TrackPar& t, float x) const; - - static double getAbsMax(const VecND& v); + GPUd() bool calcPCACoefs(); + GPUd() bool calcInverseWeight(); + GPUd() void calcResidDerivatives(); + GPUd() void calcResidDerivativesNoErr(); + GPUd() void calcRMatrices(); + GPUd() void calcChi2Derivatives(); + GPUd() void calcChi2DerivativesNoErr(); + GPUd() void calcPCA(); + GPUd() void calcPCANoErr(); + GPUd() void calcTrackResiduals(); + GPUd() void calcTrackDerivatives(); + GPUd() double calcChi2() const; + GPUd() double calcChi2NoErr() const; + GPUd() bool correctTracks(const VecND& corrX); + GPUd() bool minimizeChi2(); + GPUd() bool minimizeChi2NoErr(); + GPUd() bool roughDZCut() const; + GPUd() bool closerToAlternative() const; + GPUd() bool propagateToX(o2::track::TrackParCov& t, float x); + GPUd() bool propagateParamToX(o2::track::TrackPar& t, float x); + + GPUd() static double getAbsMax(const VecND& v); ///< track param positions at V0 candidate (no check for the candidate validity) - const Vec3D& getTrackPos(int i, int cand = 0) const { return mTrPos[mOrder[cand]][i]; } + GPUdi() const Vec3D& getTrackPos(int i, int cand = 0) const { return mTrPos[mOrder[cand]][i]; } ///< track X-param at V0 candidate (no check for the candidate validity) - float getTrackX(int i, int cand = 0) const { return getTrackPos(i, cand)[0]; } + GPUd() float getTrackX(int i, int cand = 0) const { return getTrackPos(i, cand)[0]; } - MatStd3D getTrackRotMatrix(int i) const // generate 3D matrix for track rotation to global frame + GPUd() MatStd3D getTrackRotMatrix(int i) const // generate 3D matrix for track rotation to global frame { MatStd3D mat; mat(2, 2) = 1; @@ -245,39 +325,46 @@ class DCAFitterN return mat; } - MatSym3D getTrackCovMatrix(int i, int cand = 0) const // generate covariance matrix of track position, adding fake X error - { - const auto& trc = mCandTr[mOrder[cand]][i]; - MatSym3D mat; - mat(0, 0) = trc.getSigmaY2() * XerrFactor; - mat(1, 1) = trc.getSigmaY2(); - mat(2, 2) = trc.getSigmaZ2(); - mat(2, 1) = trc.getSigmaZY(); - return mat; - } - - void assign(int) {} + GPUd() void assign(int) {} template - void assign(int i, const T& t, const Tr&... args) + GPUd() void assign(int i, const T& t, const Tr&... args) { +#ifndef GPUCA_GPUCODE_DEVICE static_assert(std::is_convertible(), "Wrong track type"); +#endif mOrigTrPtr[i] = &t; assign(i + 1, args...); } - void clear() + GPUdi() void clear() { mCurHyp = 0; mAllowAltPreference = true; + mOrder.fill(0); + mPropFailed.fill(false); + mTrPropDone.fill(false); + mNIters.fill(0); + mChi2.fill(-1); + mFitStatus.fill(FitStatus::None); } - static void setTrackPos(Vec3D& pnt, const Track& tr) + GPUdi() static void setTrackPos(Vec3D& pnt, const Track& tr) { pnt[0] = tr.getX(); pnt[1] = tr.getY(); pnt[2] = tr.getZ(); } + GPUdi() void clearLogThrottlers() + { + mLoggerBadCov.clear(); + mLoggerBadInv.clear(); + mLoggerBadProp.clear(); + } + + void setBadCovPolicy(BadCovPolicy v) { mBadCovPolicy = v; } + BadCovPolicy getBadCovPolicy() const { return mBadCovPolicy; } + private: // vectors of 1st derivatives of track local residuals over X parameters std::array, N> mDResidDx; @@ -301,18 +388,26 @@ class DCAFitterN std::array mPCA; // PCA for each vertex candidate std::array mChi2 = {0}; // Chi2 at PCA candidate std::array mNIters; // number of iterations for each seed - std::array mTrPropDone; // Flag that the tracks are fully propagated to PCA - MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T + std::array mTrPropDone{}; // Flag that the tracks are fully propagated to PCA + std::array mPropFailed{}; // Flag that some propagation failed for this PCA candidate + mutable LogLogThrottler mLoggerBadCov{}; + mutable LogLogThrottler mLoggerBadInv{}; + mutable LogLogThrottler mLoggerBadProp{}; + mutable LogLogThrottler mLoggerBadPCACov{}; + MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T std::array mOrder{0}; int mCurHyp = 0; int mCrossIDCur = 0; int mCrossIDAlt = -1; + BadCovPolicy mBadCovPolicy{BadCovPolicy::Discard}; // what to do in case of non-pos-def. cov. matrix, see BadCovPolicy enum + std::array mFitStatus{}; // fit status of each hypothesis fit bool mAllowAltPreference = true; // if the fit converges to alternative PCA seed, abandon the current one bool mUseAbsDCA = false; // use abs. distance minimization rather than chi2 bool mWeightedFinalPCA = false; // recalculate PCA as a cov-matrix weighted mean, even if absDCA method was used bool mPropagateToPCA = true; // create tracks version propagated to PCA bool mUsePropagator = false; // use propagator with 3D B-field, set automatically if material correction is requested bool mRefitWithMatCorr = false; // when doing propagateTracksToVertex, propagate tracks to V0 with material corrections and rerun minimization again + bool mIsCollinear = false; // use collinear fits when there 2 crossing points o2::base::Propagator::MatCorrType mMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; // material corrections type int mMaxIter = 20; // max number of iterations float mBz = 0; // bz field, to be set by user @@ -326,24 +421,27 @@ class DCAFitterN float mMaxDist2ToMergeSeeds = 1.; // merge 2 seeds to their average if their distance^2 is below the threshold float mMaxSnp = 0.95; // Max snp for propagation with Propagator float mMaxStep = 2.0; // Max step for propagation with Propagator - - ClassDefNV(DCAFitterN, 1); + int mFitterID = 0; // locat fitter ID (mostly for debugging) + size_t mCallID = 0; + ClassDefNV(DCAFitterN, 3); }; ///_________________________________________________________________________ template template -int DCAFitterN::process(const Tr&... args) +GPUd() int DCAFitterN::process(const Tr&... args) { // This is a main entry point: fit PCA of N tracks + mCallID++; static_assert(sizeof...(args) == N, "incorrect number of input tracks"); assign(0, args...); clear(); for (int i = 0; i < N; i++) { mTrAux[i].set(*mOrigTrPtr[i], mBz); } - if (!mCrossings.set(mTrAux[0], *mOrigTrPtr[0], mTrAux[1], *mOrigTrPtr[1], mMaxDXYIni)) { // even for N>2 it should be enough to test just 1 loop - return 0; // no crossing + if (!mCrossings.set(mTrAux[0], *mOrigTrPtr[0], mTrAux[1], *mOrigTrPtr[1], mMaxDXYIni, mIsCollinear)) { // even for N>2 it should be enough to test just 1 loop + mFitStatus[mCurHyp] = FitStatus::NoCrossing; + return 0; } if (mUseAbsDCA) { calcRMatrices(); // needed for fast residuals derivatives calculation in case of abs. distance minimization @@ -361,13 +459,11 @@ int DCAFitterN::process(const Tr&... args) for (int ic = 0; ic < mCrossings.nDCA; ic++) { // check if radius is acceptable if (mCrossings.xDCA[ic] * mCrossings.xDCA[ic] + mCrossings.yDCA[ic] * mCrossings.yDCA[ic] > mMaxR2) { + mFitStatus[mCurHyp] = FitStatus::RejRadius; continue; } mCrossIDCur = ic; mCrossIDAlt = (mCrossings.nDCA == 2 && mAllowAltPreference) ? 1 - ic : -1; // works for max 2 crossings - mNIters[mCurHyp] = 0; - mTrPropDone[mCurHyp] = false; - mChi2[mCurHyp] = -1.; mPCA[mCurHyp][0] = mCrossings.xDCA[ic]; mPCA[mCurHyp][1] = mCrossings.yDCA[ic]; @@ -383,7 +479,7 @@ int DCAFitterN::process(const Tr&... args) for (int i = mCurHyp; i--;) { // order in quality for (int j = i; j--;) { if (mChi2[mOrder[i]] < mChi2[mOrder[j]]) { - std::swap(mOrder[i], mOrder[j]); + o2::gpu::GPUCommonMath::Swap(mOrder[i], mOrder[j]); } } } @@ -392,29 +488,29 @@ int DCAFitterN::process(const Tr&... args) recalculatePCAWithErrors(i); } } - return mCurHyp; } //__________________________________________________________________________ template -bool DCAFitterN::calcPCACoefs() +GPUd() bool DCAFitterN::calcPCACoefs() { //< calculate Ti matrices for global vertex decomposition to V = sum_{0::calcPCACoefs() //__________________________________________________________________________ template -bool DCAFitterN::calcInverseWeight() +GPUd() bool DCAFitterN::calcInverseWeight() { //< calculate [sum_{0::calcInverseWeight() for (int i = N; i--;) { const auto& taux = mTrAux[i]; const auto& tcov = mTrcEInv[mCurHyp][i]; - arrmat[XX] += taux.cc * tcov.sxx + taux.ss * tcov.syy; - arrmat[XY] += taux.cs * (tcov.sxx - tcov.syy); - arrmat[XZ] += -taux.s * tcov.syz; - arrmat[YY] += taux.cc * tcov.syy + taux.ss * tcov.sxx; - arrmat[YZ] += taux.c * tcov.syz; + arrmat[XX] += taux.cc * tcov.sxx - 2. * taux.cs * tcov.sxy + taux.ss * tcov.syy; + arrmat[XY] += taux.cs * (tcov.sxx - tcov.syy) + (taux.cc - taux.ss) * tcov.sxy; + arrmat[XZ] += taux.c * tcov.sxz - taux.s * tcov.syz; + arrmat[YY] += taux.ss * tcov.sxx + 2. * taux.cs * tcov.sxy + taux.cc * tcov.syy; + arrmat[YZ] += taux.s * tcov.sxz + taux.c * tcov.syz; arrmat[ZZ] += tcov.szz; } // invert 3x3 symmetrix matrix @@ -451,7 +547,7 @@ bool DCAFitterN::calcInverseWeight() //__________________________________________________________________________ template -void DCAFitterN::calcResidDerivatives() +GPUd() void DCAFitterN::calcResidDerivatives() { //< calculate matrix of derivatives for weighted chi2: residual i vs parameter X of track j MatStd3D matMT; @@ -492,12 +588,12 @@ void DCAFitterN::calcResidDerivatives() dr2[2] += trDx.d2zdx2; } } // track over which we differentiate - } // residual being differentiated + } // residual being differentiated } //__________________________________________________________________________ template -void DCAFitterN::calcResidDerivativesNoErr() +GPUd() void DCAFitterN::calcResidDerivativesNoErr() { //< calculate matrix of derivatives for absolute distance chi2: residual i vs parameter X of track j constexpr double NInv1 = 1. - NInv; // profit from Rii = I/Ninv @@ -542,12 +638,12 @@ void DCAFitterN::calcResidDerivativesNoErr() dr2ji[2] = -trDxi.d2zdx2 * NInv; } // track over which we differentiate - } // residual being differentiated + } // residual being differentiated } //__________________________________________________________________________ template -void DCAFitterN::calcRMatrices() +GPUd() void DCAFitterN::calcRMatrices() { //< calculate Rij = 1/N M_i^T * M_j matrices (rotation from j-th track to i-th track frame) for (int i = N; i--;) { @@ -562,7 +658,7 @@ void DCAFitterN::calcRMatrices() //__________________________________________________________________________ template -void DCAFitterN::calcChi2Derivatives() +GPUd() void DCAFitterN::calcChi2Derivatives() { //< calculate 1st and 2nd derivatives of wighted DCA (chi2) over track parameters X, see EQ.Chi2 in the ref std::array, N> covIDrDx; // tempory vectors of covI_j * dres_j/dx_i @@ -576,11 +672,11 @@ void DCAFitterN::calcChi2Derivatives() const auto& covI = mTrcEInv[mCurHyp][j]; // inverse cov matrix of track j const auto& dr1 = mDResidDx[j][i]; // vector of j-th residuals 1st derivative over X param of track i auto& cidr = covIDrDx[i][j]; // vector covI_j * dres_j/dx_i, save for 2nd derivative calculation - cidr[0] = covI.sxx * dr1[0]; - cidr[1] = covI.syy * dr1[1] + covI.syz * dr1[2]; - cidr[2] = covI.syz * dr1[1] + covI.szz * dr1[2]; + cidr[0] = covI.sxx * dr1[0] + covI.sxy * dr1[1] + covI.sxz * dr1[2]; + cidr[1] = covI.sxy * dr1[0] + covI.syy * dr1[1] + covI.syz * dr1[2]; + cidr[2] = covI.sxz * dr1[0] + covI.syz * dr1[1] + covI.szz * dr1[2]; // calculate res_i * covI_j * dres_j/dx_i - dchi1 += ROOT::Math::Dot(res, cidr); + dchi1 += o2::math_utils::Dot(res, cidr); } } // chi2 2nd derivative @@ -591,12 +687,14 @@ void DCAFitterN::calcChi2Derivatives() for (int k = N; k--;) { const auto& dr1j = mDResidDx[k][j]; // vector of k-th residuals 1st derivative over X param of track j const auto& cidrkj = covIDrDx[i][k]; // vector covI_k * dres_k/dx_i - dchi2 += ROOT::Math::Dot(dr1j, cidrkj); - if (k == j) { + dchi2 += o2::math_utils::Dot(dr1j, cidrkj); + if (i == j) { const auto& res = mTrRes[mCurHyp][k]; // vector of residuals of track k const auto& covI = mTrcEInv[mCurHyp][k]; // inverse cov matrix of track k - const auto& dr2ij = mD2ResidDx2[k][j]; // vector of k-th residuals 2nd derivative over X params of track j - dchi2 += res[0] * covI.sxx * dr2ij[0] + res[1] * (covI.syy * dr2ij[1] + covI.syz * dr2ij[2]) + res[2] * (covI.syz * dr2ij[1] + covI.szz * dr2ij[2]); + const auto& dr2ij = mD2ResidDx2[k][i]; // vector of k-th residuals 2nd derivative over X param i + dchi2 += res[0] * (covI.sxx * dr2ij[0] + covI.sxy * dr2ij[1] + covI.sxz * dr2ij[2]) + + res[1] * (covI.sxy * dr2ij[0] + covI.syy * dr2ij[1] + covI.syz * dr2ij[2]) + + res[2] * (covI.sxz * dr2ij[0] + covI.syz * dr2ij[1] + covI.szz * dr2ij[2]); } } } @@ -605,22 +703,29 @@ void DCAFitterN::calcChi2Derivatives() //__________________________________________________________________________ template -void DCAFitterN::calcChi2DerivativesNoErr() +GPUd() void DCAFitterN::calcChi2DerivativesNoErr() { //< calculate 1st and 2nd derivatives of abs DCA (chi2) over track parameters X, see (6) in the ref for (int i = N; i--;) { auto& dchi1 = mDChi2Dx[i]; // DChi2/Dx_i = sum_j { res_j * Dres_j/Dx_i } dchi1 = 0; // chi2 1st derivative - for (int j = N; j--;) { - const auto& res = mTrRes[mCurHyp][j]; // vector of residuals of track j - const auto& dr1 = mDResidDx[j][i]; // vector of j-th residuals 1st derivative over X param of track i - dchi1 += ROOT::Math::Dot(res, dr1); - if (i >= j) { // symmetrix matrix - // chi2 2nd derivative - auto& dchi2 = mD2Chi2Dx2[i][j]; // D2Chi2/Dx_i/Dx_j = sum_k { Dres_k/Dx_j * covI_k * Dres_k/Dx_i + res_k * covI_k * D2res_k/Dx_i/Dx_j } - dchi2 = ROOT::Math::Dot(mTrRes[mCurHyp][i], mD2ResidDx2[i][j]); - for (int k = N; k--;) { - dchi2 += ROOT::Math::Dot(mDResidDx[k][i], mDResidDx[k][j]); + for (int k = N; k--;) { + const auto& res = mTrRes[mCurHyp][k]; // vector of residuals of track k + const auto& dr1 = mDResidDx[k][i]; // vector of k-th residuals 1st derivative over X param of track i + dchi1 += o2::math_utils::Dot(res, dr1); + } + } + for (int i = N; i--;) { + for (int j = i + 1; j--;) { + auto& dchi2 = mD2Chi2Dx2[i][j]; + dchi2 = 0.; + for (int k = N; k--;) { + // Gauss-Newton term, present for diagonal and mixed elements. + dchi2 += o2::math_utils::Dot(mDResidDx[k][i], mDResidDx[k][j]); + // A trajectory has a second derivative only with respect to its own + // X parameter, hence the curvature term contributes only to H_ii. + if (i == j) { + dchi2 += o2::math_utils::Dot(mTrRes[mCurHyp][k], mD2ResidDx2[k][i]); } } } @@ -629,7 +734,7 @@ void DCAFitterN::calcChi2DerivativesNoErr() //___________________________________________________________________ template -void DCAFitterN::calcPCA() +GPUd() void DCAFitterN::calcPCA() { // calculate point of closest approach for N prongs mPCA[mCurHyp] = mTrCFVT[mCurHyp][N - 1] * mTrPos[mCurHyp][N - 1]; @@ -640,7 +745,7 @@ void DCAFitterN::calcPCA() //___________________________________________________________________ template -bool DCAFitterN::recalculatePCAWithErrors(int cand) +GPUd() bool DCAFitterN::recalculatePCAWithErrors(int cand) { // recalculate PCA as a cov-matrix weighted mean, even if absDCA method was used if (isPropagateTracksToVertexDone(cand) && !propagateTracksToVertex(cand)) { @@ -650,7 +755,23 @@ bool DCAFitterN::recalculatePCAWithErrors(int cand) mCurHyp = mOrder[cand]; if (mUseAbsDCA) { for (int i = N; i--;) { - mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor); // prepare inverse cov.matrices at starting point + if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i])) { // prepare inverse cov.matrices at starting point + if (mLoggerBadCov.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): overrode invalid track covariance from %s\n", + mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].asString().c_str()); +#else + printf("fitter %d: error (%ld muted): overrode invalid track covariance cyy:%e czz:%e cyz:%e\n", + mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].getSigmaY2(), mCandTr[mCurHyp][i].getSigmaZ2(), mCandTr[mCurHyp][i].getSigmaZY()); +#endif + } + mFitStatus[mCurHyp] = FitStatus::FailInvCov; + if (mBadCovPolicy == Discard) { + return false; + } else if (mBadCovPolicy == OverrideAndFlag) { + mPropFailed[mCurHyp] = true; + } // otherwise, just use overridden errors w/o flagging + } } if (!calcPCACoefs()) { mCurHyp = saveCurHyp; @@ -665,7 +786,7 @@ bool DCAFitterN::recalculatePCAWithErrors(int cand) //___________________________________________________________________ template -void DCAFitterN::calcPCANoErr() +GPUd() void DCAFitterN::calcPCANoErr() { // calculate point of closest approach for N prongs w/o errors auto& pca = mPCA[mCurHyp]; @@ -687,34 +808,111 @@ void DCAFitterN::calcPCANoErr() //___________________________________________________________________ template -ROOT::Math::SMatrix> DCAFitterN::calcPCACovMatrix(int cand) const +GPUd() double DCAFitterN::calcCollinearInflation(int cand) const { - // calculate covariance matrix for the point of closest approach - MatSym3D covm; - int nAdded = 0; - for (int i = N; i--;) { // calculate sum of inverses - // MatSym3D covTr = ROOT::Math::Similarity(mUseAbsDCA ? getTrackRotMatrix(i) : mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)); - // RS by using Similarity(mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)) we underestimate the error, use simple rotation - MatSym3D covTr = ROOT::Math::Similarity(getTrackRotMatrix(i), getTrackCovMatrix(i, cand)); - if (covTr.Invert()) { - covm += covTr; - nAdded++; + std::array, N> u{}; + int nu = 0; + + for (int i = 0; i < N; ++i) { + std::array p{}; + if (!getTrack(i, cand).getPxPyPzGlo(p)) { + continue; + } + const double p2 = p[0] * p[0] + p[1] * p[1] + p[2] * p[2]; + if (p2 <= 0.) { + continue; + } + const double pI = 1. / std::sqrt(p2); + u[nu++] = {p[0] * pI, p[1] * pI, p[2] * pI}; + } + + if (nu < 2) { + return 1.; + } + + double sin2Mean = 0.; + int npairs = 0; + for (int i = 0; i < nu; ++i) { + for (int j = i + 1; j < nu; ++j) { + double cij = u[i][0] * u[j][0] + u[i][1] * u[j][1] + u[i][2] * u[j][2]; + cij = std::clamp(cij, -1., 1.); + sin2Mean += std::max(0., 1. - cij * cij); + ++npairs; } } - if (nAdded && covm.Invert()) { - return covm; + sin2Mean /= npairs; + + constexpr double Sin2Ref = 1.e-5; + constexpr double MaxInflation = 1.e4; + if (sin2Mean <= 0.) { + return MaxInflation; } - // correct way has failed, use simple sum - MatSym3D covmSum; + return sin2Mean < Sin2Ref ? std::min(MaxInflation, Sin2Ref / sin2Mean) : 1.; +} + +//___________________________________________________________________ +template +GPUd() o2::math_utils::SMatrix> DCAFitterN::calcPCACovMatrix(int cand) const +{ + // Each track measures Y and Z at the vertex X. With the local slopes + // sy = dY/dX and sz = dZ/dX, its vertex measurement matrix is + // H = {{-sy, 1, 0}, {-sz, 0, 1}}. The longitudinal information must come + // from the track geometry, not from a dummy X variance. + MatSym3D info; + auto* arrmat = info.Array(); + memset(arrmat, 0, sizeof(info)); + enum { XX, + XY, + YY, + XZ, + YZ, + ZZ }; + const int ord = mOrder[cand]; for (int i = N; i--;) { - MatSym3D covTr = ROOT::Math::Similarity(getTrackRotMatrix(i), getTrackCovMatrix(i, cand)); + const auto& taux = mTrAux[i]; + TrackCovI tcov; + tcov.set(mCandTr[ord][i]); + arrmat[XX] += taux.cc * tcov.sxx - 2. * taux.cs * tcov.sxy + taux.ss * tcov.syy; + arrmat[XY] += taux.cs * (tcov.sxx - tcov.syy) + (taux.cc - taux.ss) * tcov.sxy; + arrmat[XZ] += taux.c * tcov.sxz - taux.s * tcov.syz; + arrmat[YY] += taux.ss * tcov.sxx + 2. * taux.cs * tcov.sxy + taux.cc * tcov.syy; + arrmat[YZ] += taux.s * tcov.sxz + taux.c * tcov.syz; + arrmat[ZZ] += tcov.szz; + } + const double maxDiag = o2::gpu::GPUCommonMath::Max(o2::gpu::GPUCommonMath::Max(info(0, 0), info(1, 1)), info(2, 2)); + const double det2 = info(0, 0) * info(1, 1) - info(1, 0) * info(1, 0); + const double det3 = info(0, 0) * (info(1, 1) * info(2, 2) - info(2, 1) * info(2, 1)) - + info(1, 0) * (info(1, 0) * info(2, 2) - info(2, 1) * info(2, 0)) + + info(2, 0) * (info(1, 0) * info(2, 1) - info(1, 1) * info(2, 0)); + constexpr double MinRelDet = 1.e-12; + constexpr double InflateRelDet = 1.e-6; + constexpr double MaxInflation = 1.e4; + const bool isWellConditionedInfo = maxDiag > 0. && info(0, 0) > 0. && det2 > 0. && det3 > MinRelDet * maxDiag * maxDiag * maxDiag; + if (isWellConditionedInfo) { + auto cov = info; + if (cov.Invert() && cov(0, 0) > 0. && cov(1, 1) > 0. && cov(2, 2) > 0.) { + // if (mIsCollinear) { + // cov *= calcCollinearInflation(cand); + // } + return cov; + } + } + if (mLoggerBadPCACov.needToLog()) { + printf("fitter %d: error (%ld muted): override ill-conditioned PCACovMatrix by dummy matrix", mFitterID, mLoggerBadPCACov.evCount); } - return covmSum; + // Fall back on a deliberately loose vertex covariance. Returning a tight + // identity covariance for a singular or ill-conditioned information matrix + // would shrink the uncertainty in the weakly constrained direction. + memset(arrmat, 0, sizeof(info)); + info(0, 0) = 4.; + info(1, 1) = 4.; + info(2, 2) = 4.; + return info; } //___________________________________________________________________ template -void DCAFitterN::calcTrackResiduals() +GPUd() void DCAFitterN::calcTrackResiduals() { // calculate residuals Vec3D vtxLoc; @@ -728,7 +926,7 @@ void DCAFitterN::calcTrackResiduals() //___________________________________________________________________ template -inline void DCAFitterN::calcTrackDerivatives() +GPUdi() void DCAFitterN::calcTrackDerivatives() { // calculate track derivatives over X param for (int i = N; i--;) { @@ -738,21 +936,22 @@ inline void DCAFitterN::calcTrackDerivatives() //___________________________________________________________________ template -inline double DCAFitterN::calcChi2() const +GPUdi() double DCAFitterN::calcChi2() const { // calculate current chi2 double chi2 = 0; for (int i = N; i--;) { const auto& res = mTrRes[mCurHyp][i]; const auto& covI = mTrcEInv[mCurHyp][i]; - chi2 += res[0] * res[0] * covI.sxx + res[1] * res[1] * covI.syy + res[2] * res[2] * covI.szz + 2. * res[1] * res[2] * covI.syz; + chi2 += res[0] * res[0] * covI.sxx + res[1] * res[1] * covI.syy + res[2] * res[2] * covI.szz + + 2. * (res[0] * res[1] * covI.sxy + res[0] * res[2] * covI.sxz + res[1] * res[2] * covI.syz); } return chi2; } //___________________________________________________________________ template -inline double DCAFitterN::calcChi2NoErr() const +GPUdi() double DCAFitterN::calcChi2NoErr() const { // calculate current chi2 of abs. distance minimization double chi2 = 0; @@ -765,22 +964,35 @@ inline double DCAFitterN::calcChi2NoErr() const //___________________________________________________________________ template -bool DCAFitterN::correctTracks(const VecND& corrX) +GPUd() bool DCAFitterN::correctTracks(const VecND& corrX) { - // propagate tracks to updated X + // Propagate the actual candidate tracks to the updated X. Use the analytic + // constant-Bz transport here: Newton corrections are small, but the track + // state must stay synchronized with mTrPos for the next derivative update. for (int i = N; i--;) { - const auto& trDer = mTrDer[mCurHyp][i]; - auto dx2h = 0.5 * corrX[i] * corrX[i]; - mTrPos[mCurHyp][i][0] -= corrX[i]; - mTrPos[mCurHyp][i][1] -= trDer.dydx * corrX[i] - dx2h * trDer.d2ydx2; - mTrPos[mCurHyp][i][2] -= trDer.dzdx * corrX[i] - dx2h * trDer.d2zdx2; + /* + // Updating only mTrPos by Taylor expansion leaves mCandTr at the previous X, + // leaving calcTrackDerivatives() insensitive to the update. Use full fast propagation instead. + const auto& trDer = mTrDer[mCurHyp][i]; + auto dx2h = 0.5 * corrX[i] * corrX[i]; + mTrPos[mCurHyp][i][0] -= corrX[i]; + mTrPos[mCurHyp][i][1] -= trDer.dydx * corrX[i] - dx2h * trDer.d2ydx2; + mTrPos[mCurHyp][i][2] -= trDer.dzdx * corrX[i] - dx2h * trDer.d2zdx2; + */ + auto& trc = mCandTr[mCurHyp][i]; + const float x = static_cast(mTrPos[mCurHyp][i][0] - corrX[i]); + const bool propagated = mUseAbsDCA ? trc.propagateParamTo(x, mBz) : trc.propagateTo(x, mBz); + if (!propagated) { + return false; + } + setTrackPos(mTrPos[mCurHyp][i], trc); } return true; } //___________________________________________________________________ template -bool DCAFitterN::propagateTracksToVertex(int icand) +GPUd() bool DCAFitterN::propagateTracksToVertex(int icand) { // propagate tracks to current vertex int ord = mOrder[icand]; @@ -817,7 +1029,7 @@ bool DCAFitterN::propagateTracksToVertex(int icand) //___________________________________________________________________ template -inline o2::track::TrackPar DCAFitterN::getTrackParamAtPCA(int i, int icand) const +GPUdi() o2::track::TrackPar DCAFitterN::getTrackParamAtPCA(int i, int icand) { // propagate tracks param only to current vertex (if not already done) int ord = mOrder[icand]; @@ -828,16 +1040,16 @@ inline o2::track::TrackPar DCAFitterN::getTrackParamAtPCA(int i, int trc.invalidate(); } } - return std::move(trc); + return trc; } //___________________________________________________________________ template -inline double DCAFitterN::getAbsMax(const VecND& v) +GPUdi() double DCAFitterN::getAbsMax(const VecND& v) { double mx = -1; for (int i = N; i--;) { - auto vai = std::abs(v[i]); + auto vai = o2::gpu::GPUCommonMath::Abs(v[i]); if (mx < vai) { mx = vai; } @@ -847,20 +1059,41 @@ inline double DCAFitterN::getAbsMax(const VecND& v) //___________________________________________________________________ template -bool DCAFitterN::minimizeChi2() +GPUd() bool DCAFitterN::minimizeChi2() { // find best chi2 (weighted DCA) of N tracks in the vicinity of the seed PCA for (int i = N; i--;) { mCandTr[mCurHyp][i] = *mOrigTrPtr[i]; auto x = mTrAux[i].c * mPCA[mCurHyp][0] + mTrAux[i].s * mPCA[mCurHyp][1]; // X of PCA in the track frame - if (x < mMinXSeed || !propagateToX(mCandTr[mCurHyp][i], x)) { + if (x < mMinXSeed) { + mFitStatus[mCurHyp] = FitStatus::RejTrackX; + return false; + } + if (!propagateToX(mCandTr[mCurHyp][i], x)) { return false; } - setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions - mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor); // prepare inverse cov.matrices at starting point + setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions + if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i])) { // prepare inverse cov.matrices at starting point + if (mLoggerBadCov.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): overrode invalid track covariance from %s\n", + mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].asString().c_str()); +#else + printf("fitter %d: error (%ld muted): overrode invalid track covariance cyy:%e czz:%e cyz:%e\n", + mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].getSigmaY2(), mCandTr[mCurHyp][i].getSigmaZ2(), mCandTr[mCurHyp][i].getSigmaZY()); +#endif + } + mFitStatus[mCurHyp] = FitStatus::FailInvCov; + if (mBadCovPolicy == Discard) { + return false; + } else if (mBadCovPolicy == OverrideAndFlag) { + mPropFailed[mCurHyp] = true; + } // otherwise, just use overridden errors w/o flagging + } } if (mMaxDZIni > 0 && !roughDZCut()) { // apply rough cut on tracks Z difference + mFitStatus[mCurHyp] = FitStatus::RejTrackRoughZ; return false; } @@ -877,15 +1110,20 @@ bool DCAFitterN::minimizeChi2() // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 if (!mD2Chi2Dx2.Invert()) { - LOG(error) << "InversionFailed"; + if (mLoggerBadInv.needToLog()) { + printf("fitter %d: error (%ld muted): Inversion failed\n", mFitterID, mLoggerBadCov.evCount); + } + mFitStatus[mCurHyp] = FitStatus::FailInv2ndDeriv; return false; } VecND dx = mD2Chi2Dx2 * mDChi2Dx; if (!correctTracks(dx)) { + mFitStatus[mCurHyp] = FitStatus::FailCorrTracks; return false; } calcPCA(); // updated PCA if (mCrossIDAlt >= 0 && closerToAlternative()) { + mFitStatus[mCurHyp] = FitStatus::FailCloserAlt; mAllowAltPreference = false; return false; } @@ -893,30 +1131,43 @@ bool DCAFitterN::minimizeChi2() chi2Upd = calcChi2(); // updated chi2 if (getAbsMax(dx) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) { chi2 = chi2Upd; + mFitStatus[mCurHyp] = FitStatus::Converged; break; // converged } chi2 = chi2Upd; } while (++mNIters[mCurHyp] < mMaxIter); + if (mNIters[mCurHyp] == mMaxIter) { + mFitStatus[mCurHyp] = FitStatus::MaxIter; + } // mChi2[mCurHyp] = chi2 * NInv; - return mChi2[mCurHyp] < mMaxChi2; + if (mChi2[mCurHyp] >= mMaxChi2) { + mFitStatus[mCurHyp] = FitStatus::RejChi2Max; + return false; + } + return true; } //___________________________________________________________________ template -bool DCAFitterN::minimizeChi2NoErr() +GPUd() bool DCAFitterN::minimizeChi2NoErr() { // find best chi2 (absolute DCA) of N tracks in the vicinity of the PCA seed for (int i = N; i--;) { mCandTr[mCurHyp][i] = *mOrigTrPtr[i]; auto x = mTrAux[i].c * mPCA[mCurHyp][0] + mTrAux[i].s * mPCA[mCurHyp][1]; // X of PCA in the track frame - if (x < mMinXSeed || !propagateParamToX(mCandTr[mCurHyp][i], x)) { + if (x < mMinXSeed) { + mFitStatus[mCurHyp] = FitStatus::RejTrackX; + return false; + } + if (!propagateParamToX(mCandTr[mCurHyp][i], x)) { return false; } setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions } if (mMaxDZIni > 0 && !roughDZCut()) { // apply rough cut on tracks Z difference + mFitStatus[mCurHyp] = FitStatus::RejTrackRoughZ; return false; } @@ -930,15 +1181,20 @@ bool DCAFitterN::minimizeChi2NoErr() // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 if (!mD2Chi2Dx2.Invert()) { - LOG(error) << "InversionFailed"; + if (mLoggerBadInv.needToLog()) { + printf("fitter %d: error (%ld muted): Inversion failed\n", mFitterID, mLoggerBadCov.evCount); + } + mFitStatus[mCurHyp] = FitStatus::FailInv2ndDeriv; return false; } VecND dx = mD2Chi2Dx2 * mDChi2Dx; if (!correctTracks(dx)) { + mFitStatus[mCurHyp] = FitStatus::FailCorrTracks; return false; } calcPCANoErr(); // updated PCA if (mCrossIDAlt >= 0 && closerToAlternative()) { + mFitStatus[mCurHyp] = FitStatus::FailCloserAlt; mAllowAltPreference = false; return false; } @@ -946,24 +1202,32 @@ bool DCAFitterN::minimizeChi2NoErr() chi2Upd = calcChi2NoErr(); // updated chi2 if (getAbsMax(dx) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) { chi2 = chi2Upd; + mFitStatus[mCurHyp] = FitStatus::Converged; break; // converged } chi2 = chi2Upd; } while (++mNIters[mCurHyp] < mMaxIter); + if (mNIters[mCurHyp] == mMaxIter) { + mFitStatus[mCurHyp] = FitStatus::MaxIter; + } // mChi2[mCurHyp] = chi2 * NInv; - return mChi2[mCurHyp] < mMaxChi2; + if (mChi2[mCurHyp] >= mMaxChi2) { + mFitStatus[mCurHyp] = FitStatus::RejChi2Max; + return false; + } + return true; } //___________________________________________________________________ template -bool DCAFitterN::roughDZCut() const +GPUd() bool DCAFitterN::roughDZCut() const { // apply rough cut on DZ between the tracks in the seed point bool accept = true; for (int i = N; accept && i--;) { for (int j = i; j--;) { - if (std::abs(mCandTr[mCurHyp][i].getZ() - mCandTr[mCurHyp][j].getZ()) > mMaxDZIni) { + if (o2::gpu::GPUCommonMath::Abs(mCandTr[mCurHyp][i].getZ() - mCandTr[mCurHyp][j].getZ()) > mMaxDZIni) { accept = false; break; } @@ -974,7 +1238,7 @@ bool DCAFitterN::roughDZCut() const //___________________________________________________________________ template -bool DCAFitterN::closerToAlternative() const +GPUd() bool DCAFitterN::closerToAlternative() const { // check if the point current PCA point is closer to the seeding XY point being tested or to alternative see (if any) auto dxCur = mPCA[mCurHyp][0] - mCrossings.xDCA[mCrossIDCur], dyCur = mPCA[mCurHyp][1] - mCrossings.yDCA[mCrossIDCur]; @@ -984,33 +1248,74 @@ bool DCAFitterN::closerToAlternative() const //___________________________________________________________________ template -void DCAFitterN::print() const +GPUd() void DCAFitterN::print() const { - LOG(info) << N << "-prong vertex fitter in " << (mUseAbsDCA ? "abs." : "weighted") << " distance minimization mode"; - LOG(info) << "Bz: " << mBz << " MaxIter: " << mMaxIter << " MaxChi2: " << mMaxChi2; +#ifndef GPUCA_GPUCODE_DEVICE + LOG(info) << N << "-prong vertex fitter in " << (mUseAbsDCA ? "abs." : "weighted") << " distance minimization mode, collinear tracks mode: " << (mIsCollinear ? "ON" : "OFF"); + LOG(info) << "Bz: " << mBz << " MaxIter: " << mMaxIter << " MaxChi2: " << mMaxChi2 << " MatCorrType: " << int(mMatCorr); LOG(info) << "Stopping condition: Max.param change < " << mMinParamChange << " Rel.Chi2 change > " << mMinRelChi2Change; LOG(info) << "Discard candidates for : Rvtx > " << getMaxR() << " DZ between tracks > " << mMaxDZIni; + LOG(info) << "PropagateToPCA:" << mPropagateToPCA << " WeightedFinalPCA:" << mWeightedFinalPCA << " UsePropagator:" << mUsePropagator << " RefitWithMatCorr:" << mRefitWithMatCorr; + std::string rep{}; + for (int i = 0; i < mCrossings.nDCA; i++) { + rep += fmt::format("seed{}:{}/{} ", i, mTrPropDone[i], mPropFailed[i]); + } + LOG(info) << "Last call: NCand:" << mCurHyp << " from " << mCrossings.nDCA << " seeds, prop.done/failed: " << rep; +#else + if (mUseAbsDCA) { + printf("%d-prong vertex fitter in abs. distance minimization mode\n", N); + } else { + printf("%d-prong vertex fitter in weighted distance minimization mode\n", N); + } + printf("Bz: %1.f MaxIter: %3.d MaxChi2: %2.3f\n", mBz, mMaxIter, mMaxChi2); + printf("Stopping condition: Max.param change < %2.3f Rel.Chi2 change > %2.3f\n", mMinParamChange, mMinRelChi2Change); + printf("Discard candidates for : Rvtx > %2.3f DZ between tracks > %2.3f\n", getMaxR(), mMaxDZIni); +#endif } //___________________________________________________________________ template -o2::track::TrackParCov DCAFitterN::createParentTrackParCov(int cand, bool sectorAlpha) const +GPUd() o2::track::TrackParCov DCAFitterN::createParentTrackParCov(int cand, bool sectorAlpha) const { - const auto& trP = getTrack(0, cand); - const auto& trN = getTrack(1, cand); std::array covV = {0.}; std::array pvecV = {0.}; int q = 0; for (int it = 0; it < N; it++) { const auto& trc = getTrack(it, cand); std::array pvecT = {0.}; - std::array covT = {0.}; - trc.getPxPyPzGlo(pvecT); - trc.getCovXYZPxPyPzGlo(covT); - constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component - for (int i = 0; i < 6; i++) { - covV[MomInd[i]] += covT[MomInd[i]]; + const bool hasMomentum = trc.getPxPyPzGlo(pvecT); + + // Propagate only the native momentum-parameter covariance + // (snp,tgl,q/pt) to the lab momentum covariance. The final constructor + // below rotates the summed lab covariance to the parent alpha frame. + if (hasMomentum) { + const double snp = trc.getSnp(); + const double csp = trc.getCsp(); + const double pt = trc.getPt(); + const double alpha = trc.getAlpha(); + double sna = 0., csa = 0.; + o2::math_utils::detail::sincos(alpha, sna, csa); + const double dPxdSnp = -pt * (snp * csa / csp + sna); + const double dPydSnp = pt * (csa - snp * sna / csp); + const double dPzdTgl = pt; + const double q2ptI = 1. / trc.getQ2Pt(); + const double dPxdQ = -pvecT[0] * q2ptI; + const double dPydQ = -pvecT[1] * q2ptI; + const double dPzdQ = -pvecT[2] * q2ptI; + const double cSnpSnp = trc.getSigmaSnp2(); + const double cTglSnp = trc.getSigmaTglSnp(); + const double cTglTgl = trc.getSigmaTgl2(); + const double cQSnp = trc.getSigma1PtSnp(); + const double cQTgl = trc.getSigma1PtTgl(); + const double cQQ = trc.getSigma1Pt2(); + covV[9] += dPxdSnp * dPxdSnp * cSnpSnp + 2. * dPxdSnp * dPxdQ * cQSnp + dPxdQ * dPxdQ * cQQ; + covV[13] += dPydSnp * (dPxdSnp * cSnpSnp + dPxdQ * cQSnp) + dPydQ * (dPxdSnp * cQSnp + dPxdQ * cQQ); + covV[14] += dPydSnp * dPydSnp * cSnpSnp + 2. * dPydSnp * dPydQ * cQSnp + dPydQ * dPydQ * cQQ; + covV[18] += dPzdTgl * (dPxdSnp * cTglSnp + dPxdQ * cQTgl) + dPzdQ * (dPxdSnp * cQSnp + dPxdQ * cQQ); + covV[19] += dPzdTgl * (dPydSnp * cTglSnp + dPydQ * cQTgl) + dPzdQ * (dPydSnp * cQSnp + dPydQ * cQQ); + covV[20] += dPzdTgl * dPzdTgl * cTglTgl + 2. * dPzdTgl * dPzdQ * cQTgl + dPzdQ * dPzdQ * cQQ; } + for (int i = 0; i < 3; i++) { pvecV[i] += pvecT[i]; } @@ -1023,12 +1328,12 @@ o2::track::TrackParCov DCAFitterN::createParentTrackParCov(int cand, covV[3] = covVtxV(2, 0); covV[4] = covVtxV(2, 1); covV[5] = covVtxV(2, 2); - return std::move(o2::track::TrackParCov(getPCACandidatePos(cand), pvecV, covV, q, sectorAlpha)); + return o2::track::TrackParCov(getPCACandidatePos(cand), pvecV, covV, q, sectorAlpha); } //___________________________________________________________________ template -o2::track::TrackPar DCAFitterN::createParentTrackPar(int cand, bool sectorAlpha) const +GPUd() o2::track::TrackPar DCAFitterN::createParentTrackPar(int cand, bool sectorAlpha) const { const auto& trP = getTrack(0, cand); const auto& trN = getTrack(1, cand); @@ -1045,34 +1350,76 @@ o2::track::TrackPar DCAFitterN::createParentTrackPar(int cand, bool q += trc.getCharge(); } const std::array vertex = {(float)wvtx[0], (float)wvtx[1], (float)wvtx[2]}; - return std::move(o2::track::TrackPar(vertex, pvecV, q, sectorAlpha)); + return o2::track::TrackPar(vertex, pvecV, q, sectorAlpha); } //___________________________________________________________________ template -inline bool DCAFitterN::propagateParamToX(o2::track::TrackPar& t, float x) const +GPUdi() bool DCAFitterN::propagateParamToX(o2::track::TrackPar& t, float x) { + bool res = true; if (mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) { - return o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); +#ifndef GPUCA_GPUCODE + res = o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); +#endif } else { - return t.propagateParamTo(x, mBz); + res = t.propagateParamTo(x, mBz); + } + if (!res) { + mFitStatus[mCurHyp] = FitStatus::FailProp; + mPropFailed[mCurHyp] = true; + if (mLoggerBadProp.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): propagation to %.4f failed for %s\n", mFitterID, mLoggerBadProp.evCount, x, t.asString().c_str()); +#else + printf("fitter %d: error (%ld muted): propagation to %.4f failed\n", mFitterID, mLoggerBadProp.evCount, x); +#endif + } } + return res; } //___________________________________________________________________ template -inline bool DCAFitterN::propagateToX(o2::track::TrackParCov& t, float x) const +GPUdi() bool DCAFitterN::propagateToX(o2::track::TrackParCov& t, float x) { + bool res = true; if (mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) { - return o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); +#ifndef GPUCA_GPUCODE + res = o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); +#endif } else { - return t.propagateTo(x, mBz); + res = t.propagateTo(x, mBz); } + if (!res) { + mFitStatus[mCurHyp] = FitStatus::FailProp; + mPropFailed[mCurHyp] = true; + if (mLoggerBadProp.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): propagation to %.4f failed for %s\n", mFitterID, mLoggerBadProp.evCount, x, t.asString().c_str()); +#else + printf("fitter %d: error (%ld muted): propagation to %.4f failed\n", mFitterID, mLoggerBadProp.evCount, x); +#endif + } + } + return res; } using DCAFitter2 = DCAFitterN<2, o2::track::TrackParCov>; using DCAFitter3 = DCAFitterN<3, o2::track::TrackParCov>; +namespace device +{ +template +void print(const int nBlocks, const int nThreads, Fitter& ft); + +template +int process(const int nBlocks, const int nThreads, Fitter&, Tr&... args); + +template +void processBulk(const int nBlocks, const int nThreads, const int nBatches, std::vector& fitters, std::vector& results, std::vector
&... args); +} // namespace device + } // namespace vertexing } // namespace o2 #endif // _ALICEO2_DCA_FITTERN_ diff --git a/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h b/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h index 7bcb7966d3fe1..d5bc6631575af 100644 --- a/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h @@ -20,8 +20,10 @@ #include "MathUtils/Cartesian.h" #include "ReconstructionDataFormats/TrackFwd.h" #include "ReconstructionDataFormats/Track.h" -#include "DCAFitter/HelixHelper.h" +#include "ReconstructionDataFormats/HelixHelper.h" #include +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" namespace o2 { @@ -158,6 +160,12 @@ class FwdDCAFitterN void setMinParamChange(float x = 1e-3) { mMinParamChange = x > 1e-4 ? x : 1.e-4; } void setMinRelChi2Change(float r = 0.9) { mMinRelChi2Change = r > 0.1 ? r : 999.; } void setUseAbsDCA(bool v) { mUseAbsDCA = v; } + void setMatLUT(const o2::base::MatLayerCylSet* m) + { + mMatLUT = m; + mUseMatBudget = true; + } + void setTGeoMat(bool v = true) { mTGeoFallBackAllowed = v; } void setMaxDistance2ToMerge(float v) { mMaxDist2ToMergeSeeds = v; } int getNCandidates() const { return mCurHyp; } @@ -202,6 +210,7 @@ class FwdDCAFitterN bool roughDXCut() const; bool closerToAlternative() const; static double getAbsMax(const VecND& v); + bool propagateToVtx(o2::track::TrackParCovFwd& t, const std::array& p, const std::array& cov) const; ///< track param positions at V0 candidate (no check for the candidate validity) const Vec3D& getTrackPos(int i, int cand = 0) const { return mTrPos[mOrder[cand]][i]; } @@ -282,6 +291,8 @@ class FwdDCAFitterN bool mAllowAltPreference = true; // if the fit converges to alternative PCA seed, abandon the current one bool mUseAbsDCA = false; // use abs. distance minimization rather than chi2 bool mPropagateToPCA = true; // create tracks version propagated to PCA + bool mUseMatBudget = false; // include MCS effects in track propagation + bool mTGeoFallBackAllowed = true; // use TGeo for precise estimate of mat. budget int mMaxIter = 60; // max number of iterations float mBz = 0; // bz field, to be set by user float mMaxR2 = 200. * 200.; // reject PCA's above this radius @@ -290,6 +301,7 @@ class FwdDCAFitterN float mMinRelChi2Change = 0.98; // stop iterations is chi2/chi2old > this float mMaxChi2 = 100; // abs cut on chi2 or abs distance float mMaxDist2ToMergeSeeds = 1.; // merge 2 seeds to their average if their distance^2 is below the threshold + const o2::base::MatLayerCylSet* mMatLUT = nullptr; // use to compute material budget to include MCS effects ClassDefNV(FwdDCAFitterN, 1); }; @@ -694,14 +706,15 @@ bool FwdDCAFitterN::FwdpropagateTracksToVertex(int icand) return true; } const Vec3D& pca = mPCA[ord]; + std::array covMatrixPCA = calcPCACovMatrixFlat(ord); + std::array cov = {covMatrixPCA[0], covMatrixPCA[2]}; for (int i = N; i--;) { - if (mUseAbsDCA) { - mCandTr[ord][i] = *mOrigTrPtr[i]; // fetch the track again, as mCandTr might have been propagated w/o errors - } + mCandTr[ord][i] = *mOrigTrPtr[i]; // fetch the track again, as mCandTr might have been propagated w/o errors auto& trc = mCandTr[ord][i]; - auto z = pca[2]; - - trc.propagateToZquadratic(z, mBz); + const std::array p = {(float)pca[0], (float)pca[1], (float)pca[2]}; + if (!propagateToVtx(trc, p, cov)) { + return false; + } } mTrPropDone[ord] = true; @@ -1256,6 +1269,25 @@ void FwdDCAFitterN::print() const LOG(info) << "Stopping condition: Max.param change < " << mMinParamChange << " Rel.Chi2 change > " << mMinRelChi2Change; LOG(info) << "Discard candidates for : Rvtx > " << getMaxR() << " DZ between tracks > " << mMaxDXIni; } +//___________________________________________________________________ +template +inline bool FwdDCAFitterN::propagateToVtx(o2::track::TrackParCovFwd& t, const std::array& p, const std::array& cov) const +{ + // propagate track to vertex including MCS effects if material budget included, simple propagation to Z otherwise + float x2x0 = 0; + if (mUseMatBudget) { + auto mb = mMatLUT->getMatBudget(t.getX(), t.getY(), t.getZ(), p[0], p[1], p[2]); + x2x0 = (float)mb.meanX2X0; + return t.propagateToVtxhelixWithMCS(p[2], {p[0], p[1]}, cov, mBz, x2x0); + } else if (mTGeoFallBackAllowed) { + auto geoMan = o2::base::GeometryManager::meanMaterialBudget(t.getX(), t.getY(), t.getZ(), p[0], p[1], p[2]); + x2x0 = (float)geoMan.meanX2X0; + return t.propagateToVtxhelixWithMCS(p[2], {p[0], p[1]}, cov, mBz, x2x0); + } else { + t.propagateToZhelix(p[2], mBz); + return true; + } +} using FwdDCAFitter2 = FwdDCAFitterN<2, o2::track::TrackParCovFwd>; using FwdDCAFitter3 = FwdDCAFitterN<3, o2::track::TrackParCovFwd>; diff --git a/Common/DCAFitter/include/DCAFitter/HelixHelper.h b/Common/DCAFitter/include/DCAFitter/HelixHelper.h deleted file mode 100644 index 87575367e1af5..0000000000000 --- a/Common/DCAFitter/include/DCAFitter/HelixHelper.h +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file HelixHelper.h -/// \brief Helper classes for helical tracks manipulations -/// \author ruben.shahoyan@cern.ch - -#ifndef _ALICEO2_HELIX_HELPER_ -#define _ALICEO2_HELIX_HELPER_ - -#include "CommonConstants/MathConstants.h" -#include "MathUtils/Utils.h" -#include "MathUtils/Primitive2D.h" - -namespace o2 -{ -namespace track -{ - -///__________________________________________________________________________ -//< precalculated track radius, center, alpha sin,cos and their combinations -struct TrackAuxPar : public o2::math_utils::CircleXYf_t { - float c, s, cc, ss, cs; // cos ans sin of track alpha and their products - - TrackAuxPar() = default; - - template - TrackAuxPar(const T& trc, float bz) - { - set(trc, bz); - } - float cosDif(const TrackAuxPar& t) const { return c * t.c + s * t.s; } // cos(alpha_this - alha_t) - float sinDif(const TrackAuxPar& t) const { return s * t.c - c * t.s; } // sin(alpha_this - alha_t) - - template - void set(const T& trc, float bz) - { - trc.getCircleParams(bz, *this, s, c); - cc = c * c; - ss = s * s; - cs = c * s; - } - ClassDefNV(TrackAuxPar, 1); -}; - -//__________________________________________________________ -//< crossing coordinates of 2 circles -struct CrossInfo { - static constexpr float MaxDistXYDef = 10.; - float xDCA[2] = {}; - float yDCA[2] = {}; - int nDCA = 0; - - int circlesCrossInfo(const TrackAuxPar& trax0, const TrackAuxPar& trax1, float maxDistXY = MaxDistXYDef) - { - const auto& trcA = trax0.rC > trax1.rC ? trax0 : trax1; // designate the largest circle as A - const auto& trcB = trax0.rC > trax1.rC ? trax1 : trax0; - float xDist = trcB.xC - trcA.xC, yDist = trcB.yC - trcA.yC; - float dist2 = xDist * xDist + yDist * yDist, dist = std::sqrt(dist2), rsum = trcA.rC + trcB.rC; - if (std::abs(dist) < 1e-12) { - return nDCA; // circles are concentric? - } - if (dist > rsum) { // circles don't touch, chose a point in between - // the parametric equation of lines connecting the centers is - // x = x0 + t/dist * (x1-x0), y = y0 + t/dist * (y1-y0) - if (dist - rsum > maxDistXY) { // too large distance - return nDCA; - } - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } else if (dist + trcB.rC < trcA.rC) { // the small circle is nestled into large one w/o touching - // select the point of closest approach of 2 circles - notTouchingXY(dist, xDist, yDist, trcA, -trcB.rC); - } else { // 2 intersection points - // to simplify calculations, we move to new frame x->x+Xc0, y->y+Yc0, so that - // the 1st one is centered in origin - if (std::abs(xDist) < std::abs(yDist)) { - float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * yDist), b = -xDist / yDist, ab = a * b, bb = b * b; - float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); - if (det > 0.) { - det = std::sqrt(det); - xDCA[0] = (-ab + det) / (1. + b * b); - yDCA[0] = a + b * xDCA[0] + trcA.yC; - xDCA[0] += trcA.xC; - xDCA[1] = (-ab - det) / (1. + b * b); - yDCA[1] = a + b * xDCA[1] + trcA.yC; - xDCA[1] += trcA.xC; - nDCA = 2; - } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } - } else { - float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * xDist), b = -yDist / xDist, ab = a * b, bb = b * b; - float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); - if (det > 0.) { - det = std::sqrt(det); - yDCA[0] = (-ab + det) / (1. + bb); - xDCA[0] = a + b * yDCA[0] + trcA.xC; - yDCA[0] += trcA.yC; - yDCA[1] = (-ab - det) / (1. + bb); - xDCA[1] = a + b * yDCA[1] + trcA.xC; - yDCA[1] += trcA.yC; - nDCA = 2; - } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } - } - } - return nDCA; - } - - void notTouchingXY(float dist, float xDist, float yDist, const TrackAuxPar& trcA, float rBSign) - { - // fast method to calculate DCA between 2 circles, assuming that they don't touch each outer: - // the parametric equation of lines connecting the centers is x = xA + t/dist * xDist, y = yA + t/dist * yDist - // with xA,yY being the center of the circle A ( = trcA.xC, trcA.yC ), xDist = trcB.xC = trcA.xC ... - // There are 2 special cases: - // (a) small circle is inside the large one: provide rBSign as -trcB.rC - // (b) circle are side by side: provide rBSign as trcB.rC - nDCA = 1; - auto t2d = (dist + trcA.rC - rBSign) / dist; - xDCA[0] = trcA.xC + 0.5 * (xDist * t2d); - yDCA[0] = trcA.yC + 0.5 * (yDist * t2d); - } - - template - int linesCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - /// closest approach of 2 straight lines - /// TrackParam propagation can be parameterized in lab in a form - /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) - /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) - /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp - /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab - /// frame (filled by TrackAuxPar for straight line tracks). - /// - /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) - /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) - /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) - /// zL(t) = zL + t Kz; Kz = tgl / csp - /// Note that Kx^2 + Ky^2 + Kz^2 = (1+tgl^2) / csp^2 - - float dx = trax1.xC - trax0.xC; // for straight line TrackAuxPar stores lab coordinates at referene point!!! - float dy = trax1.yC - trax0.yC; // - float dz = tr1.getZ() - tr0.getZ(); - auto csp0i2 = 1. / tr0.getCsp2(); // 1 / csp^2 - auto csp0i = std::sqrt(csp0i2); - auto tgp0 = tr0.getSnp() * csp0i; - float kx0 = trax0.c - trax0.s * tgp0; - float ky0 = trax0.s + trax0.c * tgp0; - float kz0 = tr0.getTgl() * csp0i; - auto csp1i2 = 1. / tr1.getCsp2(); // 1 / csp^2 - auto csp1i = std::sqrt(csp1i2); - auto tgp1 = tr1.getSnp() * std::sqrt(csp1i2); - float kx1 = trax1.c - trax1.s * tgp1; - float ky1 = trax1.s + trax1.c * tgp1; - float kz1 = tr1.getTgl() * csp1i; - /// Minimize |vecL1 - vecL0|^2 wrt t0 and t1: point of closest approach - /// Leads to system - /// A Dx = B with Dx = {dx0, dx1} - /// with A = - /// | kx0^2+ky0^2+kz0^2 -(kx0*kx1+ky0*ky1+kz0*kz1) | = (1+tgl0^2) / csp0^2 .... - /// | -(kx0*kx1+ky0*ky1+kz0*kz1) kx0^2+ky0^2+kz0^2 | ..... (1+tgl1^2) / csp1^2 - /// and B = {(dx Kx0 + dy Ky0 + dz Kz0), -(dx Kx1 + dy Ky1 + dz Kz1) } - /// - float a00 = (1.f + tr0.getTgl() * tr0.getTgl()) * csp0i2, a11 = (1.f + tr1.getTgl() * tr1.getTgl()) * csp1i2, a01 = -(kx0 * kx1 + ky0 * ky1 + kz0 * kz1); - float b0 = dx * kx0 + dy * ky0 + dz * kz0, b1 = -(dx * kx1 + dy * ky1 + dz * kz1); - float det = a00 * a11 - a01 * a01, det0 = b0 * a11 - b1 * a01, det1 = a00 * b1 - a01 * b0; - if (std::abs(det) > o2::constants::math::Almost0) { - auto detI = 1. / det; - auto t0 = det0 * detI; - auto t1 = det1 * detI; - float addx0 = kx0 * t0, addy0 = ky0 * t0, addx1 = kx1 * t1, addy1 = ky1 * t1; - dx += addx1 - addx0; // recalculate XY distance at DCA - dy += addy1 - addy0; - if (dx * dx + dy * dy > maxDistXY * maxDistXY) { - return nDCA; - } - xDCA[0] = (trax0.xC + addx0 + trax1.xC + addx1) * 0.5; - yDCA[0] = (trax0.yC + addy0 + trax1.yC + addy1) * 0.5; - nDCA = 1; - } - return nDCA; - } - - template - int circleLineCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - /// closest approach of line and circle - /// TrackParam propagation can be parameterized in lab in a form - /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) - /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) - /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp - /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab - /// frame (filled by TrackAuxPar for straight line tracks). - /// - /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) - /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) - /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) - /// zL(t) = zL + t Kz; Kz = tgl / csp - /// Note that Kx^2 + Ky^2 = 1 / csp^2 - - const auto& traxH = trax0.rC > trax1.rC ? trax0 : trax1; // circle (for the line rC is set to 0) - const auto& traxL = trax0.rC > trax1.rC ? trax1 : trax0; // line - const auto& trcL = trax0.rC > trax1.rC ? tr1 : tr0; // track of the line - - // solve quadratic equation of line crossing the circle - float dx = traxL.xC - traxH.xC; // X distance between the line lab reference and circle center - float dy = traxL.yC - traxH.yC; // Y... - // t^2(kx^2+ky^2) + 2t(dx*kx+dy*ky) + dx^2 + dy^2 - r^2 = 0 - auto cspi2 = 1. / trcL.getCsp2(); // 1 / csp^2 == kx^2 + ky^2 - auto cspi = std::sqrt(cspi2); - auto tgp = trcL.getSnp() * cspi; - float kx = traxL.c - traxL.s * tgp; - float ky = traxL.s + traxL.c * tgp; - double dk = dx * kx + dy * ky; - double det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC); - if (det > 0) { // 2 crossings - det = std::sqrt(det); - float t0 = (-dk + det) * cspi2; - float t1 = (-dk - det) * cspi2; - xDCA[0] = traxL.xC + kx * t0; - yDCA[0] = traxL.yC + ky * t0; - xDCA[1] = traxL.xC + kx * t1; - yDCA[1] = traxL.yC + ky * t1; - nDCA = 2; - } else { - // there is no crossing, find the point of the closest approach on the line which is closest to the circle center - float t = -dk * cspi2; - float xL = traxL.xC + kx * t, yL = traxL.yC + ky * t; // point on the line, need to average with point on the circle - float dxc = xL - traxH.xC, dyc = yL - traxH.yC, dist = std::sqrt(dxc * dxc + dyc * dyc); - if (dist - traxH.rC > maxDistXY) { // too large distance - return nDCA; - } - float drcf = traxH.rC / dist; // radius / distance to circle center - float xH = traxH.xC + dxc * drcf, yH = traxH.yC + dyc * drcf; - xDCA[0] = (xL + xH) * 0.5; - yDCA[0] = (yL + yH) * 0.5; - nDCA = 1; - } - return nDCA; - } - - template - int set(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - // calculate up to 2 crossings between 2 circles - nDCA = 0; - if (trax0.rC > o2::constants::math::Almost0 && trax1.rC > o2::constants::math::Almost0) { // both are not straight lines - nDCA = circlesCrossInfo(trax0, trax1, maxDistXY); - } else if (trax0.rC < o2::constants::math::Almost0 && trax1.rC < o2::constants::math::Almost0) { // both are straigt lines - nDCA = linesCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); - } else { - nDCA = circleLineCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); - } - // - return nDCA; - } - - CrossInfo() = default; - - template - CrossInfo(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - set(trax0, tr0, trax1, tr1, maxDistXY); - } - ClassDefNV(CrossInfo, 1); -}; - -} // namespace track -} // namespace o2 - -#endif diff --git a/Common/DCAFitter/src/DCAFitterLinkDef.h b/Common/DCAFitter/src/DCAFitterLinkDef.h index 3589ffe559e96..6883369c1b9b6 100644 --- a/Common/DCAFitter/src/DCAFitterLinkDef.h +++ b/Common/DCAFitter/src/DCAFitterLinkDef.h @@ -18,9 +18,6 @@ #pragma link C++ class o2::vertexing::DCAFitterN < 2, o2::track::TrackParCov> + ; #pragma link C++ class o2::vertexing::DCAFitterN < 3, o2::track::TrackParCov> + ; -#pragma link C++ class o2::track::TrackAuxPar + ; -#pragma link C++ class o2::track::CrossInfo + ; - #pragma link C++ function o2::vertexing::DCAFitter2::process(const o2::track::TrackParCov&, const o2::track::TrackParCov&); #pragma link C++ function o2::vertexing::DCAFitter3::process(const o2::track::TrackParCov&, const o2::track::TrackParCov&, const o2::track::TrackParCov&); diff --git a/Common/DCAFitter/test/testDCAFitterN.cxx b/Common/DCAFitter/test/testDCAFitterN.cxx index 7fef1f6889284..8ab2c7f4ca323 100644 --- a/Common/DCAFitter/test/testDCAFitterN.cxx +++ b/Common/DCAFitter/test/testDCAFitterN.cxx @@ -56,12 +56,26 @@ float checkResults(o2::utils::TreeStreamRedirector& outs, std::string& treeName, double dst = TMath::Sqrt(df[0] * df[0] + df[1] * df[1] + df[2] * df[2]); distMin = dst < distMin ? dst : distMin; auto parentTrack = fitter.createParentTrackParCov(ic); - // float genX + const std::array genPos{static_cast(vgen[0]), static_cast(vgen[1]), static_cast(vgen[2])}; + const std::array genMom{static_cast(genPar.Px()), static_cast(genPar.Py()), static_cast(genPar.Pz())}; + o2::track::TrackPar genParentTrack(genPos, genMom, parentTrack.getCharge(), false); + genParentTrack.rotateParam(parentTrack.getAlpha()); + std::array parentCovGlo{}; + const bool hasParentCovGlo = parentTrack.getCovXYZPxPyPzGlo(parentCovGlo); + const double pullX = hasParentCovGlo && parentCovGlo[0] > 0.f ? df[0] / TMath::Sqrt(parentCovGlo[0]) : 0.; + const double pullY = hasParentCovGlo && parentCovGlo[2] > 0.f ? df[1] / TMath::Sqrt(parentCovGlo[2]) : 0.; + const double pullZ = hasParentCovGlo && parentCovGlo[5] > 0.f ? df[2] / TMath::Sqrt(parentCovGlo[5]) : 0.; outs << treeName.c_str() << "cand=" << ic << "ncand=" << nCand << "nIter=" << nIter << "chi2=" << chi2 << "genPart=" << genPar << "recPart=" << moth << "genX=" << vgen[0] << "genY=" << vgen[1] << "genZ=" << vgen[2] << "dx=" << df[0] << "dy=" << df[1] << "dz=" << df[2] << "dst=" << dst - << "useAbsDCA=" << absDCA << "useWghDCA=" << useWghDCA << "parent=" << parentTrack << "\n"; + << "pullX=" << pullX << "pullY=" << pullY << "pullZ=" << pullZ + << "genParentTrack=" << genParentTrack + << "useAbsDCA=" << absDCA << "useWghDCA=" << useWghDCA << "parent=" << parentTrack; + for (int i = 0; i < fitter.getNProngs(); i++) { + outs << treeName.c_str() << fmt::format("prong{}=", i).c_str() << fitter.getTrack(i, ic); + } + outs << treeName.c_str() << "\n"; } return distMin; } @@ -130,7 +144,7 @@ TLorentzVector generate(Vec3D& vtx, std::vector& vctr, f float rad = forceQ[i] == 0 ? 600. : TMath::Abs(1. / trc.getCurvature(bz)); if (!trc.propagateTo(trc.getX() + (gRandom->Rndm() - 0.5) * rad * 0.05, bz) || !trc.rotate(trc.getAlpha() + (gRandom->Rndm() - 0.5) * 0.2)) { - printf("Failed to randomize "); + LOGP(error, "Failed to randomize "); trc.print(); } } @@ -139,26 +153,47 @@ TLorentzVector generate(Vec3D& vtx, std::vector& vctr, f return parent; } +static constexpr int NFitStatus{14}; +using FitStatusArray = std::array, NFitStatus>; +static constexpr const char* FitStatusNames[NFitStatus] = { + "None", "Converged", "MaxIter", "NoCrossing", "RejRadius", "RejTrackX", "RejTrackRoughZ", "RejChi2Max", + "FailProp", "FailInvConv", "FailInvWeight", "FailInv2ndDeriv", "FailCorrTracks", "FailCloserAlt"}; +inline void printStat(const FitStatusArray& a) +{ + LOGP(info, "FitStatus summary : ....A / ..AWD / ...WD (A=abs.dist;AWD=abs.wghPCA.dist;WD=wgh.dist)"); + for (int i{0}; i < NFitStatus; ++i) { + LOGP(info, "{:2d}={:20s}: {:5d} / {:5d} / {:5d}", i, FitStatusNames[i], a[i][0], a[i][1], a[i][2]); + } + BOOST_CHECK(a[0][0] == 0); // ensure coverage of all possible states + BOOST_CHECK(a[0][1] == 0); + BOOST_CHECK(a[0][2] == 0); +} + BOOST_AUTO_TEST_CASE(DCAFitterNProngs) { constexpr int NTest = 10000; o2::utils::TreeStreamRedirector outStream("dcafitterNTest.root"); TGenPhaseSpace genPHS; + constexpr double ele = 0.00051; + constexpr double gamma = 2 * ele + 1e-6; constexpr double pion = 0.13957; constexpr double k0 = 0.49761; constexpr double kch = 0.49368; constexpr double dch = 1.86965; + std::vector gammadec = {ele, ele}; std::vector k0dec = {pion, pion}; std::vector dchdec = {pion, kch, pion}; std::vector vctracks; + FitStatusArray fitstat; Vec3D vtxGen; double bz = 5.0; // 2 prongs vertices { - LOG(info) << "Processing 2-prong Helix - Helix case"; + LOG(info) << "\n\nProcessing 2-prong Helix - Helix case"; std::vector forceQ{1, 1}; + std::memset(fitstat.data(), 0, sizeof(fitstat)); o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter ft.setBz(bz); @@ -189,6 +224,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -201,6 +237,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -213,30 +250,121 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDW += minD; nfoundW++; } + ++fitstat[ft.getFitStatus()][2]; } - ft.print(); + // ft.print(); meanDA /= nfoundA ? nfoundA : 1; - meanDAW /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundAW : 1; meanDW /= nfoundW ? nfoundW : 1; LOG(info) << "Processed " << NTest << " 2-prong vertices Helix : Helix"; LOG(info) << "2-prongs with abs.dist minization: eff= " << float(nfoundA) / NTest - << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime(); + << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with abs.dist but wghPCA: eff= " << float(nfoundAW) / NTest - << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime(); + << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with wgh.dist minization: eff= " << float(nfoundW) / NTest - << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime(); + << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime() * 1000 << " ms"; + printStat(fitstat); BOOST_CHECK(nfoundA > 0.99 * NTest); BOOST_CHECK(nfoundAW > 0.99 * NTest); BOOST_CHECK(nfoundW > 0.99 * NTest); BOOST_CHECK(meanDA < 0.1); BOOST_CHECK(meanDAW < 0.1); BOOST_CHECK(meanDW < 0.1); + ft.print(); + } + + // 2 prongs vertices with collinear tracks (gamma conversion) + { + LOG(info) << "\n\nProcessing 2-prong Helix - Helix case gamma conversion"; + std::vector forceQ{1, 1}; + std::memset(fitstat.data(), 0, sizeof(fitstat)); + + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMaxDXYIni(4); // do not consider V0 seeds with tracks XY-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + ft.setMaxChi2(); + ft.setCollinear(true); + + std::string treeName2A = "gpr2a", treeName2AW = "gpr2aw", treeName2W = "gpr2w"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + auto genParent = generate(vtxGen, vctracks, bz, genPHS, gamma, gammadec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = ft.process(vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swA.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName2A, ft, vtxGen, genParent, gammadec); + meanDA += minD; + nfoundA++; + } + ++fitstat[ft.getFitStatus()][0]; + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = ft.process(vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName2AW, ft, vtxGen, genParent, gammadec); + meanDAW += minD; + nfoundAW++; + } + ++fitstat[ft.getFitStatus()][1]; + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = ft.process(vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName2W, ft, vtxGen, genParent, gammadec); + meanDW += minD; + nfoundW++; + } + ++fitstat[ft.getFitStatus()][2]; + } + // ft.print(); + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundAW : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 2-prong vertices Helix : Helix from gamma conversion"; + LOG(info) << "2-prongs with abs.dist minization: eff= " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff= " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minization: eff= " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime() * 1000 << " ms"; + printStat(fitstat); + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 2.1); + BOOST_CHECK(meanDAW < 2.1); + BOOST_CHECK(meanDW < 2.1); + ft.print(); } // 2 prongs vertices with one of charges set to 0: Helix : Line { std::vector forceQ{1, 1}; - LOG(info) << "Processing 2-prong Helix - Line case"; + LOG(info) << "\n\nProcessing 2-prong Helix - Line case"; + std::memset(fitstat.data(), 0, sizeof(fitstat)); + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter ft.setBz(bz); ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway @@ -267,6 +395,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -279,6 +408,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -291,30 +421,35 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDW += minD; nfoundW++; } + ++fitstat[ft.getFitStatus()][2]; } - ft.print(); + // ft.print(); meanDA /= nfoundA ? nfoundA : 1; meanDAW /= nfoundAW ? nfoundAW : 1; meanDW /= nfoundW ? nfoundW : 1; LOG(info) << "Processed " << NTest << " 2-prong vertices: Helix : Line"; LOG(info) << "2-prongs with abs.dist minization: eff= " << float(nfoundA) / NTest - << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime(); + << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with abs.dist but wghPCA: eff= " << float(nfoundAW) / NTest - << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime(); + << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with wgh.dist minization: eff= " << float(nfoundW) / NTest - << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime(); + << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime() * 1000 << " ms"; + printStat(fitstat); BOOST_CHECK(nfoundA > 0.99 * NTest); BOOST_CHECK(nfoundAW > 0.99 * NTest); BOOST_CHECK(nfoundW > 0.99 * NTest); BOOST_CHECK(meanDA < 0.1); BOOST_CHECK(meanDAW < 0.1); BOOST_CHECK(meanDW < 0.1); + ft.print(); } // 2 prongs vertices with both of charges set to 0: Line : Line { std::vector forceQ{0, 0}; - LOG(info) << "Processing 2-prong Line - Line case"; + LOG(info) << "\n\nProcessing 2-prong Line - Line case"; + std::memset(fitstat.data(), 0, sizeof(fitstat)); + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter ft.setBz(bz); ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway @@ -344,6 +479,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -356,6 +492,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -368,29 +505,34 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDW += minD; nfoundW++; } + ++fitstat[ft.getFitStatus()][2]; } - ft.print(); + // ft.print(); meanDA /= nfoundA ? nfoundA : 1; meanDAW /= nfoundAW ? nfoundAW : 1; meanDW /= nfoundW ? nfoundW : 1; LOG(info) << "Processed " << NTest << " 2-prong vertices: Line : Line"; LOG(info) << "2-prongs with abs.dist minization: eff= " << float(nfoundA) / NTest - << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime(); + << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with abs.dist but wghPCA: eff= " << float(nfoundAW) / NTest - << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime(); + << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with wgh.dist minization: eff= " << float(nfoundW) / NTest - << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime(); + << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime() * 1000 << " ms"; + printStat(fitstat); BOOST_CHECK(nfoundA > 0.99 * NTest); BOOST_CHECK(nfoundAW > 0.99 * NTest); BOOST_CHECK(nfoundW > 0.99 * NTest); BOOST_CHECK(meanDA < 0.1); BOOST_CHECK(meanDAW < 0.1); BOOST_CHECK(meanDW < 0.1); + ft.print(); } // 3 prongs vertices { + LOG(info) << "\n\nProcessing 3-prong vertices"; std::vector forceQ{1, 1, 1}; + std::memset(fitstat.data(), 0, sizeof(fitstat)); o2::vertexing::DCAFitterN<3> ft; // 3 prong fitter ft.setBz(bz); @@ -420,6 +562,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -432,6 +575,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -444,26 +588,28 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDW += minD; nfoundW++; } + ++fitstat[ft.getFitStatus()][2]; } - ft.print(); + // ft.print(); meanDA /= nfoundA ? nfoundA : 1; meanDAW /= nfoundAW ? nfoundAW : 1; meanDW /= nfoundW ? nfoundW : 1; LOG(info) << "Processed " << NTest << " 3-prong vertices"; LOG(info) << "3-prongs with abs.dist minization: eff= " << float(nfoundA) / NTest - << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime(); + << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime() * 1000 << " ms"; LOG(info) << "3-prongs with abs.dist but wghPCA: eff= " << float(nfoundAW) / NTest - << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime(); + << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime() * 1000 << " ms"; LOG(info) << "3-prongs with wgh.dist minization: eff= " << float(nfoundW) / NTest - << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime(); + << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime() * 1000 << " ms"; + printStat(fitstat); BOOST_CHECK(nfoundA > 0.99 * NTest); BOOST_CHECK(nfoundAW > 0.99 * NTest); BOOST_CHECK(nfoundW > 0.99 * NTest); BOOST_CHECK(meanDA < 0.1); BOOST_CHECK(meanDAW < 0.1); BOOST_CHECK(meanDW < 0.1); + ft.print(); } - outStream.Close(); } diff --git a/Common/Field/CMakeLists.txt b/Common/Field/CMakeLists.txt index fd00d77accfd3..719e7bc3a6440 100644 --- a/Common/Field/CMakeLists.txt +++ b/Common/Field/CMakeLists.txt @@ -16,6 +16,7 @@ o2_add_library(Field src/MagFieldParam.cxx src/MagneticField.cxx src/MagneticWrapperChebyshev.cxx + src/FieldOriginBiasParam.cxx src/ALICE3MagneticField.cxx PUBLIC_LINK_LIBRARIES O2::MathUtils FairRoot::Base O2::CommonUtils) @@ -26,6 +27,7 @@ o2_target_root_dictionary(Field include/Field/MagFieldContFact.h include/Field/MagFieldFast.h include/Field/MagFieldFact.h + include/Field/FieldOriginBiasParam.h include/Field/ALICE3MagneticField.h) o2_add_test(MagneticField diff --git a/Common/Field/include/Field/FieldOriginBiasParam.h b/Common/Field/include/Field/FieldOriginBiasParam.h new file mode 100644 index 0000000000000..c7c815924c5f3 --- /dev/null +++ b/Common/Field/include/Field/FieldOriginBiasParam.h @@ -0,0 +1,38 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author ruben.shahoyan@cern.ch + +/// parameters to bias the origin of the magnetic field + +#ifndef ALICEO2_FIELDORIGIN_BIAS_PARAM_H +#define ALICEO2_FIELDORIGIN_BIAS_PARAM_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace field +{ + +struct FieldOriginBiasParam : public o2::conf::ConfigurableParamHelper { + double x = 0.; + double y = 0.; + double z = 0.; + + O2ParamDef(FieldOriginBiasParam, "FieldOriginBias"); +}; + +} // namespace field +} // end namespace o2 + +#endif diff --git a/Common/Field/include/Field/MagFieldFast.h b/Common/Field/include/Field/MagFieldFast.h index acff8f528ad06..ae6431a477923 100644 --- a/Common/Field/include/Field/MagFieldFast.h +++ b/Common/Field/include/Field/MagFieldFast.h @@ -57,7 +57,7 @@ class MagFieldFast bool Field(const math_utils::Point3D xyz, double bxyz[3]) const; bool GetBcomp(EDim comp, const double xyz[3], double& b) const; bool GetBcomp(EDim comp, const float xyz[3], float& b) const; - bool GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const; + bool GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const; bool GetBcomp(EDim comp, const math_utils::Point3D xyz, float& b) const; bool GetBx(const double xyz[3], double& bx) const { return GetBcomp(kX, xyz, bx); } @@ -66,6 +66,8 @@ class MagFieldFast bool GetBy(const float xyz[3], float& by) const { return GetBcomp(kY, xyz, by); } bool GetBz(const double xyz[3], double& bz) const { return GetBcomp(kZ, xyz, bz); } bool GetBz(const float xyz[3], float& bz) const { return GetBcomp(kZ, xyz, bz); } + bool GetBz(const math_utils::Point3D xyz, double& bz) const { return GetBcomp(kZ, xyz, bz); } + bool GetBz(const math_utils::Point3D xyz, float& bz) const { return GetBcomp(kZ, xyz, bz); } void setFactorSol(float v = 1.f) { mFactorSol = v; } float getFactorSol() const { return mFactorSol; } diff --git a/Common/Field/include/Field/MagneticField.h b/Common/Field/include/Field/MagneticField.h index d2639a902f3a9..7c95feacb8787 100644 --- a/Common/Field/include/Field/MagneticField.h +++ b/Common/Field/include/Field/MagneticField.h @@ -32,6 +32,7 @@ namespace o2 namespace field { class MagneticWrapperChebyshev; +class FieldOriginBiasParam; } } // namespace o2 namespace o2 @@ -249,6 +250,7 @@ class MagneticField : public FairField void setBeamType(MagFieldParam::BeamType_t type) { mBeamType = type; } void setBeamEnergy(float energy) { mBeamEnergy = energy; } + void checkOriginBias(); private: std::unique_ptr mMeasuredMap; //! Measured part of the field map @@ -273,6 +275,8 @@ class MagneticField : public FairField TNamed mParameterNames; ///< file and parameterization loaded + static const FieldOriginBiasParam* gOriginBias; + static const Double_t sSolenoidToDipoleZ; ///< conventional Z of transition from L3 to Dipole field static const UShort_t sPolarityConvention; ///< convention for the mapping of the curr.sign on main component sign diff --git a/Common/Field/src/FieldLinkDef.h b/Common/Field/src/FieldLinkDef.h index cd1b035341284..3571644096a73 100644 --- a/Common/Field/src/FieldLinkDef.h +++ b/Common/Field/src/FieldLinkDef.h @@ -22,4 +22,7 @@ #pragma link C++ class o2::field::MagFieldFast + ; #pragma link C++ class o2::field::ALICE3MagneticField + ; +#pragma link C++ class o2::field::FieldOriginBiasParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::field::FieldOriginBiasParam> + ; + #endif diff --git a/Common/Field/src/FieldOriginBiasParam.cxx b/Common/Field/src/FieldOriginBiasParam.cxx new file mode 100644 index 0000000000000..140b862fcdc07 --- /dev/null +++ b/Common/Field/src/FieldOriginBiasParam.cxx @@ -0,0 +1,18 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author ruben.shahoyan@cern.ch + +/// parameters to bias the origin of the magnetic field + +#include "Field/FieldOriginBiasParam.h" + +O2ParamImpl(o2::field::FieldOriginBiasParam); diff --git a/Common/Field/src/MagFieldFast.cxx b/Common/Field/src/MagFieldFast.cxx index e652dffa396c5..624e88fed40bb 100644 --- a/Common/Field/src/MagFieldFast.cxx +++ b/Common/Field/src/MagFieldFast.cxx @@ -16,12 +16,10 @@ #include "Field/MagFieldFast.h" #include -#ifndef GPUCA_GPUCODE_DEVICE #include #include #include using namespace std; -#endif using namespace o2::field; @@ -63,8 +61,11 @@ MagFieldFast::MagFieldFast(float factor, int nomField, const string inpFmt) : mF bool MagFieldFast::LoadData(const string inpFName) { // load field from text file - - std::ifstream in(gSystem->ExpandPathName(inpFName.data()), std::ifstream::in); + TString sName(inpFName); + if (gSystem->ExpandPathName(sName)) { + LOG(fatal) << "Failed to expand file name " << inpFName; + } + std::ifstream in(sName.Data(), std::ifstream::in); if (in.fail()) { LOG(fatal) << "Failed to open file " << inpFName; return false; @@ -145,7 +146,7 @@ bool MagFieldFast::GetBcomp(EDim comp, const double xyz[3], double& b) const } //_______________________________________________________________________ -bool MagFieldFast::GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const +bool MagFieldFast::GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const { // get field int zSeg, rSeg, quadrant; @@ -238,7 +239,7 @@ bool MagFieldFast::Field(const math_utils::Point3D xyz, double bxyz[3]) bool MagFieldFast::GetSegment(float x, float y, float z, int& zSeg, int& rSeg, int& quadrant) const { // get segment of point location - const float zGridSpaceInv = 1.f / (kSolZMax * 2 / kNSolZRanges); + const float zGridSpaceInv = 1.f / (kSolZMax * 2 / (float)kNSolZRanges); zSeg = -1; if (z < kSolZMax) { if (z > -kSolZMax) { diff --git a/Common/Field/src/MagneticField.cxx b/Common/Field/src/MagneticField.cxx index 260580f07132e..768e42bef6875 100644 --- a/Common/Field/src/MagneticField.cxx +++ b/Common/Field/src/MagneticField.cxx @@ -14,9 +14,11 @@ /// \author ruben.shahoyan@cern.ch #include "Field/MagneticField.h" -#include // for TFile -#include // for TPRegexp -#include // for TSystem, gSystem +#include "Field/FieldOriginBiasParam.h" +#include // for TFile +#include // for TPRegexp +#include // for TString +#include // for TSystem, gSystem #include // for FairLogger #include "FairParamList.h" #include "FairRun.h" @@ -26,6 +28,8 @@ using namespace o2::field; ClassImp(MagneticField); +const FieldOriginBiasParam* MagneticField::gOriginBias = nullptr; + const Double_t MagneticField::sSolenoidToDipoleZ = -700.; /// Explanation for polarity conventions: these are the mapping between the @@ -87,6 +91,9 @@ MagneticField::MagneticField() * Default constructor */ fType = 2; // flag non-constant field + if (!gOriginBias) { + checkOriginBias(); + } } MagneticField::MagneticField(const char* name, const char* title, Double_t factorSol, Double_t factorDip, @@ -115,8 +122,10 @@ MagneticField::MagneticField(const char* name, const char* title, Double_t facto /* * Constructor for human readable params */ - setDataFileName(path.c_str()); + if (!gOriginBias) { + checkOriginBias(); + } CreateField(); } @@ -144,8 +153,10 @@ MagneticField::MagneticField(const MagFieldParam& param) /* * Constructor for FairParam derived params */ - setDataFileName(param.GetMapPath()); + if (!gOriginBias) { + checkOriginBias(); + } CreateField(); } @@ -242,7 +253,8 @@ Bool_t MagneticField::loadParameterization() LOG(fatal) << "MagneticField::loadParameterization: Field data " << getParameterName() << " are already loaded from " << getDataFileName() << "\n"; } - const char* fname = gSystem->ExpandPathName(getDataFileName()); + TString fname = getDataFileName(); + gSystem->ExpandPathName(fname); TFile* file = TFile::Open(fname); if (!file) { LOG(fatal) << "MagneticField::loadParameterization: Failed to open magnetic field data file " << fname << "\n"; @@ -259,12 +271,12 @@ Bool_t MagneticField::loadParameterization() return kTRUE; } -void MagneticField::Field(const Double_t* __restrict__ xyz, Double_t* __restrict__ b) +void MagneticField::Field(const Double_t* __restrict__ xyzExt, Double_t* __restrict__ b) { /* * query field value at point */ - + double xyz[3] = {xyzExt[0] - gOriginBias->x, xyzExt[1] - gOriginBias->y, xyzExt[2] - gOriginBias->z}; // b[0]=b[1]=b[2]=0.0; if (mFastField && mFastField->Field(xyz, b)) { return; @@ -286,12 +298,12 @@ void MagneticField::Field(const Double_t* __restrict__ xyz, Double_t* __restrict } } -Double_t MagneticField::getBz(const Double_t* xyz) const +Double_t MagneticField::getBz(const Double_t* xyzExt) const { /* * query field Bz component at point */ - + double xyz[3] = {xyzExt[0] - gOriginBias->x, xyzExt[1] - gOriginBias->y, xyzExt[2] - gOriginBias->z}; if (mFastField) { double bz = 0; if (mFastField->GetBz(xyz, bz)) { @@ -539,7 +551,7 @@ void MagneticField::rescaleField(float l3Cur, float diCur, bool uniform, int con float sclL3 = l3Cur, sclDip = diCur; MagFieldParam::BMap_t map = getFieldMapScale(sclL3, sclDip, uniform); if (map != mMapType) { - LOGP(fatal, "Provided L3current={} DipCurrent={} uniform={} leads to map type {}, incompatible with loaded {}", l3Cur, diCur, uniform, map, mMapType); + LOGP(fatal, "Provided L3current={} DipCurrent={} uniform={} leads to map type {}, incompatible with loaded {}", l3Cur, diCur, uniform, (int)map, (int)mMapType); } setFactorSolenoid(sclL3); setFactorDipole(sclDip); @@ -558,18 +570,29 @@ MagFieldParam::BMap_t MagneticField::getFieldMapScale(float& l3, float& dip, boo MagFieldParam::BMap_t map = MagFieldParam::k5kG; float sclL3, sclDip; + float l3sav = l3, dipsav = dip; float l3Pol = l3 > 0 ? 1 : -1; float diPol = dip > 0 ? 1 : -1; l3 = TMath::Abs(l3); dip = TMath::Abs(dip); + static bool overrideL3 = std::getenv("O2_OVERRIDE_L3_CURRENT") != nullptr; + static bool overrideDIP = std::getenv("O2_OVERRIDE_DIPOLE_CURRENT") != nullptr; + static bool warnL3Done = false, warnDipDone = false, warnPolarityDone = false; if (TMath::Abs((sclDip = dip / diNominalCurrent) - 1.) > tolerance && !uniform) { if (dip <= zero) { sclDip = 0.; // some small current.. -> Dipole OFF } else { - LOG(fatal) << "MagneticField::createFieldMap: Wrong dipole current (" << dip << " A)!"; + if (!overrideDIP) { + LOG(fatal) << "MagneticField::createFieldMap: Wrong dipole current (" << dipsav << " A)!"; + } else { + if (!warnDipDone) { + LOGP(error, "Dipole current was overridden to unsupported value {}", dipsav); + warnDipDone = true; + } + } } } if (uniform) { @@ -587,15 +610,33 @@ MagFieldParam::BMap_t MagneticField::getFieldMapScale(float& l3, float& dip, boo sclDip = 0; map = MagFieldParam::k5kGUniform; } else { - LOG(fatal) << "MagneticField::createFieldMap: Wrong L3 current (" << l3 << " A)!"; + if (!overrideL3) { + LOG(fatal) << "MagneticField::createFieldMap: Wrong L3 current (" << l3sav << " A)!"; + } else { + if (!warnL3Done) { + LOGP(error, "L3 current was overridden to unsupported value {}", l3sav); + warnL3Done = true; + } + map = MagFieldParam::k5kG; + sclL3 = l3 / l3NominalCurrent1; + } } } if (sclDip != 0 && map != MagFieldParam::k5kGUniform) { if ((l3 <= zero) || ((convention == kConvLHC && l3Pol != diPol) || (convention == kConvDCS2008 && l3Pol == diPol))) { - LOG(fatal) << "MagneticField::createFieldMap: Wrong combination for L3/Dipole polarities (" - << (l3Pol > 0 ? '+' : '-') << "/" << (diPol > 0 ? '+' : '-') << ") for convention " - << getPolarityConvention(); + if (overrideL3 || overrideDIP) { + if (!warnPolarityDone) { + LOG(error) << "Overriden currents have wrong combination for L3/Dipole polarities (" + << (l3Pol > 0 ? '+' : '-') << "/" << (diPol > 0 ? '+' : '-') << ") for convention " + << getPolarityConvention(); + warnPolarityDone = true; + } + } else { + LOG(fatal) << "MagneticField::createFieldMap: Wrong combination for L3/Dipole polarities (" + << (l3Pol > 0 ? '+' : '-') << "/" << (diPol > 0 ? '+' : '-') << ") for convention " + << getPolarityConvention(); + } } } l3 = (l3Pol < 0) ? -sclL3 : sclL3; @@ -697,3 +738,14 @@ void MagneticField::AllowFastField(bool v) mFastField.reset(nullptr); } } + +//_____________________________________________________________________________ +void MagneticField::checkOriginBias() +{ + // posibility to globally bias all data members with the proper env.var + if (const auto* biasString = std::getenv("O2_DPL_FIELDORIGINBIAS"); biasString && *biasString) { + o2::conf::ConfigurableParam::updateFromString(biasString); + } + gOriginBias = &FieldOriginBiasParam::Instance(); + LOGP(info, "Field origin is set to: XYZ: {:.4f},{:.4f},{:.4f}", gOriginBias->x, gOriginBias->y, gOriginBias->z); +} diff --git a/Common/ML/CMakeLists.txt b/Common/ML/CMakeLists.txt new file mode 100644 index 0000000000000..0ed52e1a23e20 --- /dev/null +++ b/Common/ML/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(ML + SOURCES src/OrtInterface.cxx + TARGETVARNAME targetName + PRIVATE_LINK_LIBRARIES O2::GPUCommon onnxruntime::onnxruntime) + +# Pass ORT variables as a preprocessor definition +target_compile_definitions(${targetName} PRIVATE + $<$:ORT_ROCM_BUILD> + $<$:ORT_CUDA_BUILD> + $<$:ORT_MIGRAPHX_BUILD> + $<$:ORT_TENSORRT_BUILD>) diff --git a/Common/ML/include/ML/3rdparty/GPUORTFloat16.h b/Common/ML/include/ML/3rdparty/GPUORTFloat16.h new file mode 100644 index 0000000000000..75e146d872cd1 --- /dev/null +++ b/Common/ML/include/ML/3rdparty/GPUORTFloat16.h @@ -0,0 +1,887 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// This code was created from: +// - https://github.com/microsoft/onnxruntime/blob/main/include/onnxruntime/core/session/onnxruntime_float16.h +// - https://github.com/microsoft/onnxruntime/blob/main/include/onnxruntime/core/session/onnxruntime_cxx_api.h + +#ifndef GPUORTFLOAT16_H +#define GPUORTFLOAT16_H + +#ifndef GPUCA_GPUCODE_DEVICE +#include +#include +#include +#include +#endif + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" + +namespace o2 +{ + +namespace OrtDataType +{ + +namespace detail +{ + +enum class endian { +#if defined(_WIN32) + little = 0, + big = 1, + native = little, +#elif defined(__GNUC__) || defined(__clang__) + little = __ORDER_LITTLE_ENDIAN__, + big = __ORDER_BIG_ENDIAN__, + native = __BYTE_ORDER__, +#else +#error OrtDataType::detail::endian is not implemented in this environment. +#endif +}; + +static_assert( + endian::native == endian::little || endian::native == endian::big, + "Only little-endian or big-endian native byte orders are supported."); + +} // namespace detail + +/// +/// Shared implementation between public and internal classes. CRTP pattern. +/// +template +struct Float16Impl { + protected: + /// + /// Converts from float to uint16_t float16 representation + /// + /// + /// + GPUd() constexpr static uint16_t ToUint16Impl(float v) noexcept; + + /// + /// Converts float16 to float + /// + /// float representation of float16 value + GPUd() float ToFloatImpl() const noexcept; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + GPUd() uint16_t AbsImpl() const noexcept + { + return static_cast(val & ~kSignMask); + } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + GPUd() uint16_t NegateImpl() const noexcept + { + return IsNaN() ? val : static_cast(val ^ kSignMask); + } + + public: + // uint16_t special values + static constexpr uint16_t kSignMask = 0x8000U; + static constexpr uint16_t kBiasedExponentMask = 0x7C00U; + static constexpr uint16_t kPositiveInfinityBits = 0x7C00U; + static constexpr uint16_t kNegativeInfinityBits = 0xFC00U; + static constexpr uint16_t kPositiveQNaNBits = 0x7E00U; + static constexpr uint16_t kNegativeQNaNBits = 0xFE00U; + static constexpr uint16_t kEpsilonBits = 0x4170U; + static constexpr uint16_t kMinValueBits = 0xFBFFU; // Minimum normal number + static constexpr uint16_t kMaxValueBits = 0x7BFFU; // Largest normal number + static constexpr uint16_t kOneBits = 0x3C00U; + static constexpr uint16_t kMinusOneBits = 0xBC00U; + + uint16_t val{0}; + + GPUdDefault() Float16Impl() = default; + + /// + /// Checks if the value is negative + /// + /// true if negative + GPUd() bool IsNegative() const noexcept + { + return static_cast(val) < 0; + } + + /// + /// Tests if the value is NaN + /// + /// true if NaN + GPUd() bool IsNaN() const noexcept + { + return AbsImpl() > kPositiveInfinityBits; + } + + /// + /// Tests if the value is finite + /// + /// true if finite + GPUd() bool IsFinite() const noexcept + { + return AbsImpl() < kPositiveInfinityBits; + } + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + GPUd() bool IsPositiveInfinity() const noexcept + { + return val == kPositiveInfinityBits; + } + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + GPUd() bool IsNegativeInfinity() const noexcept + { + return val == kNegativeInfinityBits; + } + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + GPUd() bool IsInfinity() const noexcept + { + return AbsImpl() == kPositiveInfinityBits; + } + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + GPUd() bool IsNaNOrZero() const noexcept + { + auto abs = AbsImpl(); + return (abs == 0 || abs > kPositiveInfinityBits); + } + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + GPUd() bool IsNormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) != 0); // is not subnormal (has a non-zero exponent) + } + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + GPUd() bool IsSubnormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) == 0); // is subnormal (has a zero exponent) + } + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + GPUd() Derived Abs() const noexcept { return Derived::FromBits(AbsImpl()); } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + GPUd() Derived Negate() const noexcept { return Derived::FromBits(NegateImpl()); } + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + GPUd() static bool AreZero(const Float16Impl& lhs, const Float16Impl& rhs) noexcept + { + return static_cast((lhs.val | rhs.val) & ~kSignMask) == 0; + } + + GPUd() bool operator==(const Float16Impl& rhs) const noexcept + { + if (IsNaN() || rhs.IsNaN()) { + // IEEE defines that NaN is not equal to anything, including itself. + return false; + } + return val == rhs.val; + } + + GPUd() bool operator!=(const Float16Impl& rhs) const noexcept { return !(*this == rhs); } + + GPUd() bool operator<(const Float16Impl& rhs) const noexcept + { + if (IsNaN() || rhs.IsNaN()) { + // IEEE defines that NaN is unordered with respect to everything, including itself. + return false; + } + + const bool left_is_negative = IsNegative(); + if (left_is_negative != rhs.IsNegative()) { + // When the signs of left and right differ, we know that left is less than right if it is + // the negative value. The exception to this is if both values are zero, in which case IEEE + // says they should be equal, even if the signs differ. + return left_is_negative && !AreZero(*this, rhs); + } + return (val != rhs.val) && ((val < rhs.val) ^ left_is_negative); + } +}; + +// The following Float16_t conversions are based on the code from +// Eigen library. + +// The conversion routines are Copyright (c) Fabian Giesen, 2016. +// The original license follows: +// +// Copyright (c) Fabian Giesen, 2016 +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +namespace detail +{ +union float32_bits { + unsigned int u; + float f; +}; +}; // namespace detail + +template +GPUdi() constexpr uint16_t Float16Impl::ToUint16Impl(float v) noexcept +{ + detail::float32_bits f{}; + f.f = v; + + constexpr detail::float32_bits f32infty = {255 << 23}; + constexpr detail::float32_bits f16max = {(127 + 16) << 23}; + constexpr detail::float32_bits denorm_magic = {((127 - 15) + (23 - 10) + 1) << 23}; + constexpr unsigned int sign_mask = 0x80000000u; + uint16_t val = static_cast(0x0u); + + unsigned int sign = f.u & sign_mask; + f.u ^= sign; + + // NOTE all the integer compares in this function can be safely + // compiled into signed compares since all operands are below + // 0x80000000. Important if you want fast straight SSE2 code + // (since there's no unsigned PCMPGTD). + + if (f.u >= f16max.u) { // result is Inf or NaN (all exponent bits set) + val = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf + } else { // (De)normalized number or zero + if (f.u < (113 << 23)) { // resulting FP16 is subnormal or zero + // use a magic value to align our 10 mantissa bits at the bottom of + // the float. as long as FP addition is round-to-nearest-even this + // just works. + f.f += denorm_magic.f; + + // and one integer subtract of the bias later, we have our final float! + val = static_cast(f.u - denorm_magic.u); + } else { + unsigned int mant_odd = (f.u >> 13) & 1; // resulting mantissa is odd + + // update exponent, rounding bias part 1 + // Equivalent to `f.u += ((unsigned int)(15 - 127) << 23) + 0xfff`, but + // without arithmetic overflow. + f.u += 0xc8000fffU; + // rounding bias part 2 + f.u += mant_odd; + // take the bits! + val = static_cast(f.u >> 13); + } + } + + val |= static_cast(sign >> 16); + return val; +} + +template +GPUdi() float Float16Impl::ToFloatImpl() const noexcept +{ + constexpr detail::float32_bits magic = {113 << 23}; + constexpr unsigned int shifted_exp = 0x7c00 << 13; // exponent mask after shift + detail::float32_bits o{}; + + o.u = (val & 0x7fff) << 13; // exponent/mantissa bits + unsigned int exp = shifted_exp & o.u; // just the exponent + o.u += (127 - 15) << 23; // exponent adjust + + // handle exponent special cases + if (exp == shifted_exp) { // Inf/NaN? + o.u += (128 - 16) << 23; // extra exp adjust + } else if (exp == 0) { // Zero/Denormal? + o.u += 1 << 23; // extra exp adjust + o.f -= magic.f; // re-normalize + } + + // Attempt to workaround the Internal Compiler Error on ARM64 + // for bitwise | operator, including std::bitset +#if (defined _MSC_VER) && (defined _M_ARM || defined _M_ARM64 || defined _M_ARM64EC) + if (IsNegative()) { + return -o.f; + } +#else + // original code: + o.u |= (val & 0x8000U) << 16U; // sign bit +#endif + return o.f; +} + +/// Shared implementation between public and internal classes. CRTP pattern. +template +struct BFloat16Impl { + protected: + /// + /// Converts from float to uint16_t float16 representation + /// + /// + /// + GPUd() static uint16_t ToUint16Impl(float v) noexcept; + + /// + /// Converts bfloat16 to float + /// + /// float representation of bfloat16 value + GPUd() float ToFloatImpl() const noexcept; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + GPUd() uint16_t AbsImpl() const noexcept + { + return static_cast(val & ~kSignMask); + } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + GPUd() uint16_t NegateImpl() const noexcept + { + return IsNaN() ? val : static_cast(val ^ kSignMask); + } + + public: + // uint16_t special values + static constexpr uint16_t kSignMask = 0x8000U; + static constexpr uint16_t kBiasedExponentMask = 0x7F80U; + static constexpr uint16_t kPositiveInfinityBits = 0x7F80U; + static constexpr uint16_t kNegativeInfinityBits = 0xFF80U; + static constexpr uint16_t kPositiveQNaNBits = 0x7FC1U; + static constexpr uint16_t kNegativeQNaNBits = 0xFFC1U; + static constexpr uint16_t kSignaling_NaNBits = 0x7F80U; + static constexpr uint16_t kEpsilonBits = 0x0080U; + static constexpr uint16_t kMinValueBits = 0xFF7FU; + static constexpr uint16_t kMaxValueBits = 0x7F7FU; + static constexpr uint16_t kRoundToNearest = 0x7FFFU; + static constexpr uint16_t kOneBits = 0x3F80U; + static constexpr uint16_t kMinusOneBits = 0xBF80U; + + uint16_t val{0}; + + GPUdDefault() BFloat16Impl() = default; + + /// + /// Checks if the value is negative + /// + /// true if negative + GPUd() bool IsNegative() const noexcept + { + return static_cast(val) < 0; + } + + /// + /// Tests if the value is NaN + /// + /// true if NaN + GPUd() bool IsNaN() const noexcept + { + return AbsImpl() > kPositiveInfinityBits; + } + + /// + /// Tests if the value is finite + /// + /// true if finite + GPUd() bool IsFinite() const noexcept + { + return AbsImpl() < kPositiveInfinityBits; + } + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + GPUd() bool IsPositiveInfinity() const noexcept + { + return val == kPositiveInfinityBits; + } + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + GPUd() bool IsNegativeInfinity() const noexcept + { + return val == kNegativeInfinityBits; + } + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + GPUd() bool IsInfinity() const noexcept + { + return AbsImpl() == kPositiveInfinityBits; + } + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + GPUd() bool IsNaNOrZero() const noexcept + { + auto abs = AbsImpl(); + return (abs == 0 || abs > kPositiveInfinityBits); + } + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + GPUd() bool IsNormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) != 0); // is not subnormal (has a non-zero exponent) + } + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + GPUd() bool IsSubnormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) == 0); // is subnormal (has a zero exponent) + } + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + GPUd() Derived Abs() const noexcept { return Derived::FromBits(AbsImpl()); } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + GPUd() Derived Negate() const noexcept { return Derived::FromBits(NegateImpl()); } + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + GPUd() static bool AreZero(const BFloat16Impl& lhs, const BFloat16Impl& rhs) noexcept + { + // IEEE defines that positive and negative zero are equal, this gives us a quick equality check + // for two values by or'ing the private bits together and stripping the sign. They are both zero, + // and therefore equivalent, if the resulting value is still zero. + return static_cast((lhs.val | rhs.val) & ~kSignMask) == 0; + } +}; + +template +GPUdi() uint16_t BFloat16Impl::ToUint16Impl(float v) noexcept +{ + uint16_t result; + if (o2::gpu::CAMath::IsNaN(v)) { + result = kPositiveQNaNBits; + } else { + auto get_msb_half = [](float fl) { + uint16_t res; +#ifdef GPUCA_GPUCODE + o2::gpu::CAMath::memcpy(&res, reinterpret_cast(&fl) + sizeof(uint16_t), sizeof(uint16_t)); +#else +#ifdef __cpp_if_constexpr + if constexpr (detail::endian::native == detail::endian::little) +#else + if (detail::endian::native == detail::endian::little) +#endif + { + std::memcpy(&res, reinterpret_cast(&fl) + sizeof(uint16_t), sizeof(uint16_t)); + } else { + std::memcpy(&res, &fl, sizeof(uint16_t)); + } +#endif + return res; + }; + + uint16_t upper_bits = get_msb_half(v); + union { + uint32_t U32; + float F32; + }; + F32 = v; + U32 += (upper_bits & 1) + kRoundToNearest; + result = get_msb_half(F32); + } + return result; +} + +template +GPUdi() float BFloat16Impl::ToFloatImpl() const noexcept +{ +#ifndef __FAST_MATH__ + if (IsNaN()) { + return o2::gpu::CAMath::QuietNaN(); + } +#endif + float result; + char* const first = reinterpret_cast(&result); + char* const second = first + sizeof(uint16_t); +#ifdef GPUCA_GPUCODE + first[0] = first[1] = 0; + o2::gpu::CAMath::memcpy(second, &val, sizeof(uint16_t)); +#else +#ifdef __cpp_if_constexpr + if constexpr (detail::endian::native == detail::endian::little) +#else + if (detail::endian::native == detail::endian::little) +#endif + { + std::memset(first, 0, sizeof(uint16_t)); + std::memcpy(second, &val, sizeof(uint16_t)); + } else { + std::memcpy(first, &val, sizeof(uint16_t)); + std::memset(second, 0, sizeof(uint16_t)); + } +#endif + return result; +} + +/** \brief IEEE 754 half-precision floating point data type + * + * \details This struct is used for converting float to float16 and back + * so the user could feed inputs and fetch outputs using these type. + * + * The size of the structure should align with uint16_t and one can freely cast + * uint16_t buffers to/from Ort::Float16_t to feed and retrieve data. + * + * \code{.unparsed} + * // This example demonstrates converion from float to float16 + * constexpr float values[] = {1.f, 2.f, 3.f, 4.f, 5.f}; + * std::vector fp16_values; + * fp16_values.reserve(std::size(values)); + * std::transform(std::begin(values), std::end(values), std::back_inserter(fp16_values), + * [](float value) { return Ort::Float16_t(value); }); + * + * \endcode + */ +struct Float16_t : OrtDataType::Float16Impl { + private: + /// + /// Constructor from a 16-bit representation of a float16 value + /// No conversion is done here. + /// + /// 16-bit representation + constexpr explicit Float16_t(uint16_t v) noexcept { val = v; } + + public: + using Base = OrtDataType::Float16Impl; + + /// + /// Default constructor + /// + GPUdDefault() Float16_t() = default; + + /// + /// Explicit conversion to uint16_t representation of float16. + /// + /// uint16_t bit representation of float16 + /// new instance of Float16_t + GPUd() constexpr static Float16_t FromBits(uint16_t v) noexcept { return Float16_t(v); } + + /// + /// __ctor from float. Float is converted into float16 16-bit representation. + /// + /// float value + GPUd() explicit Float16_t(float v) noexcept { val = Base::ToUint16Impl(v); } + + /// + /// Converts float16 to float + /// + /// float representation of float16 value + GPUd() float ToFloat() const noexcept { return Base::ToFloatImpl(); } + + /// + /// Checks if the value is negative + /// + /// true if negative + using Base::IsNegative; + + /// + /// Tests if the value is NaN + /// + /// true if NaN + using Base::IsNaN; + + /// + /// Tests if the value is finite + /// + /// true if finite + using Base::IsFinite; + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + using Base::IsPositiveInfinity; + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + using Base::IsNegativeInfinity; + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + using Base::IsInfinity; + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + using Base::IsNaNOrZero; + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + using Base::IsNormal; + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + using Base::IsSubnormal; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + using Base::Abs; + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + using Base::Negate; + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + using Base::AreZero; + + /// + /// User defined conversion operator. Converts Float16_t to float. + /// + GPUdi() explicit operator float() const noexcept { return ToFloat(); } + + using Base::operator==; + using Base::operator!=; + using Base::operator<; +}; + +static_assert(sizeof(Float16_t) == sizeof(uint16_t), "Sizes must match"); + +/** \brief bfloat16 (Brain Floating Point) data type + * + * \details This struct is used for converting float to bfloat16 and back + * so the user could feed inputs and fetch outputs using these type. + * + * The size of the structure should align with uint16_t and one can freely cast + * uint16_t buffers to/from Ort::BFloat16_t to feed and retrieve data. + * + * \code{.unparsed} + * // This example demonstrates converion from float to float16 + * constexpr float values[] = {1.f, 2.f, 3.f, 4.f, 5.f}; + * std::vector bfp16_values; + * bfp16_values.reserve(std::size(values)); + * std::transform(std::begin(values), std::end(values), std::back_inserter(bfp16_values), + * [](float value) { return Ort::BFloat16_t(value); }); + * + * \endcode + */ +struct BFloat16_t : OrtDataType::BFloat16Impl { + private: + /// + /// Constructor from a uint16_t representation of bfloat16 + /// used in FromBits() to escape overload resolution issue with + /// constructor from float. + /// No conversion is done. + /// + /// 16-bit bfloat16 value + constexpr explicit BFloat16_t(uint16_t v) noexcept { val = v; } + + public: + using Base = OrtDataType::BFloat16Impl; + + GPUdDefault() BFloat16_t() = default; + + /// + /// Explicit conversion to uint16_t representation of bfloat16. + /// + /// uint16_t bit representation of bfloat16 + /// new instance of BFloat16_t + GPUd() static constexpr BFloat16_t FromBits(uint16_t v) noexcept { return BFloat16_t(v); } + + /// + /// __ctor from float. Float is converted into bfloat16 16-bit representation. + /// + /// float value + GPUd() explicit BFloat16_t(float v) noexcept { val = Base::ToUint16Impl(v); } + + /// + /// Converts bfloat16 to float + /// + /// float representation of bfloat16 value + GPUd() float ToFloat() const noexcept { return Base::ToFloatImpl(); } + + /// + /// Checks if the value is negative + /// + /// true if negative + using Base::IsNegative; + + /// + /// Tests if the value is NaN + /// + /// true if NaN + using Base::IsNaN; + + /// + /// Tests if the value is finite + /// + /// true if finite + using Base::IsFinite; + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + using Base::IsPositiveInfinity; + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + using Base::IsNegativeInfinity; + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + using Base::IsInfinity; + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + using Base::IsNaNOrZero; + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + using Base::IsNormal; + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + using Base::IsSubnormal; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + using Base::Abs; + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + using Base::Negate; + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + using Base::AreZero; + + /// + /// User defined conversion operator. Converts BFloat16_t to float. + /// + GPUdi() explicit operator float() const noexcept { return ToFloat(); } + + // We do not have an inherited impl for the below operators + // as the internal class implements them a little differently + bool operator==(const BFloat16_t& rhs) const noexcept; + bool operator!=(const BFloat16_t& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const BFloat16_t& rhs) const noexcept; +}; + +static_assert(sizeof(BFloat16_t) == sizeof(uint16_t), "Sizes must match"); + +} // namespace OrtDataType + +} // namespace o2 +#endif diff --git a/Common/ML/include/ML/OrtInterface.h b/Common/ML/include/ML/OrtInterface.h new file mode 100644 index 0000000000000..987ce8fb4d6dd --- /dev/null +++ b/Common/ML/include/ML/OrtInterface.h @@ -0,0 +1,130 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file OrtInterface.h +/// \author Christian Sonnabend +/// \brief A header library for loading ONNX models and inferencing them on CPU and GPU + +#ifndef O2_ML_ORTINTERFACE_H +#define O2_ML_ORTINTERFACE_H + +// C++ and system includes +#include +#include +#include +#include +#include +#include + +// O2 includes +#include "GPUCommonLogger.h" + +namespace Ort +{ +struct SessionOptions; +struct MemoryInfo; +struct Env; +} // namespace Ort + +namespace o2::ml +{ + +class OrtModel +{ + + public: + // Constructors & destructors + OrtModel(); + OrtModel(std::unordered_map optionsMap); + void init(std::unordered_map optionsMap); + virtual ~OrtModel(); + + // General purpose + void initOptions(std::unordered_map optionsMap); + void initEnvironment(); + void initSession(); + void initSessionFromBuffer(const char* buffer, size_t bufferSize); + void memoryOnDevice(int32_t = 0); + bool isInitialized() { return mInitialized; } + void resetSession(); + + // Getters + std::vector> getNumInputNodes() const { return mInputShapes; } + std::vector> getNumOutputNodes() const { return mOutputShapes; } + std::vector getInputNames() const { return mInputNames; } + std::vector getOutputNames() const { return mOutputNames; } + Ort::SessionOptions* getSessionOptions(); + Ort::MemoryInfo* getMemoryInfo(); + Ort::Env* getEnv(); + int32_t getIntraOpNumThreads() const { return mIntraOpNumThreads; } + int32_t getInterOpNumThreads() const { return mInterOpNumThreads; } + + // Setters + void setDeviceId(int32_t id) { mDeviceId = id; } + void setIO(); + void setActiveThreads(int threads) { mIntraOpNumThreads = threads; } + void setIntraOpNumThreads(int threads) + { + if (mDeviceType == "CPU") { + mIntraOpNumThreads = threads; + } + } + void setInterOpNumThreads(int threads) + { + if (mDeviceType == "CPU") { + mInterOpNumThreads = threads; + } + } + void setEnv(Ort::Env*); + + // Conversion + template + std::vector v2v(std::vector
&, bool = true); + + // Inferencing + template // class I is the input data type, e.g. float, class O is the output data type, e.g. OrtDataType::Float16_t from O2/Common/ML/include/ML/GPUORTFloat16.h + std::vector inference(std::vector&); + + template + std::vector inference(std::vector>&); + + template + void inference(I*, int64_t, O*); + + template + void inference(I**, int64_t, O*); + + void release(bool = false); + + private: + // ORT variables -> need to be hidden as pImpl + struct OrtVariables; + std::unique_ptr mPImplOrt; + + // Input & Output specifications of the loaded network + std::vector mInputNamesChar, mOutputNamesChar; + std::vector mInputNames, mOutputNames; + std::vector> mInputShapes, mOutputShapes, mInputShapesCopy, mOutputShapesCopy; // Input shapes + std::vector mInputSizePerNode, mOutputSizePerNode; // Output shapes + int32_t mInputsTotal = 0, mOutputsTotal = 0; // Total number of inputs and outputs + + // Environment settings + bool mInitialized = false, mDeterministicMode = false; + std::string mModelPath, mEnvName = "", mDeviceType = "CPU", mThreadAffinity = ""; // device options should be cpu, rocm, migraphx, cuda + int32_t mIntraOpNumThreads = 1, mInterOpNumThreads = 1, mDeviceId = -1, mEnableProfiling = 0, mLoggingLevel = 0, mAllocateDeviceMemory = 0, mEnableOptimizations = 0; + + std::string printShape(const std::vector&); + std::string printShape(const std::vector>&, std::vector&); +}; + +} // namespace o2::ml + +#endif // O2_ML_ORTINTERFACE_H diff --git a/Common/ML/src/OrtInterface.cxx b/Common/ML/src/OrtInterface.cxx new file mode 100644 index 0000000000000..9eccb9638d882 --- /dev/null +++ b/Common/ML/src/OrtInterface.cxx @@ -0,0 +1,502 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file OrtInterface.cxx +/// \author Christian Sonnabend +/// \brief A header library for loading ONNX models and inferencing them on CPU and GPU + +#include "ML/OrtInterface.h" +#include "ML/3rdparty/GPUORTFloat16.h" + +// ONNX includes +#include + +#include + +namespace o2 +{ + +namespace ml +{ + +OrtModel::OrtModel() = default; +OrtModel::OrtModel(std::unordered_map optionsMap) { init(optionsMap); } +OrtModel::~OrtModel() = default; +void OrtModel::init(std::unordered_map optionsMap) +{ + initOptions(optionsMap); + initEnvironment(); +} + +struct OrtModel::OrtVariables { // The actual implementation is hidden in the .cxx file + // ORT runtime objects + Ort::RunOptions runOptions; + std::unique_ptr env = nullptr; + std::unique_ptr session = nullptr; ///< ONNX session + Ort::SessionOptions sessionOptions; + Ort::AllocatorWithDefaultOptions allocator; + Ort::MemoryInfo memoryInfo = Ort::MemoryInfo("Cpu", OrtAllocatorType::OrtDeviceAllocator, 0, OrtMemType::OrtMemTypeDefault); + std::unique_ptr ioBinding = nullptr; +}; + +// General purpose +void OrtModel::initOptions(std::unordered_map optionsMap) +{ + mPImplOrt = std::make_unique(); + + // Load from options map + if (!optionsMap.contains("model-path")) { + LOG(fatal) << "(ORT) Model path must be contained in options map!"; + } + + if (!optionsMap["model-path"].empty()) { + mModelPath = optionsMap["model-path"]; + mDeviceType = (optionsMap.contains("device-type") ? optionsMap["device-type"] : "CPU"); + mDeviceId = (optionsMap.contains("device-id") ? std::stoi(optionsMap["device-id"]) : -1); + mAllocateDeviceMemory = (optionsMap.contains("allocate-device-memory") ? std::stoi(optionsMap["allocate-device-memory"]) : 0); + mIntraOpNumThreads = (optionsMap.contains("intra-op-num-threads") ? std::stoi(optionsMap["intra-op-num-threads"]) : 0); + mInterOpNumThreads = (optionsMap.contains("inter-op-num-threads") ? std::stoi(optionsMap["inter-op-num-threads"]) : 0); + mLoggingLevel = (optionsMap.contains("logging-level") ? std::stoi(optionsMap["logging-level"]) : 0); + mEnableProfiling = (optionsMap.contains("enable-profiling") ? std::stoi(optionsMap["enable-profiling"]) : 0); + mEnableOptimizations = (optionsMap.contains("enable-optimizations") ? std::stoi(optionsMap["enable-optimizations"]) : 0); + mEnvName = (optionsMap.contains("onnx-environment-name") ? optionsMap["onnx-environment-name"] : "onnx_model_inference"); + mDeterministicMode = (optionsMap.contains("deterministic-compute") ? std::stoi(optionsMap["deterministic-compute"]) : 0); + + if (mDeviceType == "CPU") { + (mPImplOrt->sessionOptions).SetIntraOpNumThreads(mIntraOpNumThreads); + (mPImplOrt->sessionOptions).SetInterOpNumThreads(mInterOpNumThreads); + if (mIntraOpNumThreads > 1 || mInterOpNumThreads > 1) { + (mPImplOrt->sessionOptions).SetExecutionMode(ExecutionMode::ORT_PARALLEL); + } else if (mIntraOpNumThreads == 1) { + (mPImplOrt->sessionOptions).SetExecutionMode(ExecutionMode::ORT_SEQUENTIAL); + } + if (mLoggingLevel < 2) { + LOG(info) << "(ORT) CPU execution provider set with " << mIntraOpNumThreads << " (mIntraOpNumThreads) and " << mInterOpNumThreads << " (mInterOpNumThreads) threads"; + } + } + + // OrtROCMProviderOptions rocm_options{}; + // (mPImplOrt->sessionOptions).AppendExecutionProvider_ROCM(rocm_options); + + (mPImplOrt->sessionOptions).DisableMemPattern(); + (mPImplOrt->sessionOptions).DisableCpuMemArena(); + + if (mEnableProfiling) { + if (optionsMap.contains("profiling-output-path")) { + (mPImplOrt->sessionOptions).EnableProfiling((optionsMap["profiling-output-path"] + "/ORT_LOG_").c_str()); + } else { + LOG(warning) << "(ORT) If profiling is enabled, optionsMap[\"profiling-output-path\"] should be set. Disabling profiling for now."; + (mPImplOrt->sessionOptions).DisableProfiling(); + } + } else { + (mPImplOrt->sessionOptions).DisableProfiling(); + } + + if (mDeterministicMode > 0) { + (mPImplOrt->sessionOptions).AddConfigEntry("session_options.use_deterministic_compute", "1"); + } + + (mPImplOrt->sessionOptions).SetGraphOptimizationLevel(GraphOptimizationLevel(mEnableOptimizations)); + (mPImplOrt->sessionOptions).SetLogSeverityLevel(OrtLoggingLevel(mLoggingLevel)); + + mInitialized = true; + } else { + LOG(fatal) << "(ORT) Model path cannot be empty!"; + } +} + +void OrtModel::initEnvironment() +{ + mPImplOrt->env = std::make_unique( + OrtLoggingLevel(mLoggingLevel), + (mEnvName.empty() ? "ORT" : mEnvName.c_str()), + // Integrate ORT logging into Fairlogger + [](void* param, OrtLoggingLevel severity, const char* category, const char* logid, const char* code_location, const char* message) { + if (severity == ORT_LOGGING_LEVEL_VERBOSE) { + LOG(debug) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_INFO) { + LOG(info) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_WARNING) { + LOG(warning) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_ERROR) { + LOG(error) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_FATAL) { + LOG(fatal) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else { + LOG(info) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } + }, + (void*)3); + (mPImplOrt->env)->DisableTelemetryEvents(); // Disable telemetry events +} + +void OrtModel::initSessionFromBuffer(const char* buffer, size_t bufferSize) +{ + if (mAllocateDeviceMemory) { + memoryOnDevice(mDeviceId); + } + mPImplOrt->sessionOptions.AddConfigEntry("session.load_model_format", "ONNX"); + mPImplOrt->sessionOptions.AddConfigEntry("session.use_ort_model_bytes_directly", "1"); + + mPImplOrt->session = std::make_unique(*mPImplOrt->env, + buffer, + bufferSize, + mPImplOrt->sessionOptions); + mPImplOrt->ioBinding = std::make_unique(*mPImplOrt->session); + + setIO(); + + if (mLoggingLevel < 2) { + LOG(info) << "(ORT) Model loaded successfully from buffer! (inputs: " << printShape(mInputShapes, mInputNames) << ", outputs: " << printShape(mOutputShapes, mInputNames) << ")"; + } +} + +void OrtModel::initSession() +{ + if (mAllocateDeviceMemory) { + memoryOnDevice(mDeviceId); + } + mPImplOrt->session = std::make_unique(*mPImplOrt->env, mModelPath.c_str(), mPImplOrt->sessionOptions); + mPImplOrt->ioBinding = std::make_unique(*mPImplOrt->session); + + setIO(); + + if (mLoggingLevel < 2) { + LOG(info) << "(ORT) Model loaded successfully! (inputs: " << printShape(mInputShapes, mInputNames) << ", outputs: " << printShape(mOutputShapes, mInputNames) << ")"; + } +} + +void OrtModel::memoryOnDevice(int32_t deviceIndex) +{ + if (deviceIndex >= 0) { + (mPImplOrt->runOptions).AddConfigEntry("disable_synchronize_execution_providers", "1"); + (mPImplOrt->sessionOptions).AddConfigEntry("session.use_device_allocator_for_initializers", "1"); // See kOrtSessionOptionsUseDeviceAllocatorForInitializers, https://github.com/microsoft/onnxruntime/blob/main/include/onnxruntime/core/session/onnxruntime_session_options_config_keys.h + (mPImplOrt->sessionOptions).AddConfigEntry("session.use_env_allocators", "1"); // This should enable to use the volatile memory allocation defined in O2/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerHost.cxx; not working yet: ONNX still assigns new memory at init time + (mPImplOrt->sessionOptions).AddConfigEntry("session_options.enable_cpu_mem_arena", "0"); // This should enable to use the volatile memory allocation defined in O2/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerHost.cxx; not working yet: ONNX still assigns new memory at init time + // Arena memory shrinkage comes at performance cost + // For now prefer to use single allocation, enabled by O2/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu -> SetONNXGPUStream -> rocm_options.arena_extend_strategy = 0; + (mPImplOrt->runOptions).AddConfigEntry("memory.enable_memory_arena_shrinkage", ("gpu:" + std::to_string(deviceIndex)).c_str()); // See kOrtRunOptionsConfigEnableMemoryArenaShrinkage, https://github.com/microsoft/onnxruntime/blob/90c263f471bbce724e77d8e62831d3a9fa838b2f/include/onnxruntime/core/session/onnxruntime_run_options_config_keys.h#L27 + + std::string dev_mem_str = ""; + if (mDeviceType == "ROCM") { + dev_mem_str = "HipPinned"; + } + if (mDeviceType == "CUDA") { + dev_mem_str = "Cuda"; + } + mPImplOrt->memoryInfo = Ort::MemoryInfo(dev_mem_str.c_str(), OrtAllocatorType::OrtDeviceAllocator, deviceIndex, OrtMemType::OrtMemTypeDefault); + if (mLoggingLevel < 2) { + LOG(info) << "(ORT) Memory info set to on-device memory for device type " << mDeviceType << " with ID " << deviceIndex << " and mPImplOrt pointer " << mPImplOrt; + } + } +} + +void OrtModel::resetSession() +{ + mPImplOrt->session = std::make_unique(*(mPImplOrt->env), mModelPath.c_str(), mPImplOrt->sessionOptions); +} + +// Getters +Ort::SessionOptions* OrtModel::getSessionOptions() +{ + return &mPImplOrt->sessionOptions; +} + +Ort::MemoryInfo* OrtModel::getMemoryInfo() +{ + return &mPImplOrt->memoryInfo; +} + +Ort::Env* OrtModel::getEnv() +{ + return (mPImplOrt->env).get(); +} + +template +std::vector OrtModel::v2v(std::vector& input, bool clearInput) +{ + if constexpr (std::is_same_v) { + return input; + } else { + std::vector output(input.size()); + std::transform(std::begin(input), std::end(input), std::begin(output), [](I f) { return O(f); }); + if (clearInput) { + input.clear(); + } + return output; + } +} + +void OrtModel::setIO() +{ + for (size_t i = 0; i < (mPImplOrt->session)->GetInputCount(); ++i) { + mInputNames.push_back((mPImplOrt->session)->GetInputNameAllocated(i, mPImplOrt->allocator).get()); + } + for (size_t i = 0; i < (mPImplOrt->session)->GetInputCount(); ++i) { + mInputShapes.emplace_back((mPImplOrt->session)->GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); + } + for (size_t i = 0; i < (mPImplOrt->session)->GetOutputCount(); ++i) { + mOutputNames.push_back((mPImplOrt->session)->GetOutputNameAllocated(i, mPImplOrt->allocator).get()); + } + for (size_t i = 0; i < (mPImplOrt->session)->GetOutputCount(); ++i) { + mOutputShapes.emplace_back((mPImplOrt->session)->GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); + } + + mInputNamesChar.resize(mInputNames.size(), nullptr); + std::transform(std::begin(mInputNames), std::end(mInputNames), std::begin(mInputNamesChar), + [&](const std::string& str) { return str.c_str(); }); + mOutputNamesChar.resize(mOutputNames.size(), nullptr); + std::transform(std::begin(mOutputNames), std::end(mOutputNames), std::begin(mOutputNamesChar), + [&](const std::string& str) { return str.c_str(); }); + + mInputShapesCopy = mInputShapes; + mOutputShapesCopy = mOutputShapes; + mInputSizePerNode.resize(mInputShapes.size(), 1); + mOutputSizePerNode.resize(mOutputShapes.size(), 1); + mInputsTotal = 1; + for (size_t i = 0; i < mInputShapes.size(); ++i) { + if (mInputShapes[i].size() > 0) { + for (size_t j = 1; j < mInputShapes[i].size(); ++j) { + if (mInputShapes[i][j] > 0) { + mInputsTotal *= mInputShapes[i][j]; + mInputSizePerNode[i] *= mInputShapes[i][j]; + } + } + } + } + mOutputsTotal = 1; + for (size_t i = 0; i < mOutputShapes.size(); ++i) { + if (mOutputShapes[i].size() > 0) { + for (size_t j = 1; j < mOutputShapes[i].size(); ++j) { + if (mOutputShapes[i][j] > 0) { + mOutputsTotal *= mOutputShapes[i][j]; + mOutputSizePerNode[i] *= mOutputShapes[i][j]; + } + } + } + } +} + +void OrtModel::setEnv(Ort::Env* env) +{ + mPImplOrt->env.reset(env); +} + +// Inference +template +std::vector OrtModel::inference(std::vector& input) +{ + std::vector inputShape = mInputShapes[0]; + inputShape[0] = input.size(); + for (size_t i = 1; i < mInputShapes[0].size(); ++i) { + inputShape[0] /= mInputShapes[0][i]; + } + std::vector inputTensor; + if constexpr (std::is_same_v) { + inputTensor.emplace_back(Ort::Value::CreateTensor(mPImplOrt->memoryInfo, reinterpret_cast(input.data()), input.size(), inputShape.data(), inputShape.size())); + } else { + inputTensor.emplace_back(Ort::Value::CreateTensor(mPImplOrt->memoryInfo, input.data(), input.size(), inputShape.data(), inputShape.size())); + } + // input.clear(); + auto outputTensors = (mPImplOrt->session)->Run(mPImplOrt->runOptions, mInputNamesChar.data(), inputTensor.data(), inputTensor.size(), mOutputNamesChar.data(), mOutputNamesChar.size()); + O* outputValues = outputTensors[0].template GetTensorMutableData(); + std::vector outputValuesVec{outputValues, outputValues + inputShape[0] * mOutputShapes[0][1]}; + outputTensors.clear(); + return outputValuesVec; +} + +template std::vector o2::ml::OrtModel::inference(std::vector&); +template std::vector o2::ml::OrtModel::inference(std::vector&); +template std::vector o2::ml::OrtModel::inference(std::vector&); + +template +void OrtModel::inference(I* input, int64_t input_size, O* output) +{ + // std::vector providers = Ort::GetAvailableProviders(); + // for (const auto& provider : providers) { + // LOG(info) << "Available Execution Provider: " << provider; + // } + std::vector inputShape{input_size, (int64_t)mInputShapes[0][1]}; + Ort::Value inputTensor = Ort::Value(nullptr); + if constexpr (std::is_same_v) { + inputTensor = Ort::Value::CreateTensor(mPImplOrt->memoryInfo, reinterpret_cast(input), input_size * mInputShapes[0][1], inputShape.data(), inputShape.size()); + } else { + inputTensor = Ort::Value::CreateTensor(mPImplOrt->memoryInfo, input, input_size * mInputShapes[0][1], inputShape.data(), inputShape.size()); + } + (mPImplOrt->ioBinding)->BindInput(mInputNames[0].c_str(), inputTensor); + + std::vector outputShape{input_size, mOutputShapes[0][1]}; + Ort::Value outputTensor = Ort::Value(nullptr); + if constexpr (std::is_same_v) { + outputTensor = Ort::Value::CreateTensor(mPImplOrt->memoryInfo, reinterpret_cast(output), input_size * mOutputShapes[0][1], outputShape.data(), outputShape.size()); + } else { + outputTensor = Ort::Value::CreateTensor(mPImplOrt->memoryInfo, output, input_size * mOutputShapes[0][1], outputShape.data(), outputShape.size()); + } + (mPImplOrt->ioBinding)->BindOutput(mOutputNames[0].c_str(), outputTensor); + + (mPImplOrt->session)->Run(mPImplOrt->runOptions, *mPImplOrt->ioBinding); + // mPImplOrt->session->Run( + // mPImplOrt->runOptions, + // mInputNamesChar.data(), + // &inputTensor, + // mInputNamesChar.size(), + // mOutputNamesChar.data(), + // &outputTensor, + // mOutputNamesChar.size()); +} + +template void OrtModel::inference(OrtDataType::Float16_t*, int64_t, OrtDataType::Float16_t*); +template void OrtModel::inference(OrtDataType::Float16_t*, int64_t, float*); +template void OrtModel::inference(float*, int64_t, OrtDataType::Float16_t*); +template void OrtModel::inference(float*, int64_t, float*); + +template +void OrtModel::inference(I** input, int64_t input_size, O* output) +{ + std::vector inputTensors(mInputShapesCopy.size()); + + for (size_t i = 0; i < mInputShapesCopy.size(); ++i) { + + mInputShapesCopy[i][0] = input_size; // batch-size + mOutputShapesCopy[i][0] = input_size; // batch-size + + if constexpr (std::is_same_v) { + inputTensors[i] = Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + reinterpret_cast(input[i]), + mInputSizePerNode[i] * input_size, + mInputShapesCopy[i].data(), + mInputShapesCopy[i].size()); + } else { + inputTensors[i] = Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + input[i], + mInputSizePerNode[i] * input_size, + mInputShapesCopy[i].data(), + mInputShapesCopy[i].size()); + } + } + + Ort::Value outputTensor = Ort::Value(nullptr); + if constexpr (std::is_same_v) { + outputTensor = Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + reinterpret_cast(output), + mOutputSizePerNode[0] * input_size, // assumes that there is only one output node + mOutputShapesCopy[0].data(), + mOutputShapesCopy[0].size()); + } else { + outputTensor = Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + output, + mOutputSizePerNode[0] * input_size, // assumes that there is only one output node + mOutputShapesCopy[0].data(), + mOutputShapesCopy[0].size()); + } + + // === Run inference === + mPImplOrt->session->Run( + mPImplOrt->runOptions, + mInputNamesChar.data(), + inputTensors.data(), + mInputNamesChar.size(), + mOutputNamesChar.data(), + &outputTensor, + mOutputNamesChar.size()); +} + +template void OrtModel::inference(OrtDataType::Float16_t**, int64_t, OrtDataType::Float16_t*); +template void OrtModel::inference(OrtDataType::Float16_t**, int64_t, float*); +template void OrtModel::inference(float**, int64_t, OrtDataType::Float16_t*); +template void OrtModel::inference(float**, int64_t, float*); + +template +std::vector OrtModel::inference(std::vector>& inputs) +{ + std::vector input_tensors; + + for (size_t i = 0; i < inputs.size(); ++i) { + + mInputShapesCopy[i][0] = inputs[i].size() / mInputSizePerNode[i]; // batch-size + + if constexpr (std::is_same_v) { + input_tensors.emplace_back( + Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + reinterpret_cast(inputs[i].data()), + mInputSizePerNode[i] * mInputShapesCopy[i][0], + mInputShapesCopy[i].data(), + mInputShapesCopy[i].size())); + } else { + input_tensors.emplace_back( + Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + inputs[i].data(), + mInputSizePerNode[i] * mInputShapesCopy[i][0], + mInputShapesCopy[i].data(), + mInputShapesCopy[i].size())); + } + } + + int32_t totalOutputSize = mOutputsTotal * mInputShapesCopy[0][0]; + + // === Run inference === + auto output_tensors = mPImplOrt->session->Run( + mPImplOrt->runOptions, + mInputNamesChar.data(), + input_tensors.data(), + input_tensors.size(), + mOutputNamesChar.data(), + mOutputNamesChar.size()); + + // === Extract output values === + O* output_data = output_tensors[0].template GetTensorMutableData(); + std::vector output_vec(output_data, output_data + totalOutputSize); + output_tensors.clear(); + return output_vec; +} + +template std::vector OrtModel::inference(std::vector>&); +template std::vector OrtModel::inference(std::vector>&); + +// Release session +void OrtModel::release(bool profilingEnabled) +{ + mPImplOrt.reset(); +} + +// private +std::string OrtModel::printShape(const std::vector& v) +{ + std::stringstream ss(""); + for (size_t i = 0; i < v.size() - 1; i++) { + ss << v[i] << "x"; + } + ss << v[v.size() - 1]; + return ss.str(); +} + +std::string OrtModel::printShape(const std::vector>& v, std::vector& n) +{ + std::stringstream ss(""); + for (size_t i = 0; i < v.size(); i++) { + ss << n[i] << " -> ("; + for (size_t j = 0; j < v[i].size() - 1; j++) { + ss << v[i][j] << "x"; + } + ss << v[i][v[i].size() - 1] << "); "; + } + return ss.str(); +} + +} // namespace ml + +} // namespace o2 diff --git a/Common/MathUtils/CMakeLists.txt b/Common/MathUtils/CMakeLists.txt index 04fc2e9915028..733d776f6b492 100644 --- a/Common/MathUtils/CMakeLists.txt +++ b/Common/MathUtils/CMakeLists.txt @@ -16,6 +16,7 @@ o2_add_library( src/Chebyshev3D.cxx src/Chebyshev3DCalc.cxx src/SymMatrixSolver.cxx + src/Tsallis.cxx PUBLIC_LINK_LIBRARIES ROOT::Hist FairLogger::FairLogger @@ -23,7 +24,8 @@ o2_add_library( O2::GPUCommon ROOT::GenVector ROOT::Geom - Vc::Vc) + Vc::Vc + Boost::boost) o2_target_root_dictionary( MathUtils @@ -37,7 +39,9 @@ o2_target_root_dictionary( include/MathUtils/RandomRing.h include/MathUtils/Primitive2D.h include/MathUtils/SMatrixGPU.h - include/MathUtils/SymMatrixSolver.h) + include/MathUtils/SymMatrixSolver.h + include/MathUtils/Tsallis.h + include/MathUtils/LegendrePols.h) o2_add_test( CachingTF1 @@ -53,6 +57,13 @@ o2_add_test( PUBLIC_LINK_LIBRARIES O2::MathUtils LABELS utils) +o2_add_test( + Chebyshev3D + SOURCES test/testChebyshev3D.cxx + COMPONENT_NAME MathUtils + PUBLIC_LINK_LIBRARIES O2::MathUtils + LABELS utils) + o2_add_test( Utils SOURCES test/testUtils.cxx diff --git a/Common/MathUtils/include/MathUtils/BetheBlochAleph.h b/Common/MathUtils/include/MathUtils/BetheBlochAleph.h new file mode 100644 index 0000000000000..bd72faffb0503 --- /dev/null +++ b/Common/MathUtils/include/MathUtils/BetheBlochAleph.h @@ -0,0 +1,35 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef AliceO2_COMMON_BETHEBLOCH_H_ +#define AliceO2_COMMON_BETHEBLOCH_H_ + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" + +namespace o2::common +{ + +template +GPUdi() T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5) +{ + T beta = bg / o2::gpu::GPUCommonMath::Sqrt(static_cast(1.) + bg * bg); + + T aa = o2::gpu::GPUCommonMath::Pow(beta, kp4); + T bb = o2::gpu::GPUCommonMath::Pow(static_cast(1.) / bg, kp5); + bb = o2::gpu::GPUCommonMath::Log(kp3 + bb); + + return (kp2 - aa - bb) * kp1 / aa; +} + +} // namespace o2::common + +#endif diff --git a/Common/MathUtils/include/MathUtils/Cartesian.h b/Common/MathUtils/include/MathUtils/Cartesian.h index 14509def6efb1..d7e421ecd965b 100644 --- a/Common/MathUtils/include/MathUtils/Cartesian.h +++ b/Common/MathUtils/include/MathUtils/Cartesian.h @@ -34,8 +34,8 @@ #include "GPUCommonMath.h" #include "CartesianGPU.h" #include "SMatrixGPU.h" - #endif + #include "GPUROOTCartesianFwd.h" #include "GPUROOTSMatrixFwd.h" @@ -93,6 +93,9 @@ class Rotation2D sn = mSin; } + value_t getCos() const { return mCos; } + value_t getSin() const { return mSin; } + template Point3D operator()(const Point3D& v) const { // local->master @@ -247,31 +250,49 @@ class Transform3D : public ROOT::Math::Transform3D ClassDefNV(Transform3D, 1); }; #endif // Disable for GPU + +// Aliasing of the Dot(SVector a, SVector b) operation between SVectors +#if (!defined(GPUCA_STANDALONE) || !defined(DGPUCA_NO_ROOT)) && !defined(GPUCA_GPUCODE) && !defined(GPUCOMMONRTYPES_H_ACTIVE) +template +inline T Dot(const SVector& lhs, const SVector& rhs) +{ + return ROOT::Math::Dot(lhs, rhs); +} + +template +inline SMatrix> Similarity(const SMatrix& lhs, const SMatrix>& rhs) +{ + return ROOT::Math::Similarity(lhs, rhs); +} +#else +template +GPUdi() T Dot(const SVector& lhs, const SVector& rhs) +{ + return o2::math_utils::detail::Dot(lhs, rhs); +} + +template +GPUdi() SMatrix> Similarity(const SMatrix& lhs, const SMatrix>& rhs) +{ + return o2::math_utils::detail::Similarity(lhs, rhs); +} +#endif // Disable for GPU + } // namespace math_utils } // namespace o2 #if (!defined(GPUCA_STANDALONE) || !defined(DGPUCA_NO_ROOT)) && !defined(GPUCA_GPUCODE) && !defined(GPUCOMMONRTYPES_H_ACTIVE) std::ostream& operator<<(std::ostream& os, const o2::math_utils::Rotation2Df_t& t); std::ostream& operator<<(std::ostream& os, const o2::math_utils::Rotation2Dd_t& t); - -namespace std +namespace o2::framework { +template +struct is_forced_trivially_copyable; -/// Defining Point3D explicitly as trivially copyable -/// -/// std::is_trivially_copyable> fails because the class -/// implements a copy constructor, although it does not much more than the default copy -/// constructor. We need Point3D to fulfill the condition in order to make types -/// inheriting from it or using it as member can be safely detected as messageable. -/// -/// We believe that Point3D is messageable and explicitly specialize the type trait. -/// There is a unit test for checking trivial copy -/// This is a workaround, we will also make suggestions to fix the cause in ROOT itself -/// TODO: delete once it is fixed in ROOT template -struct is_trivially_copyable> : std::true_type { +struct is_forced_trivially_copyable> : std::true_type { }; -} // namespace std +} // namespace o2::framework #endif // Disable for GPU #endif diff --git a/Common/MathUtils/include/MathUtils/Chebyshev3DCalc.h b/Common/MathUtils/include/MathUtils/Chebyshev3DCalc.h index 0db2ec49ef752..5f611172928eb 100644 --- a/Common/MathUtils/include/MathUtils/Chebyshev3DCalc.h +++ b/Common/MathUtils/include/MathUtils/Chebyshev3DCalc.h @@ -18,6 +18,7 @@ #include // for TNamed #include // for FILE, stdout +#include // for std::fma #include "Rtypes.h" // for Float_t, UShort_t, Int_t, Double_t, etc class TString; @@ -208,9 +209,14 @@ inline Float_t Chebyshev3DCalc::chebyshevEvaluation1D(Float_t x, const Float_t* for (int i = ncf; i--;) { b2 = b1; b1 = b0; - b0 = array[i] + x2 * b1 - b2; + // Clenshaw recurrence, grouped as fma(x2, b1, array[i] - b2). Mathematically + // identical to `array[i] + x2 * b1 - b2`, but `array[i] - b2` does not depend + // on the just-updated b1, so the loop-carried chain collapses to a single FMA + // latency instead of a dependent multiply+add+subtract. This kernel dominates + // magnetic-field evaluation in (e.g.) muon track extrapolation. + b0 = std::fma(x2, b1, array[i] - b2); } - return b0 - x * b1; + return std::fma(-x, b1, b0); } /// Evaluates Chebyshev parameterization for 3D function. diff --git a/Common/MathUtils/include/MathUtils/LegendrePols.h b/Common/MathUtils/include/MathUtils/LegendrePols.h new file mode 100644 index 0000000000000..d5e6a259573f4 --- /dev/null +++ b/Common/MathUtils/include/MathUtils/LegendrePols.h @@ -0,0 +1,213 @@ +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file LegendrePols.h +/// \brief Definition of the NDim Legendre Polynominals +/// \author felix.schlepper@cern.ch + +#ifndef LEGENDRE_NDIM_POLYNOMINAL_H_ +#define LEGENDRE_NDIM_POLYNOMINAL_H_ + +#include "TNamed.h" +#include "Math/IParamFunction.h" +#include "TMatrixD.h" + +#include + +#include + +namespace o2::math_utils +{ + +// Defines the 1D Legendre Polynominals with coefficients: +// w(u) = c_0 + c_1 * u + c_2 * 0.5 * (3 * u * u - 1) + ... +// for u in [-1.0, 1.0] +class Legendre1DPolynominal final : public TNamed, + public ROOT::Math::IParametricFunctionOneDim +{ + public: + Legendre1DPolynominal() = default; + Legendre1DPolynominal(const Legendre1DPolynominal&) = default; + Legendre1DPolynominal(Legendre1DPolynominal&&) = delete; + Legendre1DPolynominal& operator=(const Legendre1DPolynominal&) = default; + Legendre1DPolynominal& operator=(Legendre1DPolynominal&&) = delete; + Legendre1DPolynominal(unsigned int order) : fOrder(order) {} + Legendre1DPolynominal(const std::vector p) + : fOrder(p.size() - 1), fParams(p) {} + + double operator()(double x) const { return DoEvalPar(x, Parameters()); } + double operator()(int i, double x) const + { + return DoEvalParSingle(i, x, Parameters()); + } + + ~Legendre1DPolynominal() final = default; + + const double* Parameters() const final { return &fParams.front(); } + + virtual void SetParameters(const double* p) final + { + fParams = std::vector(p, p + NPar()); + } + + unsigned int NPar() const final { return fParams.size(); } + unsigned int NOrder() const { return fOrder; } + + ROOT::Math::IBaseFunctionOneDim* Clone() const final { return new Legendre1DPolynominal(fParams); } + TObject* Clone(const char* name) const final + { + auto n = new Legendre1DPolynominal(fParams); + n->SetName(name); + return n; + } + + private: + double DoEvalPar(double x, const double* p) const final + { + double sum{0.0}; + for (unsigned int iOrder{0}; iOrder <= fOrder; ++iOrder) { + sum += p[iOrder] * boost::math::legendre_p(iOrder, x); + } + return sum; + } + + double DoEvalParSingle(int i, double x, const double* p) const + { + return p[i] * boost::math::legendre_p(i, x); + } + + unsigned int fOrder{0}; + std::vector fParams; + + ClassDefOverride(o2::math_utils::Legendre1DPolynominal, 1); +}; + +// Defines the 2D Legendre Polynominals with coefficients: +// w(u, v) = c_00 + +// c_10 * u + c_11 * v + +// c_20 * 0.5 * (3 * u * u - 1) + c_21 * u * v + c_22 * (3 * v * v - 1) + +/// .... +// for u&v in [-1.0, 1.0] +class Legendre2DPolynominal final : public TNamed, + public ROOT::Math::IParametricFunctionMultiDim +{ + public: + Legendre2DPolynominal() = default; + Legendre2DPolynominal(unsigned int order) : fOrder(order) {} + Legendre2DPolynominal(const std::vector& p) + : fOrder(p.size() - 1), fParams(p) {} + Legendre2DPolynominal(const TMatrixD& p) : fOrder(p.GetNrows() - 1) + { + fParams = std::vector(NPar()); + for (unsigned int iOrder{0}; iOrder <= fOrder; ++iOrder) { + for (unsigned int jOrder{0}; jOrder <= iOrder; ++jOrder) { + fParams[getFlatIdx(iOrder, jOrder)] = p(iOrder, jOrder); + } + } + } + ~Legendre2DPolynominal() final = default; + + double operator()(const double* x) const + { + return DoEvalPar(x, Parameters()); + } + double operator()(double x, double y) const { return DoEvalPar(x, y); } + double operator()(int i, int j, const double* x) const + { + return DoEvalParSingle(i, j, x, Parameters()); + } + double operator()(int i, int j, double x, double y) const + { + return DoEvalParSingle(i, j, x, y, Parameters()); + } + + const double* Parameters() const final { return &fParams.front(); } + + void SetParameters(const double* p) final + { + fParams = std::vector(p, p + NPar()); + } + + unsigned int NPar() const final + { + return fOrder * (fOrder + 1) / 2 + fOrder + 1; + } + unsigned int NDim() const final { return 2; } + unsigned int NOrder() const { return fOrder; } + + TMatrixD getCoefficients() const + { + TMatrixD m(fOrder + 1, fOrder + 1); + for (unsigned int iOrder{0}; iOrder <= fOrder; ++iOrder) { + for (unsigned int jOrder{0}; jOrder <= iOrder; ++jOrder) { + m(iOrder, jOrder) = fParams[getFlatIdx(iOrder, jOrder)]; + } + } + return m; + } + + void printCoefficients() const { getCoefficients().Print(); } + + // Unimplemented + ROOT::Math::IBaseFunctionMultiDim* Clone() const final { return new Legendre2DPolynominal(fParams); } + TObject* Clone(const char* name) const final + { + auto n = new Legendre2DPolynominal(fParams); + n->SetName(name); + return n; + } + + private: + double DoEvalPar(const double* x, const double* p) const final + { + double sum{0.0}; + for (unsigned int iOrder{0}; iOrder <= fOrder; ++iOrder) { + for (unsigned int jOrder{0}; jOrder <= iOrder; ++jOrder) { + sum += DoEvalParSingle(iOrder, jOrder, x, p); + } + } + return sum; + } + + double DoEvalPar(double x, double y) const + { + double sum{0.0}; + for (unsigned int iOrder{0}; iOrder <= fOrder; ++iOrder) { + for (unsigned int jOrder{0}; jOrder <= iOrder; ++jOrder) { + sum += DoEvalParSingle(iOrder, jOrder, x, y, Parameters()); + } + } + return sum; + } + + double DoEvalParSingle(int i, int j, const double* x, const double* p) const + { + return DoEvalParSingle(i, j, x[0], x[1], p); + } + + double DoEvalParSingle(int i, int j, double x, double y, + const double* p) const + { + return p[getFlatIdx(i, j)] * boost::math::legendre_p(j, x) * + boost::math::legendre_p(i - j, y); + } + + inline int getFlatIdx(int i, int j) const { return i * (i + 1) / 2 + j; } + + unsigned int fOrder{0}; + std::vector fParams; + + ClassDefOverride(o2::math_utils::Legendre2DPolynominal, 1); +}; + +} // namespace o2::math_utils + +#endif diff --git a/Common/MathUtils/include/MathUtils/SMatrixGPU.h b/Common/MathUtils/include/MathUtils/SMatrixGPU.h index 2faaf118d1daa..8158a93666a92 100644 --- a/Common/MathUtils/include/MathUtils/SMatrixGPU.h +++ b/Common/MathUtils/include/MathUtils/SMatrixGPU.h @@ -15,10 +15,6 @@ /// Only parts strictly requiring STD library have been changed. /// Also some utilities to have basic checks and printouts working on GPUs have been rewritten. /// -/// Notably only templated implementation of -/// row_offsets_utils::make and row_offsets_utils::do_make -/// has been reworked to support gpustd::array as backend. -/// /// Other than that, the author is not taking any credit on the methodologies implemented /// which have been taken straight from root source code /// @@ -29,11 +25,15 @@ #define ALICEO2_SMATRIX_GPU_H #include "GPUCommonDef.h" -#include "GPUCommonArray.h" #include "GPUCommonMath.h" #include "GPUCommonAlgorithm.h" +#include "GPUCommonLogger.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include +#include +#endif -namespace o2::math_utils +namespace o2::math_utils::detail { template struct Check { @@ -66,9 +66,9 @@ class SVectorGPU GPUd() SVectorGPU(); GPUd() SVectorGPU(const SVectorGPU& rhs); - GPUd() const T& operator[](unsigned int i) const; + GPUhd() const T& operator[](unsigned int i) const; + GPUhd() T& operator[](unsigned int i); GPUd() const T& operator()(unsigned int i) const; - GPUd() T& operator[](unsigned int i); GPUd() T& operator()(unsigned int i); GPUd() const T* Array() const; GPUd() T* Array(); @@ -113,19 +113,19 @@ GPUdi() const T* SVectorGPU::end() const } template -GPUdi() const T& SVectorGPU::operator[](unsigned int i) const +GPUhdi() const T& SVectorGPU::operator[](unsigned int i) const { return mArray[i]; } template -GPUdi() const T& SVectorGPU::operator()(unsigned int i) const +GPUhdi() T& SVectorGPU::operator[](unsigned int i) { return mArray[i]; } template -GPUdi() T& SVectorGPU::operator[](unsigned int i) +GPUdi() const T& SVectorGPU::operator()(unsigned int i) const { return mArray[i]; } @@ -140,7 +140,7 @@ template GPUd() SVectorGPU::SVectorGPU() { for (unsigned int i = 0; i < N; ++i) { - mArray[i] = 7; + mArray[i] = 0; } } @@ -204,7 +204,7 @@ GPUdi() SVectorGPU& SVectorGPU::operator-=(const SVectorGPU& r } template -GPUd() SVectorGPU& SVectorGPU::operator+=(const SVectorGPU& rhs) +GPUdi() SVectorGPU& SVectorGPU::operator+=(const SVectorGPU& rhs) { for (unsigned int i = 0; i < D; ++i) { mArray[i] += rhs.apply(i); @@ -283,14 +283,14 @@ struct make_indices : make_indices_impl<0, indices<>, N> { }; template -constexpr auto do_make(F f, indices) -> gpu::gpustd::array +constexpr auto do_make(F f, indices) -> std::array { - gpu::gpustd::array retarr = {f(I0 + I)...}; + std::array retarr = {f(I0 + I)...}; return retarr; } template -constexpr auto make(F f) -> gpu::gpustd::array +constexpr auto make(F f) -> std::array { return do_make(f, typename make_indices::type()); } @@ -303,31 +303,11 @@ class MatRepSymGPU public: typedef T value_type; GPUdDefault() MatRepSymGPU() = default; - GPUdi() T& operator()(unsigned int i, unsigned int j) - { - return mArray[offset(i, j)]; - } - - GPUdi() T const& operator()(unsigned int i, unsigned int j) const - { - return mArray[offset(i, j)]; - } - - GPUdi() T& operator[](unsigned int i) - { - return mArray[off(i)]; - } - - GPUdi() T const& operator[](unsigned int i) const - { - return mArray[off(i)]; - } - - GPUdi() T apply(unsigned int i) const - { - return mArray[off(i)]; - } - + GPUdi() T& operator()(unsigned int i, unsigned int j) { return mArray[offset(i, j)]; } + GPUdi() T const& operator()(unsigned int i, unsigned int j) const { return mArray[offset(i, j)]; } + GPUhdi() T& operator[](unsigned int i) { return mArray[off(i)]; } + GPUdi() T const& operator[](unsigned int i) const { return mArray[off(i)]; } + GPUdi() T apply(unsigned int i) const { return mArray[off(i)]; } GPUdi() T* Array() { return mArray; } GPUdi() const T* Array() const { return mArray; } @@ -469,6 +449,8 @@ class SMatrixGPU GPUdi() SMatrixGPU(SMatrixNoInit) {} GPUd() SMatrixGPU(SMatrixIdentity); GPUd() SMatrixGPU(const SMatrixGPU& rhs); + template + GPUd() SMatrixGPU(const SMatrixGPU& rhs); template GPUd() SMatrixGPU(const Expr& rhs); template @@ -480,14 +462,18 @@ class SMatrixGPU kCols = D2, // columns kSize = D1 * D2 // rows*columns }; - GPUd() T apply(unsigned int i) const; - GPUd() const T* Array() const; - GPUd() T* Array(); + // https://root.cern/doc/master/SMatrix_8icc_source.html#l00627 + GPUd() T apply(unsigned int i) const { return mRep[i]; } + GPUd() const T* Array() const { return mRep.Array(); } + GPUd() T* Array() { return mRep.Array(); } GPUd() iterator begin(); GPUd() iterator end(); GPUd() const T& operator()(unsigned int i, unsigned int j) const; GPUd() T& operator()(unsigned int i, unsigned int j); + template + GPUd() friend X& operator<<(Y& y, const SMatrixGPU&); + class SMatrixRowGPU { public: @@ -517,6 +503,14 @@ class SMatrixGPU GPUd() SMatrixRowGPUconst operator[](unsigned int i) const { return SMatrixRowGPUconst(*this, i); } GPUd() SMatrixRowGPU operator[](unsigned int i) { return SMatrixRowGPU(*this, i); } + template + GPUd() SMatrixGPU& operator+=(const SMatrixGPU& rhs); + GPUd() SMatrixGPU& operator*=(const T& rhs); + template + GPUd() SMatrixGPU& operator*=(const SMatrixGPU& rhs); + template + GPUd() SMatrixGPU& operator*=(const Expr& rhs); + GPUd() bool Invert(); GPUd() bool IsInUse(const T* p) const; @@ -524,6 +518,15 @@ class SMatrixGPU R mRep; }; +#ifndef __OPENCL__ // TODO: current C++ for OpenCL 2021 is at C++17, so no concepts. But we don't need this trick for OpenCL anyway, so we can just hide it. +template + requires(sizeof(typename X::traits_type::pos_type) != 0) // do not provide a template to fair::Logger, etc... (pos_type is a member type of all std::ostream classes) +GPUd() X& operator<<(Y& y, const SMatrixGPU&) +{ + return y; +} +#endif + template GPUdi() SMatrixGPU::SMatrixGPU(SMatrixIdentity) { @@ -547,6 +550,13 @@ GPUdi() SMatrixGPU::SMatrixGPU(const SMatrixGPU& rhs mRep = rhs.mRep; } +template +template +GPUd() SMatrixGPU::SMatrixGPU(const SMatrixGPU& rhs) +{ + operator=(rhs); +} + template GPUdi() T* SMatrixGPU::begin() { @@ -675,7 +685,7 @@ GPUdi() SMatrixGPU& SMatrixGPU::operator=(const Expr template template -GPUdi() SMatrixGPU& SMatrixGPU::operator=(const M& rhs) +GPUdi() SMatrixGPU& SMatrixGPU::operator=(const M & rhs) { mRep = rhs.mRep; return *this; @@ -980,7 +990,7 @@ GPUdi() void Inverter::InvertBunchKaufman(MatRepSymGPU& rhs, int& if // invert D(j:j+1,j:j+1) temp2 = *mjj * *(mjj + j + 1) - *(mjj + j) * *(mjj + j); if (temp2 == 0) { - printf("SymMatrix::bunch_invert: error in pivot choice"); + LOGF(error, "SymMatrix::bunch_invert: error in pivot choice"); } temp2 = 1. / temp2; // this quotient is guaranteed to exist by the choice @@ -1067,10 +1077,10 @@ GPUdi() void Inverter::InvertBunchKaufman(MatRepSymGPU& rhs, int& if } *mjj -= static_cast(temp2); } - } else //2x2 pivot, compute columns j and j-1 of the inverse + } else // 2x2 pivot, compute columns j and j-1 of the inverse { if (piv[j - 1] != 0) { - printf("error in piv %lf \n", piv[j - 1]); + LOGF(error, "error in piv %lf \n", static_cast(piv[j - 1])); } s = 2; if (j < nrow) { @@ -1344,7 +1354,7 @@ GPUdi() int Inverter::DfinvMatrix(MatRepStdGPU& rhs, unsigned int for (unsigned int i = 1; i < n; i++) { unsigned int ni = n - i; mIter mij = mi; - //int j; + // int j; for (unsigned j = 1; j <= i; j++) { s33 = *mij; mIter mikj = mi + n + j - 1; @@ -1398,6 +1408,37 @@ GPUdi() bool SMatrixGPU::Invert() return Inverter::Dinv((*this).mRep); } +template +template +GPUdi() SMatrixGPU& SMatrixGPU::operator+=(const SMatrixGPU& rhs) +{ + mRep += rhs.mRep; + return *this; +} + +template +GPUdi() SMatrixGPU& SMatrixGPU::operator*=(const T & rhs) +{ + for (unsigned int i = 0; i < R::kSize; ++i) { + mRep.Array()[i] *= rhs; + } + return *this; +} + +template +template +GPUdi() SMatrixGPU& SMatrixGPU::operator*=(const SMatrixGPU& rhs) +{ + return operator=(*this* rhs); +} + +template +template +GPUdi() SMatrixGPU& SMatrixGPU::operator*=(const Expr& rhs) +{ + return operator=(*this* rhs); +} + template struct TranspPolicyGPU { enum { @@ -1424,6 +1465,7 @@ class TransposeOpGPU { return mRhs.apply((i % D1) * D2 + i / D1); } + GPUdi() T operator()(unsigned int i, unsigned j) const { return mRhs(j, i); @@ -1467,5 +1509,5 @@ GPUdi() SMatrixGPU> Similarity(const SMatrixGPU1) used to check for sampling + /// \param mass particles mass (use pion if not known) + static bool downsampleTsallisCharged(float pt, float factorPt, float sqrts, float& weight, float rnd, float mass = 0.13957); +}; + +} // namespace math_utils +} // namespace o2 + +#endif diff --git a/Common/MathUtils/include/MathUtils/Utils.h b/Common/MathUtils/include/MathUtils/Utils.h index 618d3e379f6d2..3c51245fc6c29 100644 --- a/Common/MathUtils/include/MathUtils/Utils.h +++ b/Common/MathUtils/include/MathUtils/Utils.h @@ -128,17 +128,17 @@ GPUdi() void rotateZd(double xL, double yL, double& xG, double& yG, double snAlp return detail::rotateZ(xL, yL, xG, yG, snAlp, csAlp); } -#ifndef GPUCA_GPUCODE_DEVICE -inline void rotateZInv(float xG, float yG, float& xL, float& yL, float snAlp, float csAlp) +GPUdi() void rotateZInv(float xG, float yG, float& xL, float& yL, float snAlp, float csAlp) { detail::rotateZInv(xG, yG, xL, yL, snAlp, csAlp); } -inline void rotateZInvd(double xG, double yG, double& xL, double& yL, double snAlp, double csAlp) +GPUdi() void rotateZInvd(double xG, double yG, double& xL, double& yL, double snAlp, double csAlp) { detail::rotateZInv(xG, yG, xL, yL, snAlp, csAlp); } +#ifndef GPUCA_GPUCODE_DEVICE inline std::tuple rotateZInv(float xG, float yG, float snAlp, float csAlp) { return detail::rotateZInv(xG, yG, snAlp, csAlp); @@ -210,12 +210,12 @@ inline double angle2Alphad(double phi) return detail::angle2Alpha(phi); } -GPUhdi() float fastATan2(float y, float x) +GPUhdi() constexpr float fastATan2(float y, float x) { return detail::fastATan2(y, x); } -GPUhdi() double fastATan2d(double y, double x) +GPUhdi() constexpr double fastATan2d(double y, double x) { return detail::fastATan2(y, x); } diff --git a/Common/MathUtils/include/MathUtils/detail/Bracket.h b/Common/MathUtils/include/MathUtils/detail/Bracket.h index 25d0dbd1d0a6e..2da6949c4a6f8 100644 --- a/Common/MathUtils/include/MathUtils/detail/Bracket.h +++ b/Common/MathUtils/include/MathUtils/detail/Bracket.h @@ -17,7 +17,7 @@ #define ALICEO2_BRACKET_H #include -#ifndef GPUCA_ALIGPUCODE +#ifndef GPUCA_GPUCODE_DEVICE #include #include #endif @@ -76,7 +76,7 @@ class Bracket Relation isOutside(T t, T tErr) const; Relation isOutside(T t) const; -#ifndef GPUCA_ALIGPUCODE +#ifndef GPUCA_GPUCODE_DEVICE std::string asString() const; #endif @@ -247,7 +247,7 @@ inline typename Bracket::Relation Bracket::isOutside(T t) const return t < mMin ? Below : (t > mMax ? Above : Inside); } -#ifndef GPUCA_ALIGPUCODE +#ifndef GPUCA_GPUCODE_DEVICE template std::string Bracket::asString() const { diff --git a/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h b/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h index 50a09804da6ed..abb8a716cc5ee 100644 --- a/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h +++ b/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h @@ -66,6 +66,7 @@ struct StatAccumulator { sum += other.sum; sum2 += other.sum2; wsum += other.wsum; + n += other.n; return *this; } diff --git a/Common/MathUtils/include/MathUtils/detail/basicMath.h b/Common/MathUtils/include/MathUtils/detail/basicMath.h index 5dd66182e47d8..059dd4cca66fb 100644 --- a/Common/MathUtils/include/MathUtils/detail/basicMath.h +++ b/Common/MathUtils/include/MathUtils/detail/basicMath.h @@ -16,14 +16,15 @@ #ifndef MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BASICMATH_H_ #define MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BASICMATH_H_ +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "CommonConstants/MathConstants.h" + #ifndef GPUCA_GPUCODE_DEVICE #include #include +#include #endif -#include "GPUCommonArray.h" -#include "GPUCommonDef.h" -#include "GPUCommonMath.h" -#include "CommonConstants/MathConstants.h" namespace o2 { @@ -33,7 +34,7 @@ namespace detail { template -GPUhdi() T copysign(T x, T y) +GPUhdi() constexpr T copysign(T x, T y) { return o2::gpu::GPUCommonMath::Copysign(x, y); } @@ -65,7 +66,7 @@ GPUhdi() T abs(T x) template GPUdi() int nint(T x) { - return o2::gpu::GPUCommonMath::Nint(x); + return o2::gpu::GPUCommonMath::Float2IntRn(x); }; template @@ -113,7 +114,11 @@ GPUdi() int nint(double x) template <> GPUdi() bool finite(double x) { +#ifdef __FAST_MATH__ + return false; +#else return std::isfinite(x); +#endif } template <> GPUdi() double log(double x) @@ -126,4 +131,4 @@ GPUdi() double log(double x) } // namespace math_utils } // namespace o2 -#endif /* MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BASICMATH_H_ */ \ No newline at end of file +#endif /* MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BASICMATH_H_ */ diff --git a/Common/MathUtils/include/MathUtils/detail/trigonometric.h b/Common/MathUtils/include/MathUtils/detail/trigonometric.h index a216b80aaa203..e13d965663dc9 100644 --- a/Common/MathUtils/include/MathUtils/detail/trigonometric.h +++ b/Common/MathUtils/include/MathUtils/detail/trigonometric.h @@ -16,16 +16,17 @@ #ifndef MATHUTILS_INCLUDE_MATHUTILS_DETAIL_TRIGONOMETRIC_H_ #define MATHUTILS_INCLUDE_MATHUTILS_DETAIL_TRIGONOMETRIC_H_ -#ifndef GPUCA_GPUCODE_DEVICE -#include -#include -#endif -#include "GPUCommonArray.h" #include "GPUCommonDef.h" #include "GPUCommonMath.h" #include "CommonConstants/MathConstants.h" #include "MathUtils/detail/basicMath.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include +#include +#include +#endif + namespace o2 { namespace math_utils @@ -156,7 +157,7 @@ GPUhdi() std::tuple rotateZInv(T xG, T yG, T snAlp, T csAlp) #endif template -GPUhdi() void rotateZ(gpu::gpustd::array& xy, T alpha) +GPUhdi() void rotateZ(std::array& xy, T alpha) { // transforms vector in tracking frame alpha to global frame T sin, cos; @@ -196,7 +197,7 @@ GPUhdi() int angle2Sector(T phi) if (phi < 0.f) { sect += o2::constants::math::NSectors - 1; } - return sect; + return sect % o2::constants::math::NSectors; } template @@ -268,7 +269,7 @@ GPUhdi() constexpr T deltaPhiSmall(T phi0, T phi1) } template -GPUhdi() T fastATan2(T y, T x) +GPUhdi() constexpr T fastATan2(T y, T x) { // Fast atan2(y,x) for any angle [-Pi,Pi] // Average inaccuracy: 0.00048 @@ -282,7 +283,7 @@ GPUhdi() T fastATan2(T y, T x) // https://stackoverflow.com/questions/42537957/fast-accurate-atan-arctan-approximation-algorithm constexpr T A = 0.0776509570923569; constexpr T B = -0.287434475393028; - constexpr T C = (Pi / 4 - A - B); + constexpr T C = ((Pi / 4) - A - B); const T a2 = a * a; return ((A * a2 + B) * a2 + C) * a; }; @@ -297,10 +298,10 @@ GPUhdi() T fastATan2(T y, T x) T tan = 0; if (xx < 0) { // p1 is in the range [Pi/4, 3*Pi/4] phi0 = Pi075; - tan = -x1 / y1; + tan = y1 > T(0) ? -x1 / y1 : T(0); // yy is always >=0, hence y1>=0 } else { // p1 is in the range [-Pi/4, Pi/4] phi0 = Pi025; - tan = y1 / x1; + tan = x1 > T(0) ? y1 / x1 : T(0); } return phi0 + atan(tan); }; diff --git a/Common/MathUtils/include/MathUtils/fit.h b/Common/MathUtils/include/MathUtils/fit.h index 1f7223605a99b..cd5cb415070d3 100644 --- a/Common/MathUtils/include/MathUtils/fit.h +++ b/Common/MathUtils/include/MathUtils/fit.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "Rtypes.h" #include "TLinearFitter.h" @@ -59,7 +60,7 @@ template TFitResultPtr fit(const size_t nBins, const T* arr, const T xMin, const T xMax, TF1& func, std::string_view option = "") { Foption_t fitOption; - ROOT::Fit::FitOptionsMake(ROOT::Fit::kHistogram, option.data(), fitOption); + ROOT::Fit::FitOptionsMake(ROOT::Fit::EFitObjectType::kHistogram, option.data(), fitOption); ROOT::Fit::DataRange range(xMin, xMax); ROOT::Fit::DataOptions opt; @@ -69,9 +70,9 @@ TFitResultPtr fit(const size_t nBins, const T* arr, const T xMin, const T xMax, // create an empty TFitResult std::shared_ptr tfr(new TFitResult()); // create the fitter from an empty fit result - //std::shared_ptr fitter(new ROOT::Fit::Fitter(std::static_pointer_cast(tfr) ) ); + // std::shared_ptr fitter(new ROOT::Fit::Fitter(std::static_pointer_cast(tfr) ) ); ROOT::Fit::Fitter fitter(tfr); - //ROOT::Fit::FitConfig & fitConfig = fitter->Config(); + // ROOT::Fit::FitConfig & fitConfig = fitter->Config(); const double binWidth = double(xMax - xMin) / double(nBins); @@ -225,8 +226,8 @@ bool medmadGaus(size_t nBins, const T* arr, const T xMin, const T xMax, std::arr /// -1: only one point has been used for the calculation - center of gravity was uesed for calculation /// -4: invalid result!! /// -//template -//Double_t fitGaus(const size_t nBins, const T *arr, const T xMin, const T xMax, std::vector& param); +// template +// Double_t fitGaus(const size_t nBins, const T *arr, const T xMin, const T xMax, std::vector& param); template Double_t fitGaus(const size_t nBins, const T* arr, const T xMin, const T xMax, std::vector& param) { @@ -301,7 +302,7 @@ Double_t fitGaus(const size_t nBins, const T* arr, const T xMin, const T xMax, s Double_t chi2 = 0; if (npoints >= 3) { if (npoints == 3) { - //analytic calculation of the parameters for three points + // analytic calculation of the parameters for three points A.Invert(); TMatrixD res(1, 3); res.Mult(A, b); @@ -334,7 +335,7 @@ Double_t fitGaus(const size_t nBins, const T* arr, const T xMin, const T xMax, s } if (npoints == 2) { - //use center of gravity for 2 points + // use center of gravity for 2 points meanCOG /= sumCOG; rms2COG /= sumCOG; param[0] = max; @@ -524,7 +525,7 @@ R median(std::vector v) auto n = v.size() / 2; nth_element(v.begin(), v.begin() + n, v.end()); auto med = R{v[n]}; - if (!(v.size() & 1)) { //If the set size is even + if (!(v.size() & 1)) { // If the set size is even auto max_it = max_element(v.begin(), v.begin() + n); med = R{(*max_it + med) / 2.0}; } @@ -712,6 +713,245 @@ bool LTMUnbinnedSig(const std::vector& data, std::vector& index, std: params[4] = params[3] / std::sqrt(2.0); // error on RMS return true; } + +//___________________________________________________________________ +template +T selKthMin(int k, int np, T* arr) +{ + // Returns the k th smallest value in the array. The input array will be rearranged + // to have this value in location arr[k] , with all smaller elements moved before it + // (in arbitrary order) and all larger elements after (also in arbitrary order). + // From Numerical Recipes in C++ + + int i, j, mid, ir = np - 1, l = 0; + T a; + for (;;) { + if (ir <= l + 1) { + if (ir == l + 1 && arr[ir] < arr[l]) { + std::swap(arr[l], arr[ir]); + } + return arr[k]; + } else { + int mid = (l + ir) >> 1, i = l + 1; + std::swap(arr[mid], arr[i]); + if (arr[i] > arr[ir]) { + std::swap(arr[i], arr[ir]); + } + if (arr[l] > arr[ir]) { + std::swap(arr[l], arr[ir]); + } + if (arr[i] > arr[l]) { + std::swap(arr[i], arr[l]); + } + j = ir; + a = arr[l]; + for (;;) { + do { + i++; + } while (arr[i] < a); + do { + j--; + } while (arr[j] > a); + if (j < i) { + break; + } + std::swap(arr[i], arr[j]); + } + arr[l] = arr[j]; + arr[j] = a; + if (j >= k) { + ir = j - 1; + } + if (j <= k) { + l = i; + } + } + } +} + +//___________________________________________________________________ +template +T MAD2Sigma(int np, T* y) +{ + // Sigma calculated from median absolute deviations, https://en.wikipedia.org/wiki/Median_absolute_deviation + // the input array is modified + if (np < 2) { + return 0; + } + int nph = np >> 1; + float median = (np & 0x1) ? selKthMin(nph, np, y) : 0.5 * (selKthMin(nph - 1, np, y) + selKthMin(nph, np, y)); + // build abs differences to median + for (int i = np; i--;) { + y[i] = std::abs(y[i] - median); + } + // now get median of abs deviations + median = (np & 0x1) ? selKthMin(nph, np, y) : 0.5 * (selKthMin(nph - 1, np, y) + selKthMin(nph, np, y)); + return median * 1.4826; // convert to Gaussian sigma +} + +/// \return returns the index of the closest timestamps to the left and right of the given timestamp +/// \param timestamps vector of timestamps +/// \param timestamp the timestamp to find the closest timestamps for +template +std::optional> findClosestIndices(const std::vector& timestamps, DataTime timestamp) +{ + if (timestamps.empty()) { + LOGP(warning, "Timestamp vector is empty!"); + return std::nullopt; + } + + if (timestamp <= timestamps.front()) { + return std::pair{0, 0}; + } else if (timestamp >= timestamps.back()) { + return std::pair{timestamps.size() - 1, timestamps.size() - 1}; + } + + const auto it = std::lower_bound(timestamps.begin(), timestamps.end(), timestamp); + const size_t idx = std::distance(timestamps.begin(), it); + const auto prevTimestamp = timestamps[idx - 1]; + const auto nextTimestamp = timestamps[idx]; + return std::pair{(idx - 1), idx}; +} + +struct RollingStats { + RollingStats() = default; + RollingStats(const int nValues) + { + median.resize(nValues); + std.resize(nValues); + nPoints.resize(nValues); + closestDistanceL.resize(nValues); + closestDistanceR.resize(nValues); + } + + std::vector median; ///< median of rolling data + std::vector std; ///< std of rolling data + std::vector nPoints; ///< number of points used for the calculation + std::vector closestDistanceL; ///< distance of closest point to the left + std::vector closestDistanceR; ///< distance of closest point to the right + + ClassDefNV(RollingStats, 1); +}; + +/// \brief calculates the rolling statistics of the input data +/// \return returns the rolling statistics +/// \param timeData times of the input data (assumed to be sorted) +/// \param data values of the input data +/// \param times times for which to calculate the rolling statistics +/// \param deltaMax time range for which the rolling statistics is calculated +/// \param mNthreads number of threads to use for the calculation +/// \param minPoints minimum number of points to use for the calculation of the statistics - otherwise use nearest nClosestPoints points weighted with distance +/// \param nClosestPoints number of closest points in case of number of points in given range is smaller than minPoints +template +RollingStats getRollingStatistics(const DataTimeType& timeData, const DataType& data, const DataTime& times, const double deltaMax, const int mNthreads, const size_t minPoints = 4, const size_t nClosestPoints = 4) +{ + // output statistics + const size_t vecSize = times.size(); + RollingStats stats(vecSize); + + if (!std::is_sorted(timeData.begin(), timeData.end())) { + LOGP(error, "Input data is NOT sorted!"); + return stats; + } + + if (timeData.empty()) { + LOGP(error, "Input data is empty!"); + return stats; + } + + const size_t dataSize = data.size(); + const size_t timeDataSize = timeData.size(); + if (timeDataSize != dataSize) { + LOGP(error, "Input data has different sizes {}!={}", timeDataSize, dataSize); + return stats; + } + + auto myThread = [&](int iThread) { + // data in given time window for median calculation + DataType window; + for (size_t i = iThread; i < vecSize; i += mNthreads) { + const double timeI = times[i]; + + // lower index + const double timeStampLower = timeI - deltaMax; + const auto lower = std::lower_bound(timeData.begin(), timeData.end(), timeStampLower); + size_t idxStart = std::distance(timeData.begin(), lower); + + // upper index + const double timeStampUpper = timeI + deltaMax; + const auto upper = std::lower_bound(timeData.begin(), timeData.end(), timeStampUpper); + size_t idxEnd = std::distance(timeData.begin(), upper); + + // closest data point + if (auto idxClosest = findClosestIndices(timeData, timeI)) { + auto [idxLeft, idxRight] = *idxClosest; + const auto closestL = std::abs(timeData[idxLeft] - timeI); + const auto closestR = std::abs(timeData[idxRight] - timeI); + stats.closestDistanceL[i] = closestL; + stats.closestDistanceR[i] = closestR; + + // if no points are in the range use the n closest points - n from the left and n from the right + const size_t reqSize = idxEnd - idxStart; + if (reqSize < minPoints) { + // calculate weighted average + idxStart = (idxRight > nClosestPoints) ? (idxRight - nClosestPoints) : 0; + idxEnd = std::min(data.size(), idxRight + nClosestPoints); + constexpr float epsilon = 1e-6f; + double weightedSum = 0.0; + double weightTotal = 0.0; + for (size_t j = idxStart; j < idxEnd; ++j) { + const double dist = std::abs(timeI - timeData[j]); + const double weight = 1.0 / (dist + epsilon); + weightedSum += weight * data[j]; + weightTotal += weight; + } + stats.median[i] = (weightTotal > 0.) ? (weightedSum / weightTotal) : 0.0f; + } else { + // calculate statistics + stats.nPoints[i] = reqSize; + + if (idxStart >= data.size()) { + stats.median[i] = data.back(); + continue; + } + + if (reqSize <= 1) { + stats.median[i] = data[idxStart]; + continue; + } + + // calculate median + window.clear(); + if (reqSize > window.capacity()) { + window.reserve(static_cast(reqSize * 1.5)); + } + window.insert(window.end(), data.begin() + idxStart, data.begin() + idxEnd); + const size_t middle = window.size() / 2; + std::nth_element(window.begin(), window.begin() + middle, window.end()); + stats.median[i] = (window.size() % 2 == 1) ? window[middle] : ((window[middle - 1] + window[middle]) / 2.0); + + // calculate the stdev + const float mean = std::accumulate(window.begin(), window.end(), 0.0f) / window.size(); + std::transform(window.begin(), window.end(), window.begin(), [mean](const float val) { return val - mean; }); + const float sqsum = std::inner_product(window.begin(), window.end(), window.begin(), 0.0f); + const float stdev = std::sqrt(sqsum / window.size()); + stats.std[i] = stdev; + } + } + } + }; + + std::vector threads(mNthreads); + for (int i = 0; i < mNthreads; i++) { + threads[i] = std::thread(myThread, i); + } + + for (auto& th : threads) { + th.join(); + } + return stats; +} + } // namespace math_utils } // namespace o2 #endif diff --git a/Common/MathUtils/src/MathUtilsLinkDef.h b/Common/MathUtils/src/MathUtilsLinkDef.h index d042a054c559e..0b070e537afcd 100644 --- a/Common/MathUtils/src/MathUtilsLinkDef.h +++ b/Common/MathUtils/src/MathUtilsLinkDef.h @@ -41,5 +41,11 @@ #pragma link C++ class o2::math_utils::IntervalXYd_t + ; #pragma link C++ class o2::math_utils::Bracketf_t + ; #pragma link C++ class o2::math_utils::Bracketd_t + ; +#pragma link C++ class o2::math_utils::Tsallis + ; + +#pragma link C++ class o2::math_utils::Legendre1DPolynominal + ; +#pragma link C++ class o2::math_utils::Legendre2DPolynominal + ; + +#pragma link C++ class o2::math_utils::RollingStats + ; #endif diff --git a/Common/MathUtils/src/Tsallis.cxx b/Common/MathUtils/src/Tsallis.cxx new file mode 100644 index 0000000000000..850c249f0441f --- /dev/null +++ b/Common/MathUtils/src/Tsallis.cxx @@ -0,0 +1,39 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "MathUtils/Tsallis.h" +#include + +namespace o2::math_utils +{ + +float Tsallis::tsallisCharged(float pt, float mass, float sqrts) +{ + const float a = 6.81; + const float b = 59.24; + const float c = 0.082; + const float d = 0.151; + const float mt = std::sqrt(mass * mass + pt * pt); + const float n = a + b / sqrts; + const float T = c + d / sqrts; + const float p0 = n * T; + return std::pow((1. + mt / p0), -n) * pt; +} + +bool Tsallis::downsampleTsallisCharged(float pt, float factorPt, float sqrts, float& weight, float rnd, float mass) +{ + const float prob = tsallisCharged(pt, mass, sqrts); + const float probNorm = tsallisCharged(1., mass, sqrts); + weight = prob / probNorm; + return (rnd * (weight * pt * pt)) < factorPt; +} + +} // namespace o2::math_utils diff --git a/Common/MathUtils/test/testCartesian.cxx b/Common/MathUtils/test/testCartesian.cxx index ec04c34670fc3..9f2b4c912007e 100644 --- a/Common/MathUtils/test/testCartesian.cxx +++ b/Common/MathUtils/test/testCartesian.cxx @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(Cartesian_test) BOOST_AUTO_TEST_CASE(Point3D_messageable) { using ElementType = math_utils::Point3D; - static_assert(std::is_trivially_copyable::value == true); + static_assert(o2::framework::is_forced_trivially_copyable::value == true); std::vector pts(10); auto makeElement = [](int idx) { return ElementType{idx, idx + 10, idx + 20}; diff --git a/Common/MathUtils/test/testChebyshev3D.cxx b/Common/MathUtils/test/testChebyshev3D.cxx new file mode 100644 index 0000000000000..6f8143977c7f2 --- /dev/null +++ b/Common/MathUtils/test/testChebyshev3D.cxx @@ -0,0 +1,101 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file testChebyshev3D.cxx +/// \brief Accuracy of the Chebyshev3D evaluation kernel. +/// +/// Guards `Chebyshev3DCalc::Eval` / `chebyshevEvaluation1D` (the Clenshaw +/// recurrence that dominates magnetic-field evaluation in track extrapolation). +/// We build an in-memory parameterization of a known smooth function and check +/// that `Eval` reproduces it to the requested precision over many random points, +/// and that the per-dimension and double-precision overloads agree with the +/// float vector overload. Any breakage of the recurrence (e.g. a wrong FMA +/// regrouping) makes the reproduction error explode and fails the test. + +#define BOOST_TEST_MODULE Test Chebyshev3D +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include +#include +#include +#include "MathUtils/Chebyshev3D.h" + +using o2::math_utils::Chebyshev3D; + +namespace +{ +// A smooth, low-degree (≤3 per variable) vector function over the fit box, of +// the kind a Chebyshev parameterization reproduces to ~float precision. Stands +// in for a slowly-varying magnetic field B(x,y,z). +void referenceField(float* in, float* out) +{ + const float x = in[0], y = in[1], z = in[2]; + out[0] = 0.50f + 0.020f * x - 1.0e-4f * x * y + 3.0e-3f * z - 2.0e-6f * x * x * z; + out[1] = -0.30f + 0.015f * y + 5.0e-5f * y * z - 1.0e-3f * x; + out[2] = 5.00f - 4.0e-4f * x * x + 6.0e-4f * y * y + 1.0e-3f * x * y - 2.0e-6f * x * y * z; +} +} // namespace + +BOOST_AUTO_TEST_CASE(Chebyshev3D_eval_accuracy) +{ + const Float_t bmin[3] = {-40.f, -40.f, -200.f}; + const Float_t bmax[3] = {40.f, 40.f, 200.f}; + const Int_t np[3] = {7, 7, 7}; // > polynomial degree in every dimension + const Float_t fitPrec = 1.0e-5f; + + Chebyshev3D cheb(referenceField, 3, bmin, bmax, np, fitPrec); + + // Deterministic interior sampling (fixed seed -> no flakiness). Stay a hair + // inside the box so we never hit the boundary-clamping branch. + std::mt19937 rng(20260604u); + std::uniform_real_distribution ux(bmin[0] + 1.f, bmax[0] - 1.f); + std::uniform_real_distribution uy(bmin[1] + 1.f, bmax[1] - 1.f); + std::uniform_real_distribution uz(bmin[2] + 1.f, bmax[2] - 1.f); + + float maxAbsErr = 0.f; // |cheb - reference| (kernel reproduces the function) + float maxDimMismatch = 0.f; // |vector overload - per-dim overload| + float maxDoubleMismatch = 0.f; // |float overload - double overload| + + for (int i = 0; i < 20000; ++i) { + float par[3] = {ux(rng), uy(rng), uz(rng)}; + float ref[3]; + referenceField(par, ref); + + float res[3]; + cheb.Eval(par, res); + + double pard[3] = {par[0], par[1], par[2]}; + double resd[3]; + cheb.Eval(pard, resd); + + for (int d = 0; d < 3; ++d) { + BOOST_REQUIRE(std::isfinite(res[d])); + maxAbsErr = std::max(maxAbsErr, std::abs(res[d] - ref[d])); + // Single-component overload must match the vector overload (same kernel). + maxDimMismatch = std::max(maxDimMismatch, std::abs(res[d] - cheb.Eval(par, d))); + // Double overload differs only by intermediate precision. + maxDoubleMismatch = std::max(maxDoubleMismatch, std::abs(static_cast(resd[d]) - res[d])); + } + } + + BOOST_TEST_MESSAGE("Chebyshev3D max |eval - reference| = " << maxAbsErr); + BOOST_TEST_MESSAGE("Chebyshev3D max vector-vs-perdim = " << maxDimMismatch); + BOOST_TEST_MESSAGE("Chebyshev3D max float-vs-double = " << maxDoubleMismatch); + + // Reproduction of the known function: fit precision (1e-5) plus a little float + // slack from the three nested Clenshaw sums (observed ~1.4e-6). A broken + // recurrence misses this by orders of magnitude (coefficient-scale error / NaN). + BOOST_CHECK_SMALL(maxAbsErr, 1.0e-4f); + // The two float entry points share the kernel: expect bit-for-bit agreement. + BOOST_CHECK_SMALL(maxDimMismatch, 1.0e-6f); + // float vs double evaluation of the same coefficients: within float epsilon. + BOOST_CHECK_SMALL(maxDoubleMismatch, 1.0e-3f); +} diff --git a/Common/SimConfig/CMakeLists.txt b/Common/SimConfig/CMakeLists.txt index aa78dd8ad1a34..65d30935904ad 100644 --- a/Common/SimConfig/CMakeLists.txt +++ b/Common/SimConfig/CMakeLists.txt @@ -12,10 +12,15 @@ o2_add_library(SimConfig SOURCES src/SimConfig.cxx src/SimParams.cxx + src/SimDLLoader.cxx src/SimUserDecay.cxx - src/DigiParams.cxx src/G4Params.cxx + src/DigiParams.cxx + src/G4Params.cxx + src/DetectorLists.cxx src/MatMapParams.cxx src/InteractionDiamondParam.cxx + src/GlobalProcessCutSimParam.cxx + src/FluenceWeightCalculator.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils O2::DetectorsCommonDataFormats O2::SimulationDataFormat FairRoot::Base Boost::program_options) @@ -24,11 +29,15 @@ o2_add_library(SimConfig o2_target_root_dictionary(SimConfig HEADERS include/SimConfig/SimConfig.h include/SimConfig/SimParams.h + include/SimConfig/SimDLLoader.h include/SimConfig/SimUserDecay.h include/SimConfig/InteractionDiamondParam.h - include/SimConfig/DigiParams.h + include/SimConfig/DigiParams.h include/SimConfig/G4Params.h - include/SimConfig/MatMapParams.h) + include/SimConfig/DetectorLists.h + include/SimConfig/GlobalProcessCutSimParam.h + include/SimConfig/MatMapParams.h + include/SimConfig/FluenceWeightCalculator.h) o2_add_test(Config SOURCES test/TestConfig.cxx diff --git a/Common/SimConfig/include/SimConfig/DetectorLists.h b/Common/SimConfig/include/SimConfig/DetectorLists.h new file mode 100644 index 0000000000000..bdabe71db0872 --- /dev/null +++ b/Common/SimConfig/include/SimConfig/DetectorLists.h @@ -0,0 +1,37 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_DETECTORLISTS_H_ +#define O2_DETECTORLISTS_H_ + +#include +#include +#include + +#include "Framework/Logger.h" + +namespace o2::conf +{ +// Container defining different general evolutions of the ALICE experiment. Each +// evolution is given a name and a list defining the names of the detectors and +// passive elements present. +using DetectorList_t = std::vector; +using DetectorMap_t = std::unordered_map; + +// Parse the detector map from a JSON file. +// Return false if parsing failed. +bool parseDetectorMapfromJSON(const std::string& path, DetectorMap_t& map); + +// Print the DetetectorMap +void printDetMap(const DetectorMap_t& map, const std::string& list = ""); +} // namespace o2::conf + +#endif // O2_DETECTORLISTS_H_ diff --git a/Common/SimConfig/include/SimConfig/DigiParams.h b/Common/SimConfig/include/SimConfig/DigiParams.h index ecbd79f3de12f..7692ceff057ea 100644 --- a/Common/SimConfig/include/SimConfig/DigiParams.h +++ b/Common/SimConfig/include/SimConfig/DigiParams.h @@ -33,6 +33,7 @@ struct DigiParams : public o2::conf::ConfigurableParamHelper { int maxOrbitsToDigitize = -1; // Digitizer can use this to truncate digits that fall beyond an orbit limit (relative to start of digization) given by this param; -1 means no limit imposed // This parameter should typically be set to coincide with a single timeframe length or multiples thereof. std::string passName = "unanchored"; // passName for anchored MC + int seed = 0; // rndSeed to be applied in digitization; convention is that 0 is time based O2ParamDef(DigiParams, "DigiParams"); }; diff --git a/Common/SimConfig/include/SimConfig/FluenceWeightCalculator.h b/Common/SimConfig/include/SimConfig/FluenceWeightCalculator.h new file mode 100644 index 0000000000000..15d74ba27ab1b --- /dev/null +++ b/Common/SimConfig/include/SimConfig/FluenceWeightCalculator.h @@ -0,0 +1,35 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef FluenceWeightCalculator_h +#define FluenceWeightCalculator_h +#include +#include +#include +#include "TGraph.h" +// +// Static container class for damage weight funnctions in form of TGraphs +// The weights can be read from a csv file and stored in the graphs. +// +class FluenceWeightCalculator +{ + public: + FluenceWeightCalculator() = delete; + static void InitWeights(const std::string& filename); + static void InitWeightsFromCSV(const std::string& filename); + static double GetWeight(const int pdg, const double ekin); + + private: + static std::unique_ptr neutronG; + static std::unique_ptr protonG; + static std::unique_ptr pionG; +}; +#endif diff --git a/Common/SimConfig/include/SimConfig/G4Params.h b/Common/SimConfig/include/SimConfig/G4Params.h index 3c6506bddf801..2a333a39e4242 100644 --- a/Common/SimConfig/include/SimConfig/G4Params.h +++ b/Common/SimConfig/include/SimConfig/G4Params.h @@ -22,17 +22,38 @@ namespace conf // enumerating the possible G4 physics settings enum class EG4Physics { - kFTFP_BERT_optical = 0, /* just ordinary */ - kFTFP_BERT_optical_biasing = 1, /* with biasing enabled */ - kFTFP_INCLXX_optical = 2, /* special INCL++ version */ - kFTFP_BERT_HP_optical = 3 /* enable low energy neutron transport */ + kFTFP_BERT_optical = 0, /* just ordinary */ + kFTFP_BERT_optical_biasing = 1, /* with biasing enabled */ + kFTFP_INCLXX_optical = 2, /* special INCL++ version */ + kFTFP_BERT_HP_optical = 3, /* enable low energy neutron transport */ + kFTFP_BERT_EMV_optical = 4, /* just ordinary with faster electromagnetic physics */ + kFTFP_BERT_EMV_optical_biasing = 5, /* with biasing enabled with faster electromagnetic physics */ + kFTFP_INCLXX_EMV_optical = 6, /* special INCL++ version */ + kFTFP_BERT_EMV_HP_optical = 7, /* enable low energy neutron transport */ + kUSER = 8 /* allows to give own string combination */ +}; + +// enumerating possible geometry navigation modes +// (understanding that geometry description is always done with TGeo) +enum class EG4Nav { + kTGeo = 0, /* navigate with TGeo */ + kG4 = 1 /* navigate with G4 native geometry */ }; // parameters to influence the G4 engine struct G4Params : public o2::conf::ConfigurableParamHelper { - EG4Physics physicsmode = EG4Physics::kFTFP_BERT_optical; // physics mode with which to configure G4 + EG4Physics physicsmode = EG4Physics::kFTFP_BERT_EMV_optical; // default physics mode with which to configure G4 + + std::string configMacroFile = ""; // a user provided g4Config.in file (otherwise standard one fill be taken) + std::string userPhysicsList = ""; // possibility to directly give physics list as string + + EG4Nav navmode = EG4Nav::kTGeo; // geometry navigation mode (default TGeo) + + std::string fluenceWeightFile = ""; // file containing the scoring weights (pdg, ekin, weight) std::string const& getPhysicsConfigString() const; + bool g4scoring = false; + bool g4fluenceweight = false; O2ParamDef(G4Params, "G4"); }; diff --git a/Detectors/gconfig/include/SimSetup/GlobalProcessCutSimParam.h b/Common/SimConfig/include/SimConfig/GlobalProcessCutSimParam.h similarity index 97% rename from Detectors/gconfig/include/SimSetup/GlobalProcessCutSimParam.h rename to Common/SimConfig/include/SimConfig/GlobalProcessCutSimParam.h index c8bacfa7c4e7c..c9811c727cb32 100644 --- a/Detectors/gconfig/include/SimSetup/GlobalProcessCutSimParam.h +++ b/Common/SimConfig/include/SimConfig/GlobalProcessCutSimParam.h @@ -43,7 +43,7 @@ struct GlobalProcessCutSimParam : public o2::conf::ConfigurableParamHelper 1 MeV double DCUTM = 1.0E-3; // GeV --> 1 MeV double PPCUTM = 1.0E-3; // GeV --> 1 MeV - double TOFMAX = 1.E10; // seconds + double TOFMAX = 0.1; // seconds // boilerplate stuff + make principal key "GlobalSimProcs" O2ParamDef(GlobalProcessCutSimParam, "GlobalSimProcs"); diff --git a/Common/SimConfig/include/SimConfig/InteractionDiamondParam.h b/Common/SimConfig/include/SimConfig/InteractionDiamondParam.h index 22fc8caded127..59eabbfe73ffe 100644 --- a/Common/SimConfig/include/SimConfig/InteractionDiamondParam.h +++ b/Common/SimConfig/include/SimConfig/InteractionDiamondParam.h @@ -35,7 +35,7 @@ enum class EVertexDistribution { **/ struct InteractionDiamondParam : public o2::conf::ConfigurableParamHelper { double position[3] = {0., 0., 0.}; - double width[3] = {0., 0., 0.}; + double width[3] = {0.01, 0.01, 0.01}; double slopeX = 0.; // z-dependent x pos (see MeanVertexObject) double slopeY = 0.; // z-dependent y pos (see MeanVertexObject) EVertexDistribution distribution = EVertexDistribution::kGaus; diff --git a/Common/SimConfig/include/SimConfig/SimConfig.h b/Common/SimConfig/include/SimConfig/SimConfig.h index 1cfed6eacdae9..0be82ba1921b5 100644 --- a/Common/SimConfig/include/SimConfig/SimConfig.h +++ b/Common/SimConfig/include/SimConfig/SimConfig.h @@ -13,7 +13,15 @@ #define O2_SIM_CONFIGURATION #include +#ifndef __CLING__ #include +#else +namespace boost::program_options +{ +class variables_map; +class options_description; +} // namespace boost::program_options +#endif namespace o2 { @@ -29,7 +37,8 @@ enum class SimFieldMode { enum class VertexMode { kNoVertex = 0, // no vertexing should be applied in the generator kDiamondParam = 1, // Diamond param will influence vertexing - kCCDB = 2 // vertex should be taken from CCDB (Calib/MeanVertex object) + kCCDB = 2, // vertex should be taken from CCDB (Calib/MeanVertex object) + kCollCxt = 3 // vertex should be taken from collision context }; enum class TimeStampMode { @@ -40,46 +49,48 @@ enum class TimeStampMode { // configuration struct (which can be passed around) struct SimConfigData { - std::vector mActiveModules; // list of active modules - std::vector mReadoutDetectors; // list of readout detectors - std::string mMCEngine; // chosen VMC engine - std::string mGenerator; // chosen VMC generator - std::string mTrigger; // chosen VMC generator trigger - unsigned int mNEvents; // number of events to be simulated - std::string mExtKinFileName; // file name of external kinematics file (needed for ext kinematics generator) - std::string mEmbedIntoFileName; // filename containing the reference events to be used for the embedding - unsigned int mStartEvent; // index of first event to be taken - float mBMax; // maximum for impact parameter sampling - bool mIsMT; // chosen MT mode (Geant4 only) - std::string mOutputPrefix; // prefix to be used for output files - std::string mLogSeverity; // severity for FairLogger - std::string mLogVerbosity; // loglevel for FairLogger - std::string mKeyValueTokens; // a string holding arbitrary sequence of key-value tokens - // Foo.parameter1=x,Bar.parameter2=y,Baz.paramter3=hello - // (can be used to **loosely** change any configuration parameter from command-line) - std::string mConfigFile; // path to a JSON or INI config file (file extension is required to determine type). - // values within the config file will override values set in code by the param classes - // but will themselves be overridden by any values given in mKeyValueTokens. - int mPrimaryChunkSize; // defining max granularity for input primaries of a sim job - int mInternalChunkSize; // - ULong_t mStartSeed; // base for random number seeds - int mSimWorkers = 1; // number of parallel sim workers (when it applies) - bool mFilterNoHitEvents = false; // whether to filter out events not leaving any response - std::string mCCDBUrl; // the URL where to find CCDB - uint64_t mTimestamp; // timestamp in ms to anchor transport simulation to + std::vector mActiveModules; // list of active modules + std::vector mReadoutDetectors; // list of readout detectors + std::string mMCEngine; // chosen VMC engine + std::string mGenerator; // chosen VMC generator + std::string mTrigger; // chosen VMC generator trigger + unsigned int mNEvents; // number of events to be simulated + std::string mExtKinFileName; // file name of external kinematics file (needed for ext kinematics generator) + std::string mEmbedIntoFileName; // filename containing the reference events to be used for the embedding + unsigned int mStartEvent; // index of first event to be taken + float mBMax; // maximum for impact parameter sampling + bool mIsMT; // chosen MT mode (Geant4 only) + std::string mOutputPrefix; // prefix to be used for output files + std::string mLogSeverity; // severity for FairLogger + std::string mLogVerbosity; // loglevel for FairLogger + std::string mKeyValueTokens; // a string holding arbitrary sequence of key-value tokens + // Foo.parameter1=x,Bar.parameter2=y,Baz.paramter3=hello + // (can be used to **loosely** change any configuration parameter from command-line) + std::string mConfigFile; // path to a JSON or INI config file (file extension is required to determine type). + // values within the config file will override values set in code by the param classes + // but will themselves be overridden by any values given in mKeyValueTokens. + unsigned int mPrimaryChunkSize; // defining max granularity for input primaries of a sim job + int mInternalChunkSize; // + ULong_t mStartSeed; // base for random number seeds + int mSimWorkers = 1; // number of parallel sim workers (when it applies) + bool mFilterNoHitEvents = false; // whether to filter out events not leaving any response + std::string mCCDBUrl; // the URL where to find CCDB + uint64_t mTimestamp; // timestamp in ms to anchor transport simulation to TimeStampMode mTimestampMode = TimeStampMode::kNow; // telling of timestamp was given as option or defaulted to now - int mRunNumber = -1; // ALICE run number (if set != -1); the timestamp should be compatible - int mField; // L3 field setting in kGauss: +-2,+-5 and 0 - SimFieldMode mFieldMode = SimFieldMode::kDefault; // uniform magnetic field - bool mAsService = false; // if simulation should be run as service/deamon (does not exit after run) - bool mNoGeant = false; // if Geant transport should be turned off (when one is only interested in the generated events) - bool mIsRun5 = false; // true if the simulation is for Run 5 - std::string mFromCollisionContext = ""; // string denoting a collision context file; If given, this file will be used to determine number of events - bool mForwardKine = false; // true if tracks and event headers are to be published on a FairMQ channel (for reading by other consumers) - bool mWriteToDisc = true; // whether we write simulation products (kine, hits) to disc + int mRunNumber = -1; // ALICE run number (if set != -1); the timestamp should be compatible + int mField; // L3 field setting in kGauss: +-2,+-5 and 0 + SimFieldMode mFieldMode = SimFieldMode::kDefault; // uniform magnetic field + bool mAsService = false; // if simulation should be run as service/deamon (does not exit after run) + bool mNoGeant = false; // if Geant transport should be turned off (when one is only interested in the generated events) + bool mIsUpgrade = false; // true if the simulation is for Run 5 + std::string mFromCollisionContext = ""; // string denoting a collision context file; If given, this file will be used to determine number of events + // + bool mForwardKine = false; // true if tracks and event headers are to be published on a FairMQ channel (for reading by other consumers) + bool mWriteToDisc = true; // whether we write simulation products (kine, hits) to disc VertexMode mVertexMode = VertexMode::kDiamondParam; // by default we should use die InteractionDiamond parameter + std::string mExtGeomFile = ""; // optional path to a JSON file describing external (CAD) geometry modules to inject - ClassDefNV(SimConfigData, 4); + ClassDefNV(SimConfigData, 5); }; // A singleton class which can be used @@ -110,7 +121,7 @@ class SimConfig return SimConfig(); } - static void initOptions(boost::program_options::options_description&); + static void initOptions(boost::program_options::options_description&, bool isUpgrade = false); // initializes the configuration from command line arguments // returns true of correctly initialized and not --help called @@ -131,7 +142,8 @@ class SimConfig // static helper functions to determine list of active / readout modules // can also be used from outside - static void determineActiveModules(std::vector const& input, std::vector const& skipped, std::vector& active, bool isRun5 = false); + static void determineActiveModules(std::vector const& input, std::vector const& skipped, std::vector& active, bool isUpgrade = false); + static bool determineActiveModulesList(const std::string& version, std::vector const& input, std::vector const& skipped, std::vector& active); static void determineReadoutDetectors(std::vector const& active, std::vector const& enabledRO, std::vector const& skippedRO, std::vector& finalRO); // helper to parse field option @@ -163,16 +175,24 @@ class SimConfig uint64_t getTimestamp() const { return mConfigData.mTimestamp; } int getRunNumber() const { return mConfigData.mRunNumber; } bool isNoGeant() const { return mConfigData.mNoGeant; } - void setRun5(bool value = true) { mConfigData.mIsRun5 = value; } + void setRun5(bool value = true) { mConfigData.mIsUpgrade = value; } bool forwardKine() const { return mConfigData.mForwardKine; } bool writeToDisc() const { return mConfigData.mWriteToDisc; } VertexMode getVertexMode() const { return mConfigData.mVertexMode; } + std::string getExtGeomFilename() const { return mConfigData.mExtGeomFile; } + + // returns the pair of collision context filename as well as event prefix encoded + // in the mFromCollisionContext string. Returns empty string if information is not available or set. + std::pair getCollContextFilenameAndEventPrefix() const; private: SimConfigData mConfigData; //! + // Filter out skipped elements in the list + static bool filterSkippedElements(std::vector& elements, std::vector const& skipped); + // adjust/overwrite some option settings when collision context is used - void adjustFromCollContext(); + void adjustFromCollContext(std::string const& collcontextfile, std::string const& prefix); ClassDefNV(SimConfig, 1); }; @@ -198,9 +218,9 @@ struct SimReconfigData { std::string configFile; // path to a JSON or INI config file (file extension is required to determine type). // values within the config file will override values set in code by the param classes // but will themselves be overridden by any values given in mKeyValueTokens. - unsigned int primaryChunkSize; // defining max granularity for input primaries of a sim job - ULong_t startSeed; // base for random number seeds - bool stop; // to shut down the service + unsigned int primaryChunkSize; // defining max granularity for input primaries of a sim job + ULong_t startSeed; // base for random number seeds + bool stop; // to shut down the service std::string mFromCollisionContext = ""; // string denoting a collision context file; If given, this file will be used to determine number of events ClassDefNV(SimReconfigData, 1); diff --git a/Common/SimConfig/include/SimConfig/SimDLLoader.h b/Common/SimConfig/include/SimConfig/SimDLLoader.h new file mode 100644 index 0000000000000..e778023b28276 --- /dev/null +++ b/Common/SimConfig/include/SimConfig/SimDLLoader.h @@ -0,0 +1,27 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef SIMDLLOADER_H_ +#define SIMDLLOADER_H_ + +#include "CommonUtils/DLLoaderBase.h" + +namespace o2::conf +{ + +class SimDLLoader : public o2::utils::DLLoaderBase +{ + O2DLLoaderDef(SimDLLoader) +}; + +} // namespace o2::conf + +#endif // SIMDLLOADER_H_ diff --git a/Common/SimConfig/include/SimConfig/SimParams.h b/Common/SimConfig/include/SimConfig/SimParams.h index 2c103f43b2b04..b5f975d1b0c6e 100644 --- a/Common/SimConfig/include/SimConfig/SimParams.h +++ b/Common/SimConfig/include/SimConfig/SimParams.h @@ -36,7 +36,6 @@ struct SimCutParams : public o2::conf::ConfigurableParamHelper { float maxRTrackingZDC = 50; // R-cut applied in the tunnel leading to ZDC when z > beampipeZ (custom stepping function) float tunnelZ = 1900; // Z-value from where we apply maxRTrackingZDC (default value taken from standard "hall" dimensions) - float globalDensityFactor = 1.f; // global factor that scales all material densities for systematic studies bool lowneut = false; O2ParamDef(SimCutParams, "SimCutParams"); }; @@ -44,8 +43,12 @@ struct SimCutParams : public o2::conf::ConfigurableParamHelper { // parameter influencing material manager struct SimMaterialParams : public o2::conf::ConfigurableParamHelper { // Local density value takes precedence over global density value, i.e. local values overwrite the global value. - float globalDensityFactor = 1.f; - std::string localDensityFactor; // Expected format: "SimMaterialParams.localDensityFactor=:,:,..." + float globalDensityFactor = 1.f; // global factor that scales all material densities for systematic studies + // String to set densities on module or material level. Expected format: + // "SimMaterialParams.localDensityFactor=:,:,..." + // Example: "SimMaterialParams.localDensityFactor=TPC/Air:1.2,ITS:5." will scale the density of the Air in TPC + // with 1.2 and to 5.0 for all materials in ITS". + std::string localDensityFactor; O2ParamDef(SimMaterialParams, "SimMaterialParams"); }; diff --git a/Common/SimConfig/src/DetectorLists.cxx b/Common/SimConfig/src/DetectorLists.cxx new file mode 100644 index 0000000000000..c9132a3cb84a5 --- /dev/null +++ b/Common/SimConfig/src/DetectorLists.cxx @@ -0,0 +1,78 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "SimConfig/DetectorLists.h" +#include +#include +#include +#include +#include + +namespace o2::conf +{ + +bool parseDetectorMapfromJSON(const std::string& path, DetectorMap_t& map) +{ + // Parse JSON file to build map + std::ifstream fileStream(path, std::ios::in); + if (!fileStream.is_open()) { + LOGP(error, "Cannot open '{}'!", path); + return false; + } + rapidjson::IStreamWrapper isw(fileStream); + rapidjson::Document doc; + doc.ParseStream(isw); + if (doc.HasParseError()) { + LOGP(error, "Error parsing provided json file '{}':", path); + LOGP(error, " - Error -> {}", rapidjson::GetParseError_En(doc.GetParseError())); + LOGP(error, " - Offset -> {}", doc.GetErrorOffset()); + return false; + } + + // Clear and rebuild map + map.clear(); + try { + for (auto verItr = doc.MemberBegin(); verItr != doc.MemberEnd(); ++verItr) { + const auto& version = verItr->name.GetString(); + DetectorList_t list; + const auto& elements = doc[version]; + for (const auto& ele : elements.GetArray()) { + list.emplace_back(ele.GetString()); + } + map.emplace(version, list); + } + } catch (const std::exception& e) { + LOGP(error, "Failed to build detector map from file '{}' with '{}'", path, e.what()); + return false; + } + + return true; +} + +void printDetMap(const DetectorMap_t& map, const std::string& list) +{ + if (list.empty()) { + LOGP(error, "List of all available versions including their detectors:"); + for (int i{0}; const auto& [version, elements] : map) { + LOGP(error, " - {: >2d}. {}:", i++, version); + for (int j{0}; const auto& element : elements) { + LOGP(error, "\t\t* {: >2d}.\t{}", j++, element); + } + } + } else { + LOGP(error, "List of available modules for version {}:", list); + for (int j{0}; const auto& element : map.at(list)) { + LOGP(error, "\t* {: >2d}.\t{}", j++, element); + } + } +} + +} // namespace o2::conf diff --git a/Common/SimConfig/src/FluenceWeightCalculator.cxx b/Common/SimConfig/src/FluenceWeightCalculator.cxx new file mode 100644 index 0000000000000..63828f71286e2 --- /dev/null +++ b/Common/SimConfig/src/FluenceWeightCalculator.cxx @@ -0,0 +1,138 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "SimConfig/FluenceWeightCalculator.h" +#include +#include +#include +#include + +std::unique_ptr FluenceWeightCalculator::neutronG; +std::unique_ptr FluenceWeightCalculator::protonG; +std::unique_ptr FluenceWeightCalculator::pionG; + +double FluenceWeightCalculator::GetWeight(const int pdg, const double kineticEnergy) +{ + // + // Obtain weight for given particle and kinetic energy + if (!neutronG || !protonG || !pionG) { + std::cerr << "FluenceWeightCalculator not initialized\n"; + return 0.; + } + switch (std::abs(pdg)) { + case 2112: { + return neutronG->Eval(kineticEnergy, nullptr, "S"); + } + case 2212: { + return ((kineticEnergy > 1e-3) ? protonG->Eval(kineticEnergy, nullptr, "S") : 0.); + } + case 211: { + return ((kineticEnergy > 10.) ? pionG->Eval(kineticEnergy, nullptr, "S") : 0.); + } + default: + return 0.0; + } +} + +void FluenceWeightCalculator::InitWeights(const std::string& filename) +{ + // + // Read graphs from file + TFile inFile(filename.c_str(), "READ"); + if (inFile.IsZombie()) { + std::cerr << "Cannot open " << filename << "\n"; + return; + } + // + TGraph* tmp = nullptr; + inFile.GetObject("neutronDW", tmp); + neutronG.reset(tmp ? static_cast(tmp->Clone()) : nullptr); + if (!neutronG) { + std::cerr << "Missing graph neutronDW\n"; + return; + } + neutronG->SetBit(TGraph::kIsSortedX); + inFile.GetObject("protonDW", tmp); + protonG.reset(tmp ? static_cast(tmp->Clone()) : nullptr); + if (!protonG) { + std::cerr << "Missing graph protonDW\n"; + return; + } + protonG->SetBit(TGraph::kIsSortedX); + inFile.GetObject("pionDW", tmp); + pionG.reset(tmp ? static_cast(tmp->Clone()) : nullptr); + if (!pionG) { + std::cerr << "Missing graph pionDW\n"; + return; + } + pionG->SetBit(TGraph::kIsSortedX); +} + +void FluenceWeightCalculator::InitWeightsFromCSV(const std::string& filename) +{ + // + // read the NIEL weights from input file and store them as TGraphs + neutronG = std::make_unique(); + neutronG->SetName("neutronDW"); + auto neuN = 0; + protonG = std::make_unique(); + protonG->SetName("protonDW"); + auto proN = 0; + pionG = std::make_unique(); + pionG->SetName("pionDW"); + auto pioN = 0; + + std::ifstream in(filename); + if (!in.is_open()) { + std::cerr << "Error: cannot open file with damage weights.\n"; + return; + } + std::string line; + while (std::getline(in, line)) { + if (line.empty() || line[0] == '#') { + continue; + } + std::istringstream ss(line); + std::string particle, e_str, w_str; + if (!std::getline(ss, particle, ',')) { + continue; + } + if (!std::getline(ss, e_str, ',')) { + continue; + } + if (!std::getline(ss, w_str, ',')) { + continue; + } + auto e = std::stod(e_str); + auto w = std::stod(w_str); + auto pdg = std::stoi(particle); + switch (pdg) { + case 2112: { + neutronG->SetPoint(neuN++, e, w); + break; + } + case 2212: { + protonG->SetPoint(proN++, e, w); + break; + } + case 211: { + pionG->SetPoint(pioN++, e, w); + break; + } + default:; + } + } + auto fout = new TFile("rd50_niel.root", "recreate"); + neutronG->Write(); + protonG->Write(); + pionG->Write(); + fout->Close(); +} diff --git a/Common/SimConfig/src/G4Params.cxx b/Common/SimConfig/src/G4Params.cxx index 48f7a1c240d7b..37625d914a85b 100644 --- a/Common/SimConfig/src/G4Params.cxx +++ b/Common/SimConfig/src/G4Params.cxx @@ -19,12 +19,21 @@ namespace conf namespace { -static const std::string confstrings[4] = {"FTFP_BERT_EMV+optical", "FTFP_BERT_EMV+optical+biasing", "FTFP_INCLXX_EMV+optical", +static const std::string confstrings[8] = {"FTFP_BERT+optical", + "FTFP_BERT+optical+biasing", + "FTFP_INCLXX+optical", + "FTFP_BERT_HP+optical", + "FTFP_BERT_EMV+optical", + "FTFP_BERT_EMV+optical+biasing", + "FTFP_INCLXX_EMV+optical", "FTFP_BERT_HP_EMV+optical"}; } std::string const& G4Params::getPhysicsConfigString() const { + if (physicsmode == o2::conf::EG4Physics::kUSER) { + return userPhysicsList; + } return confstrings[(int)physicsmode]; } diff --git a/Detectors/gconfig/src/GlobalProcessCutSimParam.cxx b/Common/SimConfig/src/GlobalProcessCutSimParam.cxx similarity index 92% rename from Detectors/gconfig/src/GlobalProcessCutSimParam.cxx rename to Common/SimConfig/src/GlobalProcessCutSimParam.cxx index 4f30020133996..5ae72164bb81d 100644 --- a/Detectors/gconfig/src/GlobalProcessCutSimParam.cxx +++ b/Common/SimConfig/src/GlobalProcessCutSimParam.cxx @@ -9,5 +9,5 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "SimSetup/GlobalProcessCutSimParam.h" +#include "SimConfig/GlobalProcessCutSimParam.h" O2ParamImpl(o2::GlobalProcessCutSimParam); diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index a35019648abb9..14f99dd4ea580 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include +#include #include #include #include @@ -23,8 +24,15 @@ using namespace o2::conf; namespace bpo = boost::program_options; -void SimConfig::initOptions(boost::program_options::options_description& options) +void SimConfig::initOptions(boost::program_options::options_description& options, bool isUpgrade) { + // some default args might depend on whether Run3 or Run5 + // can be updated here: + std::string defaultGeomList{"ALICE2"}; + if (isUpgrade == true) { + defaultGeomList = "ALICE3"; + } + int nsimworkersdefault = std::max(1u, std::thread::hardware_concurrency() / 2); options.add_options()( "mcEngine,e", bpo::value()->default_value("TGeant4"), "VMC backend to be used.")( @@ -34,6 +42,14 @@ void SimConfig::initOptions(boost::program_options::options_description& options "skipModules", bpo::value>()->multitoken()->default_value(std::vector({""}), ""), "list of modules excluded in geometry (precendence over -m")( "readoutDetectors", bpo::value>()->multitoken()->default_value(std::vector(), ""), "list of detectors creating hits, all if not given; added to to active modules")( "skipReadoutDetectors", bpo::value>()->multitoken()->default_value(std::vector(), ""), "list of detectors to skip hit creation (precendence over --readoutDetectors")( + "detectorList", bpo::value()->default_value(defaultGeomList), + "Use a specific version of ALICE, e.g., a predefined list." + "There is an 'official' list provided with:" + "\nALICE2 : The default configuration for Run 3" + "\nALICE2.1: The future configuration for Run 4" + "\nALICE3 : The far-future configuration for Run 5-6" + "\nAdditionally one can provide their own custom list of modules which should be included in the geometry." + "\nBy specifiying LIST:JSONFILE where LIST is a list present in JSONFILE.")( "nEvents,n", bpo::value()->default_value(0), "number of events")( "startEvent", bpo::value()->default_value(0), "index of first event to be used (when applicable)")( "extKinFile", bpo::value()->default_value("Kinematics.root"), @@ -59,11 +75,12 @@ void SimConfig::initOptions(boost::program_options::options_description& options "asservice", bpo::value()->default_value(false), "run in service/server mode")( "noGeant", bpo::bool_switch(), "prohibits any Geant transport/physics (by using tight cuts)")( "forwardKine", bpo::bool_switch(), "forward kinematics on a FairMQ channel")( - "noDiscOutput", bpo::bool_switch(), "switch off writing sim results to disc (useful in combination with forwardKine)"); - options.add_options()("fromCollContext", bpo::value()->default_value(""), "Use a pregenerated collision context to infer number of events to simulate, how to embedd them, the vertex position etc. Takes precedence of other options such as \"--nEvents\"."); + "noDiscOutput", bpo::bool_switch(), "switch off writing sim results to disc (useful in combination with forwardKine)")( + "extGeomFile", bpo::value()->default_value(""), "Path to a JSON file describing external (CAD) geometry modules to inject (see Detectors/Passive ExternalModule). Modules are added when their 'name' is part of the active module list."); + options.add_options()("fromCollContext", bpo::value()->default_value(""), "Use a pregenerated collision context to infer number of events to simulate, how to embedd them, the vertex position etc. Takes precedence of other options such as \"--nEvents\". The format is COLLISIONCONTEXTFILE.root[:SIGNALNAME] where SIGNALNAME is the event part in the context which is relevant."); } -void SimConfig::determineActiveModules(std::vector const& inputargs, std::vector const& skippedModules, std::vector& activeModules, bool mIsRun5) +void SimConfig::determineActiveModules(std::vector const& inputargs, std::vector const& skippedModules, std::vector& activeModules, bool isUpgrade) { using o2::detectors::DetID; @@ -72,16 +89,33 @@ void SimConfig::determineActiveModules(std::vector const& inputargs activeModules = inputargs; #ifdef ENABLE_UPGRADES if (activeModules[0] != "all") { - if (mIsRun5) { + if (isUpgrade) { for (int i = 0; i < activeModules.size(); ++i) { - if (activeModules[i] != "IT3" && activeModules[i] != "TRK" && activeModules[i] != "FT3" && activeModules[i] != "FCT" && activeModules[i] != "A3IP") { - LOGP(fatal, "List of active modules contains {}, which is not a run 5 module", activeModules[i]); + if (activeModules[i] != "A3IP" && + activeModules[i] != "IT3" && + activeModules[i] != "TRK" && + activeModules[i] != "FT3" && + activeModules[i] != "FCT" && + activeModules[i] != "TF3" && + activeModules[i] != "RCH" && + activeModules[i] != "MI3" && + activeModules[i] != "ECL" && + activeModules[i] != "FD3") { + LOGP(fatal, "List of active modules contains {}, which is not a module from the upgrades.", activeModules[i]); } } } - if (!mIsRun5) { + if (!isUpgrade) { for (int i = 0; i < activeModules.size(); ++i) { - if (activeModules[i] == "TRK" || activeModules[i] == "FT3" || activeModules[i] == "FCT" || activeModules[i] == "A3IP") { + if (activeModules[i] == "A3IP" || + activeModules[i] == "TRK" || + activeModules[i] == "FT3" || + activeModules[i] == "FCT" || + activeModules[i] == "TF3" || + activeModules[i] == "RCH" || + activeModules[i] == "MI3" || + activeModules[i] == "ECL" || + activeModules[i] == "FD3") { LOGP(fatal, "List of active modules contains {}, which is not a run 3 module", activeModules[i]); } } @@ -91,13 +125,22 @@ void SimConfig::determineActiveModules(std::vector const& inputargs if (activeModules.size() == 1 && activeModules[0] == "all") { activeModules.clear(); #ifdef ENABLE_UPGRADES - if (mIsRun5) { + if (isUpgrade) { for (int d = DetID::First; d <= DetID::Last; ++d) { - if (d == DetID::IT3 || d == DetID::TRK || d == DetID::FT3 || d == DetID::FCT) { + if (d == DetID::TRK || + d == DetID::FT3 || + d == DetID::FCT || + d == DetID::TF3 || + d == DetID::RCH || + d == DetID::ECL || + d == DetID::FD3 || + d == DetID::MI3) { activeModules.emplace_back(DetID::getName(d)); } } activeModules.emplace_back("A3IP"); + activeModules.emplace_back("A3ABSO"); + activeModules.emplace_back("A3MAG"); } else { #endif // add passive components manually (make a PassiveDetID for them!) @@ -110,7 +153,7 @@ void SimConfig::determineActiveModules(std::vector const& inputargs activeModules.emplace_back("SHIL"); for (int d = DetID::First; d <= DetID::Last; ++d) { #ifdef ENABLE_UPGRADES - if (d != DetID::IT3 && d != DetID::TRK && d != DetID::FT3 && d != DetID::FCT) { + if (d != DetID::IT3 && d != DetID::TRK && d != DetID::FT3 && d != DetID::FCT && d != DetID::TF3 && d != DetID::RCH && d != DetID::ECL && d != DetID::FD3 && d != DetID::MI3) { activeModules.emplace_back(DetID::getName(d)); } } @@ -119,14 +162,72 @@ void SimConfig::determineActiveModules(std::vector const& inputargs #endif } } - // now we take out detectors listed as skipped - for (auto& s : skippedModules) { - auto iter = std::find(activeModules.begin(), activeModules.end(), s); - if (iter != activeModules.end()) { - // take it out - activeModules.erase(iter); + filterSkippedElements(activeModules, skippedModules); +} + +bool SimConfig::determineActiveModulesList(const std::string& version, std::vector const& inputargs, std::vector const& skippedModules, std::vector& activeModules) +{ + DetectorList_t modules; + DetectorMap_t map; + if (auto pos = version.find(':'); pos != std::string::npos) { + auto pversion = version.substr(0, pos); + auto ppath = version.substr(pos + 1); + if (!parseDetectorMapfromJSON(ppath, map)) { + LOGP(error, "Could not parse {}; check errors above!", ppath); + return false; + } + if (map.find(pversion) == map.end()) { + LOGP(error, "List {} is not defined in custom JSON file!", pversion); + printDetMap(map); + return false; + } + modules = map[pversion]; + LOGP(info, "Running with version {} from custom detector list '{}'", pversion, ppath); + } else { + // Otherwise check 'official' versions which provided in config + auto o2env = std::getenv("O2_ROOT"); + if (!o2env) { + LOGP(error, "O2_ROOT environment not defined"); + return false; + } + const std::string rootpath(fmt::format("{}/share/config/o2simdefaultdetectorlist.json", o2env)); + if (!parseDetectorMapfromJSON(rootpath, map)) { + LOGP(error, "Could not parse {} -> check errors above!", rootpath); + return false; + } + if (map.find(version) == map.end()) { + LOGP(error, "List {} is not defined in 'official' JSON file!", version); + printDetMap(map); + return false; + } + modules = map[version]; + static std::string last_version{}; // prevent multiple printouts of same message + if (last_version != version) { + LOGP(info, "Running with official detector version '{}'", version); + last_version = version; + } + } + // check if specified modules are in list + if (inputargs.size() != 1 || inputargs[0] != "all") { + std::vector diff; + for (const auto& in : inputargs) { + if (std::find(modules.begin(), modules.end(), in) == std::end(modules)) { + diff.emplace_back(in); + } + } + if (!diff.empty()) { + LOGP(error, "Modules specified that are not present in detector list {}", version); + for (int j{0}; const auto& m : diff) { + LOGP(info, " - {: <2}. {}", j++, m); + } + printDetMap(map, version); + return false; } } + // Insert into active modules if module is built buy -m or insert all if default for -m is used ("all") + std::copy_if(modules.begin(), modules.end(), std::back_inserter(activeModules), + [&inputargs](const auto& e) { return (inputargs.size() == 1 && inputargs[0] == "all") || (std::find(inputargs.begin(), inputargs.end(), e) != inputargs.end()); }); + return filterSkippedElements(activeModules, skippedModules); } void SimConfig::determineReadoutDetectors(std::vector const& activeModules, std::vector const& enableReadout, std::vector const& disableReadout, std::vector& readoutDetectors) @@ -177,14 +278,36 @@ void SimConfig::determineReadoutDetectors(std::vector const& active } } +std::pair SimConfig::getCollContextFilenameAndEventPrefix() const +{ + // we decompose the argument to fetch + // (a) collision contextfilename + // (b) sim prefix to use from the context + auto pos = mConfigData.mFromCollisionContext.find(':'); + std::string collcontextfile{mConfigData.mFromCollisionContext}; + std::string simprefix{mConfigData.mOutputPrefix}; + if (pos != std::string::npos) { + collcontextfile = mConfigData.mFromCollisionContext.substr(0, pos); + simprefix = mConfigData.mFromCollisionContext.substr(pos + 1); + } + return std::make_pair(collcontextfile, simprefix); +} + bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& vm) { using o2::detectors::DetID; mConfigData.mMCEngine = vm["mcEngine"].as(); mConfigData.mNoGeant = vm["noGeant"].as(); - // get final set of active Modules - determineActiveModules(vm["modules"].as>(), vm["skipModules"].as>(), mConfigData.mActiveModules, mConfigData.mIsRun5); + // Reset modules and detectors as they are anyway re-parsed + mConfigData.mReadoutDetectors.clear(); + mConfigData.mActiveModules.clear(); + + // Get final set of active Modules + if (!determineActiveModulesList(vm["detectorList"].as(), vm["modules"].as>(), vm["skipModules"].as>(), mConfigData.mActiveModules)) { + return false; + } + if (mConfigData.mNoGeant) { // CAVE is all that's needed (and that will be built either way), so clear all modules and set the O2TrivialMCEngine mConfigData.mActiveModules.clear(); @@ -232,8 +355,10 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& if (vm.count("noemptyevents")) { mConfigData.mFilterNoHitEvents = true; } + mConfigData.mExtGeomFile = vm["extGeomFile"].as(); mConfigData.mFromCollisionContext = vm["fromCollContext"].as(); - adjustFromCollContext(); + auto collcontext_simprefix = getCollContextFilenameAndEventPrefix(); + adjustFromCollContext(collcontext_simprefix.first, collcontext_simprefix.second); // analyse vertex options if (!parseVertexModeString(vm["vertexMode"].as(), mConfigData.mVertexMode)) { @@ -243,9 +368,9 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& // analyse field options // either: "ccdb" or +-2[U],+-5[U] and 0[U]; +-U auto& fieldstring = vm["field"].as(); - std::regex re("(ccdb)|([+-]?[250]U?)"); + std::regex re("(ccdb)|([+-]?(0|[2-9]|[12][0-9]|20)U?)"); if (!std::regex_match(fieldstring, re)) { - LOG(error) << "Invalid field option"; + LOG(error) << "Invalid field option " << fieldstring; return false; } if (fieldstring == "ccdb") { @@ -275,8 +400,11 @@ bool SimConfig::parseVertexModeString(std::string const& vertexstring, VertexMod } else if (vertexstring == "kCCDB") { mode = VertexMode::kCCDB; return true; + } else if (vertexstring == "kCollContext") { + mode = VertexMode::kCollCxt; + return true; } - LOG(error) << "Vertex mode must be one of kNoVertex, kDiamondParam, kCCDB"; + LOG(error) << "Vertex mode must be one of kNoVertex, kDiamondParam, kCCDB, kCollContext"; return false; } @@ -284,9 +412,9 @@ bool SimConfig::parseFieldString(std::string const& fieldstring, int& fieldvalue { // analyse field options // either: "ccdb" or +-2[U],+-5[U] and 0[U]; +-U - std::regex re("(ccdb)|([+-]?[250]U?)"); + std::regex re("(ccdb)|([+-]?(0|[2-9]|[12][0-9]|20)U?)"); if (!std::regex_match(fieldstring, re)) { - LOG(error) << "Invalid field option"; + LOG(error) << "Invalid field option " << fieldstring; return false; } if (fieldstring == "ccdb") { @@ -300,28 +428,60 @@ bool SimConfig::parseFieldString(std::string const& fieldstring, int& fieldvalue return true; } -void SimConfig::adjustFromCollContext() +bool SimConfig::filterSkippedElements(std::vector& elements, std::vector const& skipped) +{ + for (auto& s : skipped) { + if (s.empty()) { // nothing to skip here + continue; + } + auto iter = std::find(elements.begin(), elements.end(), s); + if (iter != elements.end()) { + // take it out + elements.erase(iter); + } else { + LOGP(error, "Skipped modules specified that are not present in built modules!"); + LOGP(error, "Built modules:"); + for (int j{0}; const auto& m : elements) { + LOGP(error, " + {: <2}. {}", j++, m); + } + std::vector diff; + for (const auto& skip : skipped) { + if (std::find(elements.begin(), elements.end(), skip) == std::end(elements)) { + diff.emplace_back(skip); + } + } + LOGP(error, "Specified skipped modules not present in built modules:"); + for (int j{0}; const auto& m : diff) { + LOGP(error, " - {: <2}. {}", j++, m); + } + return false; + } + } + return true; +} + +void SimConfig::adjustFromCollContext(std::string const& collcontextfile, std::string const& prefix) { // When we use pregenerated collision contexts, some options // need to be auto-adjusted. Do so and inform about this in the logs. - if (mConfigData.mFromCollisionContext == "") { + if (collcontextfile == "") { return; } - auto context = o2::steer::DigitizationContext::loadFromFile(mConfigData.mFromCollisionContext); + auto context = o2::steer::DigitizationContext::loadFromFile(collcontextfile); if (context) { // find the events belonging to a source that corresponds to a sim prefix - LOG(info) << "Looking up simprefixes " << mConfigData.mOutputPrefix; - int sourceid = context->findSimPrefix(mConfigData.mOutputPrefix); + LOG(info) << "Looking up simprefixes " << prefix; + int sourceid = context->findSimPrefix(prefix); if (sourceid == -1) { - LOG(error) << "Could not find collisions with sim prefix " << mConfigData.mOutputPrefix << " in the collision context. The collision contet specifies the following prefixes:"; - for (auto& prefix : context->getSimPrefixes()) { - LOG(info) << prefix; + LOG(error) << "Could not find collisions with sim prefix " << prefix << " in the collision context. The collision context specifies the following prefixes:"; + for (auto& sp : context->getSimPrefixes()) { + LOG(info) << sp; } LOG(fatal) << "Aborting due to prefix error"; } else { auto collisionmap = context->getCollisionIndicesForSource(sourceid); - LOG(info) << "Found " << collisionmap.size() << " events in the collisioncontext for prefix " << mConfigData.mOutputPrefix; + LOG(info) << "Found " << collisionmap.size() << " events in the collisioncontext for prefix " << prefix; // check if collisionmap is dense (otherwise it will get screwed up with order/indexing in ROOT output) bool good = true; @@ -339,25 +499,23 @@ void SimConfig::adjustFromCollContext() // we take what is specified in the context mConfigData.mNEvents = collisionmap.size(); } else { - LOG(warning) << "The number of events on the command line " << mConfigData.mNEvents << " and in the collision context differ. Taking the min of the 2"; - mConfigData.mNEvents = std::min((size_t)mConfigData.mNEvents, collisionmap.size()); + LOG(warning) << "The number of events on the command line " << mConfigData.mNEvents << " and in the collision context differ. We take the one from collision context " << collisionmap.size(); + mConfigData.mNEvents = collisionmap.size(); } LOG(info) << "Setting number of events to simulate to " << mConfigData.mNEvents; } } else { - LOG(fatal) << "Could not open collision context file " << mConfigData.mFromCollisionContext; + LOG(fatal) << "Could not open collision context file " << collcontextfile; } } bool SimConfig::resetFromArguments(int argc, char* argv[]) { - namespace bpo = boost::program_options; - // Arguments parsing bpo::variables_map vm; bpo::options_description desc("Allowed options"); desc.add_options()("help,h", "Produce help message."); - initOptions(desc); + initOptions(desc, mConfigData.mIsUpgrade); try { bpo::store(parse_command_line(argc, argv, desc), vm); diff --git a/Common/SimConfig/src/SimConfigLinkDef.h b/Common/SimConfig/src/SimConfigLinkDef.h index 844ea8db3ed8c..a1315e24ffedd 100644 --- a/Common/SimConfig/src/SimConfigLinkDef.h +++ b/Common/SimConfig/src/SimConfigLinkDef.h @@ -29,6 +29,7 @@ #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::DigiParams> + ; #pragma link C++ enum o2::conf::EG4Physics; +#pragma link C++ enum o2::conf::EG4Nav; #pragma link C++ enum o2::conf::SimFieldMode; #pragma link C++ struct o2::conf::G4Params + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::G4Params> + ; @@ -39,4 +40,7 @@ #pragma link C++ class o2::eventgen::InteractionDiamondParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::InteractionDiamondParam> + ; +#pragma link C++ class o2::GlobalProcessCutSimParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::GlobalProcessCutSimParam> + ; + #endif diff --git a/Common/SimConfig/src/SimDLLoader.cxx b/Common/SimConfig/src/SimDLLoader.cxx new file mode 100644 index 0000000000000..ed15a8b0cef6a --- /dev/null +++ b/Common/SimConfig/src/SimDLLoader.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "SimConfig/SimDLLoader.h" +O2DLLoaderImpl(o2::conf::SimDLLoader) diff --git a/Common/SimConfig/test/testSimCutParam.cxx b/Common/SimConfig/test/testSimCutParam.cxx index 7a17ae9d1f2cf..468d76429cbba 100644 --- a/Common/SimConfig/test/testSimCutParam.cxx +++ b/Common/SimConfig/test/testSimCutParam.cxx @@ -15,6 +15,7 @@ #include #include "SimConfig/SimParams.h" #include "CommonUtils/ConfigurableParam.h" +#include using namespace o2::conf; diff --git a/Common/Topologies/o2prototype_topology.xml b/Common/Topologies/o2prototype_topology.xml index 240b8d87d469a..8d53c9eb0127a 100644 --- a/Common/Topologies/o2prototype_topology.xml +++ b/Common/Topologies/o2prototype_topology.xml @@ -74,7 +74,7 @@ The following parameters need adjustment when extending the FLP-EPN configuratio - $ALICEO2_INSTALL_DIR/bin/aliceHLTWrapper Tracker_%collectionIndex%_%taskIndex% 1 --dds --poll-period 100 --input type=pull,size=5000,method=connect,property=EPNReceiverOutputAddress,count=1 --output type=push,size=500,method=bind,property=TrackingOutputAddress,min-port=48000 --library libAliHLTTPC.so --component TPCCATracker --run 167808 --parameter '-GlobalTracking -allowGPU -GPUHelperThreads 4 -loglevel=0x7c' + $ALICEO2_INSTALL_DIR/bin/aliceHLTWrapper Tracker_%collectionIndex%_%taskIndex% 1 --dds --poll-period 100 --input type=pull,size=5000,method=connect,property=EPNReceiverOutputAddress,count=1 --output type=push,size=500,method=bind,property=TrackingOutputAddress,min-port=48000 --library libAliHLTTPC.so --component TPCCATracker --run 167808 --parameter '-GlobalTracking -allowGPU -loglevel=0x7c' EPNReceiverOutputAddress diff --git a/Common/Utils/CMakeLists.txt b/Common/Utils/CMakeLists.txt index 4e43116b279a1..849a3d70f62e1 100644 --- a/Common/Utils/CMakeLists.txt +++ b/Common/Utils/CMakeLists.txt @@ -14,7 +14,7 @@ o2_add_library(CommonUtils src/RootChain.cxx src/CompStream.cxx src/ShmManager.cxx src/ValueMonitor.cxx src/StringUtils.cxx - src/ConfigurableParamHelper.cxx src/ConfigurableParam.cxx src/RootSerializableKeyValueStore.cxx + src/ConfigurableParamReaders.cxx src/ConfigurableParamHelper.cxx src/ConfigurableParam.cxx src/RootSerializableKeyValueStore.cxx src/KeyValParam.cxx src/FileSystemUtils.cxx src/FIFO.cxx @@ -24,15 +24,15 @@ o2_add_library(CommonUtils src/NameConf.cxx src/IRFrameSelector.cxx src/DebugStreamer.cxx + src/DLLoaderBase.cxx PUBLIC_LINK_LIBRARIES ROOT::Hist ROOT::Tree Boost::iostreams O2::CommonDataFormat O2::Headers - FairLogger::FairLogger O2::MathUtils TBB::tbb) + FairLogger::FairLogger O2::MathUtils TBB::tbb O2::GPUCommon) o2_target_root_dictionary(CommonUtils HEADERS include/CommonUtils/TreeStream.h include/CommonUtils/TreeStreamRedirector.h include/CommonUtils/RootChain.h include/CommonUtils/BoostHistogramUtils.h - include/CommonUtils/BoostSerializer.h include/CommonUtils/ShmManager.h include/CommonUtils/RngHelper.h include/CommonUtils/StringUtils.h @@ -40,27 +40,32 @@ o2_target_root_dictionary(CommonUtils include/CommonUtils/MemFileHelper.h include/CommonUtils/ConfigurableParam.h include/CommonUtils/ConfigurableParamHelper.h + include/CommonUtils/ConfigurableParamReaders.h include/CommonUtils/ConfigurationMacroHelper.h include/CommonUtils/RootSerializableKeyValueStore.h include/CommonUtils/KeyValParam.h include/CommonUtils/VerbosityConfig.h include/CommonUtils/FileFetcher.h + include/CommonUtils/DLLoaderBase.h include/CommonUtils/NameConf.h include/CommonUtils/IRFrameSelector.h include/CommonUtils/DebugStreamer.h) +# Extra dictionaries only needed if tests are built +if(BUILD_TESTING) + o2_add_library(CommonUtilsTest + SOURCES src/ConfigurableParamTest.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtils) + o2_target_root_dictionary(CommonUtilsTest + HEADERS include/CommonUtils/ConfigurableParamTest.h) +endif() + o2_add_test(TreeStream COMPONENT_NAME CommonUtils LABELS utils SOURCES test/testTreeStream.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils O2::ReconstructionDataFormats) -o2_add_test(BoostSerializer - COMPONENT_NAME CommonUtils - LABELS utils - SOURCES test/testBoostSerializer.cxx - PUBLIC_LINK_LIBRARIES O2::CommonUtils Boost::serialization) - o2_add_test(CompStream COMPONENT_NAME CommonUtils LABELS utils @@ -85,7 +90,22 @@ o2_add_test(MemFileHelper SOURCES test/testMemFileHelper.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils) +o2_add_test(EnumFlags + COMPONENT_NAME CommonUtils + LABELS utils + SOURCES test/testEnumFlags.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtils) + +o2_add_test(ConfigurableParam + COMPONENT_NAME CommonUtils + LABELS utils + SOURCES test/testConfigurableParam.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtilsTest) + o2_add_executable(treemergertool COMPONENT_NAME CommonUtils SOURCES src/TreeMergerTool.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils Boost::program_options ROOT::Core) + +add_library(fpu_support OBJECT src/fpu.cxx) +add_library(O2::fpu_support ALIAS fpu_support) diff --git a/Common/Utils/include/CommonUtils/BoostHistogramUtils.h b/Common/Utils/include/CommonUtils/BoostHistogramUtils.h index 5986ba01da7e0..0d20e97adf7a7 100644 --- a/Common/Utils/include/CommonUtils/BoostHistogramUtils.h +++ b/Common/Utils/include/CommonUtils/BoostHistogramUtils.h @@ -45,10 +45,10 @@ #include using boostHisto2d = boost::histogram::histogram, boost::histogram::axis::regular>, boost::histogram::unlimited_storage>>; -using boostHisto1d = boost::histogram::histogram>, boost::histogram::unlimited_storage>>; +using boostHisto1d = boost::histogram::histogram>>; using boostHisto2d_VarAxis = boost::histogram::histogram>, boost::histogram::axis::variable>>>; -using boostHisto1d_VarAxis = boost::histogram::histogram>>>; +using boostHisto1d_VarAxis = boost::histogram::histogram>>; namespace o2 { @@ -313,7 +313,7 @@ std::vector fitGaus(Iterator first, Iterator last, BinCenterView axisfir if (nbins > 1) { // dont take bins with 0 entries or bins with nan into account // if y-value (*iter) is 1, log(*iter) will be 0. Exclude these cases - if (isnan(*axisiter) || isinf(*axisiter) || *iter <= 0 || *iter == 1) { + if (std::isnan(*axisiter) || std::isinf(*axisiter) || *iter <= 0 || *iter == 1) { continue; } double x = *axisiter; @@ -401,10 +401,63 @@ std::vector fitBoostHistoWithGaus(boost::histogram::histogram& } /// \brief Convert a 1D root histogram to a Boost histogram -boostHisto1d_VarAxis boosthistoFromRoot_1D(TH1D* inHist1D); +template +Hist boosthistoFromRoot_1D(TH1D* inHist1D) +{ + // first setup the proper boost histogram + int nBins = inHist1D->GetNbinsX(); + std::vector binEdges; + for (int i = 0; i < nBins + 1; i++) { + binEdges.push_back(inHist1D->GetBinLowEdge(i + 1)); + } + Hist mHisto; + + if constexpr (std::is_same::value) { + mHisto = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdges)); + } else { + mHisto = boost::histogram::make_histogram(boost::histogram::axis::regular<>(nBins, binEdges[0], binEdges.back())); + } + + // trasfer the acutal values + for (Int_t x = 1; x < nBins + 1; x++) { + mHisto.at(x - 1) = inHist1D->GetBinContent(x); + } + return mHisto; +} /// \brief Convert a 2D root histogram to a Boost histogram -boostHisto2d_VarAxis boostHistoFromRoot_2D(TH2D* inHist2D); +template +Hist boostHistoFromRoot_2D(TH2D* inHist2D) +{ + // Get Xaxis binning + const int nBinsX = inHist2D->GetNbinsX(); + std::vector binEdgesX; + for (int i = 0; i < nBinsX + 1; i++) { + binEdgesX.push_back(inHist2D->GetXaxis()->GetBinLowEdge(i + 1)); + } + // Get Yaxis binning + const int nBinsY = inHist2D->GetNbinsY(); + std::vector binEdgesY; + for (int i = 0; i < nBinsY + 1; i++) { + binEdgesY.push_back(inHist2D->GetYaxis()->GetBinLowEdge(i + 1)); + } + + Hist mHisto; + + if constexpr (std::is_same::value) { + mHisto = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdgesX), boost::histogram::axis::variable<>(binEdgesY)); + } else { + mHisto = boost::histogram::make_histogram(boost::histogram::axis::regular<>(nBinsX, binEdgesX[0], binEdgesX.back()), boost::histogram::axis::regular<>(nBinsY, binEdgesY[0], binEdgesY.back())); + } + + // trasfer the acutal values + for (Int_t x = 1; x < nBinsX + 1; x++) { + for (Int_t y = 1; y < nBinsY + 1; y++) { + mHisto.at(x - 1, y - 1) = inHist2D->GetBinContent(x, y); + } + } + return mHisto; +} /// \brief Get the mean of a 1D boost histogram /// \param inHist1D input boost histogram @@ -565,7 +618,7 @@ auto ProjectBoostHistoXFast(const boost::histogram::histogram& hist2d, /// \return result /// 1d boost histogram from projection of the input 2d boost histogram template -auto ReduceBoostHistoFastSlice(boost::histogram::histogram& hist2d, int binXLow, int binXHigh, int binYLow, int binYHigh, bool includeOverflowUnderflow) +auto ReduceBoostHistoFastSlice(const boost::histogram::histogram& hist2d, int binXLow, int binXHigh, int binYLow, int binYHigh, bool includeOverflowUnderflow) { int nXbins = binXHigh - binXLow + 1; int nYbins = binYHigh - binYLow + 1; @@ -661,6 +714,29 @@ auto ReduceBoostHistoFastSliceByValue(boost::histogram::histogram& hist return ReduceBoostHistoFastSlice(hist2d, binXLow, binXHigh, binYLow, binYHigh, includeOverflowUnderflow); } +/// \brief Function to integrate 1d boost histogram in specified range +/// \param hist 1d boost histogram +/// \param min lower integration range +/// \param max upper integration range +/// \return sum of bin contents in specified range +template +double getIntegralBoostHist(boost::histogram::histogram& hist, double min, double max) +{ + // find bins for min and max values + std::array axisLimitsIndex = {hist.axis(0).index(min), hist.axis(0).index(max)}; + // over/underflow bin have to be protected + for (auto& bin : axisLimitsIndex) { + if (bin < 0) { + bin = 0; + } else if (bin >= hist.axis(0).size()) { + bin = hist.axis(0).size() - 1; + } + } + // Reduce histogram to desired range + auto slicedHist = ReduceBoostHistoFastSlice1D(hist, axisLimitsIndex[0], axisLimitsIndex[1], false); + return boost::histogram::algorithm::sum(slicedHist); +} + } // namespace utils } // end namespace o2 diff --git a/Common/Utils/include/CommonUtils/BoostSerializer.h b/Common/Utils/include/CommonUtils/BoostSerializer.h deleted file mode 100644 index 4c388a3daa8cc..0000000000000 --- a/Common/Utils/include/CommonUtils/BoostSerializer.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file BoostSerializer.h -/// \brief Templatised boost serializer/deserializer for containers and base types -/// \author Gabriele G. Fronzé -/// \date 17 July 2018 - -#ifndef ALICEO2_BOOSTSERIALIZER_H -#define ALICEO2_BOOSTSERIALIZER_H - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace o2 -{ -namespace utils -{ -template -std::ostringstream BoostSerialize(const ContT& dataSet) -{ - /// Serialises a container (vector, array or list) using boost serialisation routines. - /// Requires the contained type to be either trivial or provided with an overried of boost::serialise method. - std::ostringstream buffer; - boost::archive::binary_oarchive outputArchive(buffer); - outputArchive << dataSet; - return buffer; -} - -template -ContT BoostDeserialize(std::string& msgStr) -{ - /// Deserialises a msg contained in a string in a container type (vector, array or list) of the provided type. - ContT output; - std::istringstream buffer(msgStr); - boost::archive::binary_iarchive inputArchive(buffer); - inputArchive >> output; - return std::move(output); -} -} // namespace utils -} // namespace o2 - -#endif //ALICEO2_BOOSTSERIALIZER_H diff --git a/Common/Utils/include/CommonUtils/ConfigurableParam.h b/Common/Utils/include/CommonUtils/ConfigurableParam.h index 8b075aebd9a9e..fa4acd2744703 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParam.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParam.h @@ -9,17 +9,28 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -//first version 8/2018, Sandro Wenzel +// first version 8/2018, Sandro Wenzel #ifndef COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_ #define COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_ -#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include -#include +#include +#include #include #include +#include class TFile; class TRootIOCtor; @@ -134,6 +145,214 @@ class EnumRegistry std::unordered_map entries; }; +template +concept Container = !std::is_same_v, std::string> && requires(T t) { + typename T::value_type; + typename T::iterator; + { t.begin() } -> std::same_as; + { t.end() } -> std::same_as; +}; + +template +concept MapLike = Container && requires { + typename T::key_type; + typename T::mapped_type; +}; + +template +concept SequenceContainer = Container && !MapLike; + +template +inline constexpr bool AlwaysFalse = false; + +class ContainerParser +{ + public: + template + static T parse(const std::string& str) + { + if constexpr (MapLike) { + return parseMap(str); + } else if constexpr (Container) { + // Covers vector/list/deque as well as set/unordered_set: parseSequence + // inserts at end(), which both sequence and associative-set containers + // accept. (Any non-map Container is a SequenceContainer, so the previous + // separate parseSet branch was unreachable and re-parsed into a temporary + // vector first.) + return parseSequence(str); + } else { + return parseScalar(str); + } + } + + static std::string trim(const std::string& str) + { + auto start = str.find_first_not_of(" \t\n\r\f\v"); + if (start == std::string::npos) { + return ""; + } + auto end = str.find_last_not_of(" \t\n\r\f\v"); + return str.substr(start, end - start + 1); + } + + private: + // Parse sequence and set containers (vector, list, deque, set, unordered_set) + template + static SequenceT parseSequence(const std::string& str) + { + SequenceT result; + using ValueType = typename SequenceT::value_type; + std::string cleaned = str; + if (!cleaned.empty() && cleaned.front() == '[' && cleaned.back() == ']') { // removed brackets [1,2,3] -> 1,2,3 + cleaned = cleaned.substr(1, cleaned.length() - 2); + } + if (cleaned.empty() || cleaned == "{}") { // nothing to do + return result; + } + if constexpr (Container) { + static_assert(AlwaysFalse, "Nested containers are not supported as configurable parameters"); + } + auto tokens = split(cleaned, ','); + for (const auto& token : tokens) { + std::string trimmed = trim(token); + result.insert(result.end(), parseScalar(trimmed)); + } + return result; + } + + // Parse map, unordered_map, multimap + template + static MapT parseMap(const std::string& str) + { + MapT result; + using KeyType = typename MapT::key_type; + using ValueType = typename MapT::mapped_type; + std::string cleaned = str; + if (!cleaned.empty() && cleaned.front() == '{' && cleaned.back() == '}') { // stip braces {a:1,b:2} -> a:1,b:2 + cleaned = cleaned.substr(1, cleaned.length() - 2); + } + if (cleaned.empty()) { // nothing to do + return result; + } + if constexpr (Container || Container) { + static_assert(AlwaysFalse, "Nested containers are not supported as configurable parameters"); + } + auto pairs = split(cleaned, ','); + for (const auto& pair_str : pairs) { + auto kv = split(pair_str, ':'); + if (kv.size() != 2) { + throw std::runtime_error("Invalid map syntax: " + pair_str + ". Expected 'key:value' format, got "); + } + KeyType key = parseScalar(trim(kv[0])); + result[key] = parseScalar(trim(kv[1])); + } + return result; + } + + // Parse scalar types + template + static T parseScalar(const std::string& str) + { + if constexpr (std::is_same_v) { + return str; + } else if constexpr (std::is_same_v) { + std::string lower = str; + std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); + if (lower == "true" || lower == "1") { + return true; + } + if (lower == "false" || lower == "0") { + return false; + } + throw std::runtime_error("Invalid boolean value: " + str); + } else if constexpr (std::is_same_v || std::is_same_v) { + size_t pos = 0; + long long value = std::stoll(str, &pos); + if (pos != str.size()) { + throw std::runtime_error("Failed to parse '" + str + "' as char type"); + } + if (value < std::numeric_limits::min() || value > std::numeric_limits::max()) { + throw std::runtime_error("Value out of range for char type: " + str); + } + return static_cast(value); + } else if constexpr (std::is_same_v) { + size_t pos = 0; + unsigned long long value = std::stoull(str, &pos); + if (pos != str.size()) { + throw std::runtime_error("Failed to parse '" + str + "' as unsigned char type"); + } + if (value > std::numeric_limits::max()) { + throw std::runtime_error("Value out of range for unsigned char type: " + str); + } + return static_cast(value); + } else if constexpr (std::is_integral_v && std::is_unsigned_v) { + if (!str.empty() && str.front() == '-') { + throw std::runtime_error("Value out of range for unsigned integer type: " + str); + } + size_t pos = 0; + unsigned long long value = std::stoull(str, &pos); + if (pos != str.size() || value > std::numeric_limits::max()) { + throw std::runtime_error("Failed to parse '" + str + "' as unsigned integer type"); + } + return static_cast(value); + } else if constexpr (std::is_integral_v) { + size_t pos = 0; + long long value = std::stoll(str, &pos); + if (pos != str.size() || value < std::numeric_limits::min() || value > std::numeric_limits::max()) { + throw std::runtime_error("Failed to parse '" + str + "' as signed integer type"); + } + return static_cast(value); + } else if constexpr (std::is_floating_point_v) { + size_t pos = 0; + long double value = std::stold(str, &pos); + if (pos != str.size()) { + throw std::runtime_error("Failed to parse '" + str + "' as floating point type"); + } + return static_cast(value); + } else { + std::istringstream iss(str); + T value; + iss >> value; + iss >> std::ws; + if (iss.fail() || !iss.eof()) { + throw std::runtime_error("Failed to parse '" + str + "' as " + typeid(T).name()); + } + return value; + } + } + + // Split respecting nested brackets and braces + static std::vector split(const std::string& str, char delimiter) + { + std::vector tokens; + std::string current; + int bracket_depth = 0; + int brace_depth = 0; + for (char c : str) { + if (c == '[') { + bracket_depth++; + } else if (c == ']') { + bracket_depth--; + } else if (c == '{') { + brace_depth++; + } else if (c == '}') { + brace_depth--; + } else if (c == delimiter && bracket_depth == 0 && brace_depth == 0) { + // Keep empty fields: a stray delimiter (e.g. "[1,,3]" or "key:") must + // surface as a parse error downstream rather than silently dropping an + // element. The empty-container case ("[]"/"{}") is handled by the + // callers before split() is ever reached. + tokens.push_back(current); + current.clear(); + continue; + } + current += c; + } + tokens.push_back(current); + return tokens; + } +}; + class ConfigurableParam { public: @@ -160,7 +379,7 @@ class ConfigurableParam virtual std::string getName() const = 0; // print the current keys and values to screen (optionally with provenance information) - virtual void printKeyValues(bool showprov = true, bool useLogger = false) const = 0; + virtual void printKeyValues(bool showprov = true, bool useLogger = false, bool withPadding = false, bool showHash = false) const = 0; // get a single size_t hash_value of this parameter (can be used as a checksum to see // if object changed or different) @@ -174,15 +393,10 @@ class ConfigurableParam static void printAllRegisteredParamNames(); static void printAllKeyValuePairs(bool useLogger = false); - static const std::string& getInputDir() { return sInputDir; } static const std::string& getOutputDir() { return sOutputDir; } - static void setInputDir(const std::string& d) { sInputDir = d; } static void setOutputDir(const std::string& d) { sOutputDir = d; } - static boost::property_tree::ptree readINI(std::string const& filepath); - static boost::property_tree::ptree readJSON(std::string const& filepath); - static boost::property_tree::ptree readConfigFile(std::string const& filepath); static bool configFileExists(std::string const& filepath); // writes a human readable JSON file of all parameters @@ -190,14 +404,21 @@ class ConfigurableParam // writes a human readable INI file of all parameters static void writeINI(std::string const& filename, std::string const& keyOnly = ""); + // writes a human readable INI or JSON file depending on the extension + static void write(std::string const& filename, std::string const& keyOnly = ""); + + static std::string asJSON(std::string const& keyOnly = ""); + // can be used instead of using API on concrete child classes template static T getValueAs(std::string key) { - if (!sIsFullyInitialized) { - initialize(); - } - return sPtree->get(key); + return [](auto* tree, const std::string& key) -> T { + if (!sIsFullyInitialized) { + initialize(); + } + return tree->template get(key); + }(sPtree, key); } template @@ -206,44 +427,52 @@ class ConfigurableParam if (!sIsFullyInitialized) { initialize(); } - assert(sPtree); - try { - auto key = mainkey + "." + subkey; - if (sPtree->get_optional(key).is_initialized()) { - sPtree->put(key, x); - auto changed = updateThroughStorageMap(mainkey, subkey, typeid(T), (void*)&x); - if (changed != EParamUpdateStatus::Failed) { - sValueProvenanceMap->find(key)->second = kRT; // set to runtime + return [&subkey, &x, &mainkey](auto* tree) -> void { + assert(tree); + try { + auto key = mainkey + "." + subkey; + if (tree->template get_optional(key).is_initialized()) { + tree->put(key, x); + auto changed = updateThroughStorageMap(mainkey, subkey, typeid(T), (void*)&x); + if (changed != EParamUpdateStatus::Failed) { + sValueProvenanceMap->find(key)->second = kRT; // set to runtime + } } + } catch (std::exception const& e) { + std::cerr << "Error in setValue (T) " << e.what() << "\n"; } - } catch (std::exception const& e) { - std::cerr << "Error in setValue (T) " << e.what() << "\n"; - } + }(sPtree); } - // specialized for std::string - // which means that the type will be converted internally - static void setValue(std::string const& key, std::string const& valuestring) + static void setProvenance(std::string const& mainkey, std::string const& subkey, EParamProvenance p) { if (!sIsFullyInitialized) { - initialize(); + std::cerr << "setProvenance was called on non-initialized ConfigurableParam\n"; + return; } - assert(sPtree); try { - if (sPtree->get_optional(key).is_initialized()) { - sPtree->put(key, valuestring); - auto changed = updateThroughStorageMapWithConversion(key, valuestring); - if (changed != EParamUpdateStatus::Failed) { - sValueProvenanceMap->find(key)->second = kRT; // set to runtime - } + auto key = mainkey + "." + subkey; + auto keyProv = sValueProvenanceMap->find(key); + if (keyProv != sValueProvenanceMap->end()) { + keyProv->second = p; } } catch (std::exception const& e) { - std::cerr << "Error in setValue (string) " << e.what() << "\n"; + std::cerr << "Error in setProvenance (T) " << e.what() << "\n"; } } + // specialized for std::string + // which means that the type will be converted internally + static void setValue(std::string const& key, std::string const& valuestring); static void setEnumValue(const std::string&, const std::string&); static void setArrayValue(const std::string&, const std::string&); + static void setContainerValue(const std::string&, const std::string&); + static bool isRegisteredContainerType(const std::string& typeName); + static void registerContainerType(const std::string& key, const std::string& typeName); + static std::string getRegisteredContainerType(const std::string& key); + static bool assignRegisteredContainer(const std::string& typeName, void* target, const void* source); + static bool areRegisteredContainersEqual(const std::string& typeName, const void* lhs, const void* rhs); + static std::string registeredContainerAsString(const std::string& typeName, const void* source); // update the storagemap from a vector of key/value pairs, calling setValue for each pair static void setValues(std::vector> const& keyValues); @@ -251,7 +480,7 @@ class ConfigurableParam // initializes the parameter database static void initialize(); - // create CCDB snapsnot + // create CCDB snapshot static void toCCDB(std::string filename); // load from (CCDB) snapshot static void fromCCDB(std::string filename); @@ -267,6 +496,9 @@ class ConfigurableParam // be updated, absence of data for any of requested params will lead to fatal static void updateFromFile(std::string const&, std::string const& paramsList = "", bool unchangedOnly = false); + // update from a JSON string with the same filtering semantics as updateFromFile + static void updateFromJSONString(std::string const&, std::string const& paramsList = "", bool unchangedOnly = false); + // interface for use from the CCDB API; allows to sync objects read from CCDB with the information // stored in the registry; modifies given object as well as registry virtual void syncCCDBandRegistry(void* obj) = 0; @@ -302,7 +534,6 @@ class ConfigurableParam // (stored as a vector of pairs ) static EnumRegistry* sEnumRegistry; - static std::string sInputDir; static std::string sOutputDir; void setRegisterMode(bool b) { sRegisterMode = b; } @@ -322,17 +553,19 @@ class ConfigurableParam } // end namespace o2 // a helper macro for boilerplate code in parameter classes -#define O2ParamDef(classname, key) \ - public: \ - classname(TRootIOCtor*) {} \ - classname(classname const&) = delete; \ - \ - private: \ - static constexpr char const* const sKey = key; \ - static classname sInstance; \ - classname() = default; \ - template \ - friend class o2::conf::ConfigurableParamHelper; +#define O2ParamDef(classname, key) \ + public: \ + classname(TRootIOCtor*) {} \ + classname(classname const&) = delete; \ + \ + private: \ + static constexpr char const* const sKey = key; \ + static classname sInstance; \ + classname() = default; \ + template \ + friend class o2::conf::ConfigurableParamHelper; \ + template \ + friend class o2::conf::ConfigurableParamPromoter; // a helper macro to implement necessary symbols in source #define O2ParamImpl(classname) classname classname::sInstance; diff --git a/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h b/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h index 1dc5d5c4c38f8..f52c2079c6005 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h @@ -9,21 +9,59 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -//first version 8/2018, Sandro Wenzel +// first version 8/2018, Sandro Wenzel #ifndef COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAMHELPER_H_ #define COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAMHELPER_H_ #include "CommonUtils/ConfigurableParam.h" -#include "TClass.h" + +#include +#include +#include +#include +#include +#include +#include #include #include -#include "TFile.h" +#include -namespace o2 +namespace o2::conf { -namespace conf + +// ---------------------------------------------------------------- + +inline std::size_t damerauLevenshteinDistance(std::string_view a, std::string_view b) { + const std::size_t n = a.size(); + const std::size_t m = b.size(); + if (n == 0) { + return m; + } + if (m == 0) { + return n; + } + std::vector prev(m + 1), curr(m + 1), prev2(m + 1); + std::iota(prev.begin(), prev.end(), 0); + for (std::size_t i = 1; i <= n; ++i) { + curr[0] = i; + for (std::size_t j = 1; j <= m; ++j) { + std::size_t cost = (a[i - 1] == b[j - 1]) ? 0 : 1; + curr[j] = std::min({prev[j] + 1, + curr[j - 1] + 1, + prev[j - 1] + cost}); + if (i > 1 && j > 1 && a[i - 1] == b[j - 2] && + a[i - 2] == b[j - 1]) { + curr[j] = std::min(curr[j], prev2[j - 2] + 1); + } + } + prev2 = std::move(prev); + prev = std::move(curr); + curr.assign(m + 1, 0); + } + return prev[m]; +} // ---------------------------------------------------------------- @@ -34,7 +72,7 @@ struct ParamDataMember { std::string value; std::string provenance; - std::string toString(std::string const& prefix, bool showProv) const; + std::string toString(std::string const& prefix, bool showProv, size_t padding = 0) const; }; // ---------------------------------------------------------------- @@ -45,26 +83,29 @@ class _ParamHelper { private: static std::vector* getDataMembersImpl(std::string const& mainkey, TClass* cl, void*, - std::map const* provmap); + std::map const* provmap, size_t virtualoffset); static void fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void*, boost::property_tree::ptree*, std::map>*, - EnumRegistry*); + EnumRegistry*, size_t offset); static void printWarning(std::type_info const&); static void assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap); + std::map* provmap, size_t offset); static void syncCCDBandRegistry(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap); + std::map* provmap, size_t offset); - static void outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger); - static void printMembersImpl(std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger); + static void outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger, bool withPadding = false, bool showHash = false); + static void printMembersImpl(std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger, bool withPadding, bool showHash); static size_t getHashImpl(std::string const& mainkey, std::vector const* members); template friend class ConfigurableParamHelper; + + template + friend class ConfigurableParamPromoter; }; // ---------------------------------------------------------------- @@ -97,27 +138,28 @@ class ConfigurableParamHelper : virtual public ConfigurableParam // ---------------------------------------------------------------- // one of the key methods, using introspection to print itself - void printKeyValues(bool showProv = true, bool useLogger = false) const final + void printKeyValues(bool showProv = true, bool useLogger = false, bool withPadding = true, bool showHash = true) const final { if (!isInitialized()) { initialize(); } - auto members = getDataMembers(); - _ParamHelper::printMembersImpl(getName(), members, showProv, useLogger); + auto members = std::unique_ptr>(getDataMembers()); + _ParamHelper::printMembersImpl(getName(), members.get(), showProv, useLogger, withPadding, showHash); } // size_t getHash() const final { - return _ParamHelper::getHashImpl(getName(), getDataMembers()); + auto members = std::unique_ptr>(getDataMembers()); + return _ParamHelper::getHashImpl(getName(), members.get()); } // ---------------------------------------------------------------- void output(std::ostream& out) const final { - auto members = getDataMembers(); - _ParamHelper::outputMembersImpl(out, getName(), members, true, false); + auto members = std::unique_ptr>(getDataMembers()); + _ParamHelper::outputMembersImpl(out, getName(), members.get(), true, false); } // ---------------------------------------------------------------- @@ -140,7 +182,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam return nullptr; } - return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap); + return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap, 0); } // ---------------------------------------------------------------- @@ -153,7 +195,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam _ParamHelper::printWarning(typeid(P)); return; } - _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry); + _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry, 0); } // ---------------------------------------------------------------- @@ -167,7 +209,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam file->GetObject(getName().c_str(), readback); if (readback != nullptr) { _ParamHelper::assignmentImpl(getName(), TClass::GetClass(typeid(P)), (void*)this, (void*)readback, - sValueProvenanceMap); + sValueProvenanceMap, 0); delete readback; } setRegisterMode(true); @@ -185,7 +227,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam // setRegisterMode(false); _ParamHelper::syncCCDBandRegistry(getName(), TClass::GetClass(typeid(P)), (void*)this, (void*)externalobj, - sValueProvenanceMap); + sValueProvenanceMap, 0); setRegisterMode(true); } @@ -197,7 +239,159 @@ class ConfigurableParamHelper : virtual public ConfigurableParam } }; -} // namespace conf -} // namespace o2 +// Promotes a simple struct Base to a configurable parameter class +// Aka implements all interfaces for a ConfigurableParam P, which shares or +// takes the fields from a Base struct +template +class ConfigurableParamPromoter : public Base, virtual public ConfigurableParam +{ + public: + using ConfigurableParam::ConfigurableParam; + + static const P& Instance() + { + return P::sInstance; + } + + // extracts a copy of the underlying data struct + Base detach() const + { + static_assert(std::copyable, "Base type must be copyable."); + return static_cast(*this); + } + + // ---------------------------------------------------------------- + std::string getName() const final + { + return P::sKey; + } + + // ---------------------------------------------------------------- + // get the provenace of the member with given key + EParamProvenance getMemberProvenance(const std::string& key) const final + { + return getProvenance(getName() + '.' + key); + } + + // ---------------------------------------------------------------- + + // one of the key methods, using introspection to print itself + void printKeyValues(bool showProv = true, bool useLogger = false, bool withPadding = true, bool showHash = true) const final + { + if (!isInitialized()) { + initialize(); + } + auto members = std::unique_ptr>(getDataMembers()); + _ParamHelper::printMembersImpl(getName(), members.get(), showProv, useLogger, withPadding, showHash); + } + + // + size_t getHash() const final + { + auto members = std::unique_ptr>(getDataMembers()); + return _ParamHelper::getHashImpl(getName(), members.get()); + } + + // ---------------------------------------------------------------- + + void output(std::ostream& out) const final + { + auto members = std::unique_ptr>(getDataMembers()); + _ParamHelper::outputMembersImpl(out, getName(), members.get(), true, false); + } + + // ---------------------------------------------------------------- + + // Grab the list of ConfigurableParam data members + // Returns a nullptr if the TClass of the P template class cannot be created. + std::vector* getDataMembers() const + { + // just a helper line to make sure P::sInstance is looked-up + // and that compiler complains about missing static sInstance of type P + // volatile void* ptr = (void*)&P::sInstance; + // static assert on type of sInstance: + static_assert(std::is_same::value, + "static instance must of same type as class"); + + // obtain the TClass for the Base type and delegate further + auto cl = TClass::GetClass(typeid(Base)); + if (!cl) { + _ParamHelper::printWarning(typeid(Base)); + return nullptr; + } + + // we need to put an offset of 8 bytes since internally this is using data members of the Base class + // which doesn't account for the virtual table of P + return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap, 8); + } + + // ---------------------------------------------------------------- + + // fills the data structures with the initial default values + void putKeyValues(boost::property_tree::ptree* tree) final + { + auto cl = TClass::GetClass(typeid(Base)); + if (!cl) { + _ParamHelper::printWarning(typeid(Base)); + return; + } + _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry, 8); + } + + // ---------------------------------------------------------------- + + void initFrom(TFile* file) final + { + // switch off auto registering since the readback object is + // only a "temporary" singleton + setRegisterMode(false); + P* readback = nullptr; + file->GetObject(getName().c_str(), readback); + if (readback != nullptr) { + _ParamHelper::assignmentImpl(getName(), TClass::GetClass(typeid(Base)), (void*)this, (void*)readback, + sValueProvenanceMap, 8); + delete readback; + } + setRegisterMode(true); + } + + // ---------------------------------------------------------------- + + void syncCCDBandRegistry(void* externalobj) final + { + // We may be getting an external copy from CCDB which is passed as externalobj. + // The task of this function is to + // a) update the internal registry with fields coming from CCDB + // but only if keys have not been modified via RT == command line / ini file + // b) update the external object with with fields having RT provenance + // + setRegisterMode(false); + _ParamHelper::syncCCDBandRegistry(getName(), TClass::GetClass(typeid(Base)), (void*)this, (void*)externalobj, + sValueProvenanceMap, 8); + setRegisterMode(true); + } + + // ---------------------------------------------------------------- + + void serializeTo(TFile* file) const final + { + file->WriteObjectAny((void*)this, TClass::GetClass(typeid(P)), getName().c_str()); + } +}; + +inline bool isContainer(const std::string& typeName) +{ + return ConfigurableParam::isRegisteredContainerType(typeName); +} + +inline bool isContainer(TDataMember const& dm) +{ + if (auto* cl = dm.GetClass(); cl && isContainer(cl->GetName())) { + return true; + } + return isContainer(dm.GetTrueTypeName()) || isContainer(dm.GetFullTypeName()); +} + +} // namespace o2::conf #endif /* COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAMHELPER_H_ */ diff --git a/Common/Utils/include/CommonUtils/ConfigurableParamReaders.h b/Common/Utils/include/CommonUtils/ConfigurableParamReaders.h new file mode 100644 index 0000000000000..d5ecd6cb97f7a --- /dev/null +++ b/Common/Utils/include/CommonUtils/ConfigurableParamReaders.h @@ -0,0 +1,36 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef O2_COMMON_UTILS_CONFIGURABLEPARAMREADERS_H_ +#define O2_COMMON_UTILS_CONFIGURABLEPARAMREADERS_H_ + +#include +#include + +namespace o2::conf +{ + +// Helpers to read ConfigurableParam from different file formats +class ConfigurableParamReaders +{ + public: + static void setInputDir(const std::string& d) { sInputDir = d; } + static const std::string& getInputDir() { return sInputDir; } + + static boost::property_tree::ptree readINI(std::string const& filepath); + static boost::property_tree::ptree readJSON(std::string const& filepath); + static boost::property_tree::ptree readConfigFile(std::string const& filepath); + + private: + static std::string sInputDir; +}; + +} // namespace o2::conf +#endif // O2_COMMON_UTILS_CONF_CONFIGURABLEPARAMREADERS_H_ diff --git a/Common/Utils/include/CommonUtils/ConfigurableParamTest.h b/Common/Utils/include/CommonUtils/ConfigurableParamTest.h new file mode 100644 index 0000000000000..09d7b45ef1608 --- /dev/null +++ b/Common/Utils/include/CommonUtils/ConfigurableParamTest.h @@ -0,0 +1,56 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef COMMON_CONFIGURABLE_PARAM_TEST_H_ +#define COMMON_CONFIGURABLE_PARAM_TEST_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +#include +#include +#include +#include +#include + +namespace o2::conf::test +{ +struct TestParam : public o2::conf::ConfigurableParamHelper { + enum TestEnum : uint8_t { + A, + B, + C + }; + + int iValue{42}; + float fValue{3.14}; + double dValue{3.14}; + bool bValue{true}; + unsigned uValue{1}; + long lValue{1}; + unsigned long ulValue{1}; + long long llValue{1}; + unsigned long long ullValue{1}; + std::string sValue = "default"; + int iValueProvenanceTest{0}; + TestEnum eValue = TestEnum::C; + int caValue[3] = {0, 1, 2}; + std::vector vec; + std::vector u8vec; + std::map map; + std::map smap; + std::set set; + + O2ParamDef(TestParam, "TestParam"); +}; +} // namespace o2::conf::test + +#endif diff --git a/Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h b/Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h index f16e501d96d40..be9bbcad25772 100644 --- a/Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h +++ b/Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h @@ -69,6 +69,21 @@ T GetFromMacro(const std::string& file, const std::string& funcname, const std:: return *ptr; } +// just-in-time interpret some C++ function using ROOT and make result available to runtime +// functiondecl: A string coding the function to call (example "bool foo(){ return true; }") +// funcname: The name of the function to call (example "foo()") +// type: Return type of function (example "bool") +// unique: Some unique string identifier under which the result will be stored in gROOT global variable space +template +T JITAndEvalFunction(const std::string& functiondecl, const std::string& funcname, const std::string& type, const std::string& unique) +{ + /** interpret and execute a function and retrieve pointer to the returned type **/ + auto line = Form("%s; %s __%s__ = %s;", functiondecl.c_str(), type.c_str(), unique.c_str(), funcname.c_str()); + gROOT->ProcessLine(line); + auto ptr = (T*)gROOT->GetGlobal(Form("__%s__", unique.c_str()))->GetAddress(); + return *ptr; +} + } // namespace conf } // namespace o2 diff --git a/Common/Utils/include/CommonUtils/DLLoaderBase.h b/Common/Utils/include/CommonUtils/DLLoaderBase.h new file mode 100644 index 0000000000000..61d349876c92e --- /dev/null +++ b/Common/Utils/include/CommonUtils/DLLoaderBase.h @@ -0,0 +1,266 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// \brief A thin wrapper to manage dynamic library loading, based around boost::dll + +#ifndef DLLOADER_H_ +#define DLLOADER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "dlfcn.h" + +#if defined(__APPLE__) +#define DLLOADER_MAC_LINUX(mac, linux) mac +#else +#define DLLOADER_MAC_LINUX(mac, linux) linux +#endif + +#include "Framework/Logger.h" + +namespace o2::utils +{ + +// Manages dynamic loading and unloading (or rather ensuring they are not +// unloaded for the duration of the program) of libraries and symbol lookups. It +// ensures thread-safety through the use of a mutex and implements the Meyers +// Singleton pattern to provide a single instance of the manager. +template +class DLLoaderBase +{ + public: + struct filename_decorations { + static constexpr const char* prefix = "lib"; // same prefix on mac and linux + static constexpr const char* suffix = DLLOADER_MAC_LINUX(".dylib", ".so"); + }; + enum Options { + none = 0, + global = RTLD_GLOBAL, + local = RTLD_LOCAL, + no_delete = RTLD_NODELETE, + no_load = RTLD_NOLOAD, + lazy = RTLD_LAZY, + }; + using handle_t = void; + using handle_ptr_t = handle_t*; + struct HandleDeleter { + void operator()(handle_ptr_t p) + { + if (p != nullptr) { + dlclose(p); + } + } + }; + using library_t = std::unique_ptr; + + // Returns the singleton instance of the manager. Any function should only be + // accessed through an instance. This being a singleton serves two purposes: + // 1. Libraries are loaded only once. + // 2. Once loaded they are not again unloaded until the end of the program. + static DerivedType& Instance() + { + return DerivedType::sInstance; + } + + // Loads a dynamic library by its name and stores its handle. Returns true + // if the library is successfully loaded or already loaded. + bool addLibrary(const std::string& library) + { + const std::lock_guard lock(mLock); + + if (mLibraries.find(library) != mLibraries.end()) { + return true; // Library already loaded + } + + if (mO2Path.empty()) { + if (const auto* path = std::getenv("O2_ROOT")) { + mO2Path = path; + } else { + LOGP(error, "$O2_ROOT not set!"); + return false; + } + } + + auto path = getO2Path(library); + if (!std::filesystem::exists(path)) { + LOGP(error, "Library under '{}' does not exist!", path); + return false; + } + + try { + auto lib = std::unique_ptr(dlopen(path.c_str(), mLoadPolicy)); + if (lib == nullptr) { + throw std::runtime_error("Library handle is nullptr!"); + } + mLibraries[library] = std::move(lib); + LOGP(info, "Loaded dynamic library '{}' from '{}'", library, path); + return true; + } catch (std::exception& e) { + LOGP(error, "Failed to load library (path='{}'), failed reason: '{}'", path, e.what()); + return false; + } catch (...) { + LOGP(error, "Failed to load library (path='{}') for unknown reason!", path.c_str()); + return false; + } + } + + // Unloads a given library returns true if this succeeded. + // + // Nota bene: Actually, we have very little control here when the unloading + // hapens since the linkder decides this based on if there is any reference + // left. And even if the reference counter goes to zero the linker is free to + // leave the library loaded and clean up whenever it wants. + bool unloadLibrary(const std::string& library) + { + const std::lock_guard lock(mLock); + + if (auto it = mLibraries.find(library); it != mLibraries.end()) { + mLibraries.erase(it); + return true; + } + + LOGP(error, "No '{}' library found, cannot unload it!", library); + return false; + } + + // Resets all loaded libraries and O2Path, this invalidates all outside kept + // references. + void reset() + { + mO2Path.clear(); + mLibraries.clear(); + } + + // Checks if a library contains a specific symbol. + bool hasSymbol(const std::string& library, const std::string& symbol) + { + const std::lock_guard lock(mLock); + + if (mLibraries.find(library) == mLibraries.end()) { + // Library not loaded, attempt to load it + if (!addLibrary(library)) { + return false; + } + } + + dlerror(); // clear previous error + + // Checks if the symbol exists but does not load it. + handle_ptr_t ptr = dlsym(mLibraries[library].get(), symbol.c_str()); + if (const auto* err = dlerror(); err != nullptr) { + LOGP(error, "Did not get {} from {}; error: {}", symbol, library, err); + } + + return ptr != nullptr; + } + + // Executes a function from a loaded library or return nullopt + template + std::optional executeFunction(const std::string& library, const std::string& fname, Args... args) + { + using Func_t = Ret (*)(Args...); + + const std::lock_guard lock(mLock); + + if (fname.empty()) { + LOGP(error, "Function name cannot be empty!"); + return std::nullopt; + } + + if (mLibraries.find(library) == mLibraries.end()) { + // Library not loaded, attempt to load it + if (!addLibrary(library)) { + return std::nullopt; + } + } + + const auto& lib = mLibraries[library].get(); + if (!hasSymbol(library, fname)) { + LOGP(error, "Library '{}' does not have a symbol '{}'", library, fname); + return std::nullopt; + } + + dlerror(); // Clear previous error + + auto func = (Func_t)dlsym(lib, fname.c_str()); + if (const auto* err = dlerror(); err != nullptr) { + LOGP(error, "Did not get {} from {}; error: {}", fname, library, err); + return std::nullopt; + } + + if (func == nullptr) { + LOGP(error, "Library '{}' does not have a symbol '{}' with {}", library, fname, getTypeName()); + return std::nullopt; + } + + // Execute function and return its return value + return func(args...); + } + + // Wrapper for function execution which fatals if execution fails + template + Ret executeFunctionAlias(const std::string& library, const std::string& fname, Args... args) + { + if (auto opt = executeFunction(library, fname, args...)) { + return *opt; + } + + LOGP(fatal, "Execution of '{}' from '{}' failed spectaculary!", fname, library); + __builtin_unreachable(); // is this safe, AFACIT only gcc and clang are supported anyway + } + + // Delete copy and move constructors to enforce singleton pattern. + DLLoaderBase(const DLLoaderBase&) = delete; + DLLoaderBase& operator=(const DLLoaderBase&) = delete; + DLLoaderBase(DLLoaderBase&&) = delete; + DLLoaderBase& operator=(DLLoaderBase&&) = delete; + + protected: + // Constructor and destructor are protected to enforce singleton pattern. + DLLoaderBase() = default; + ~DLLoaderBase() = default; + + private: + // Returns the full path to a O2 shared library.. + [[nodiscard]] std::string getO2Path(const std::string& library) const + { + return mO2Path + "/lib/" + filename_decorations::prefix + library + filename_decorations::suffix; + } + + // Returns the demangled type name of a prototype, e.g., for pretty printing. + template + [[nodiscard]] auto getTypeName() -> std::string + { + return typeid(ProtoType).name(); // TODO + } + + std::unordered_map mLibraries{}; // Pointers to loaded libraries, calls `unload()' for each library, e.g., correctly destroy this object + std::recursive_mutex mLock{}; // While a recursive mutex is more expansive it makes locking easier + std::string mO2Path{}; // Holds the path O2 dynamic library determined by $O2_ROOT + Options mLoadPolicy{Options::lazy}; // load resolution policy +}; + +} // namespace o2::utils + +#define O2DLLoaderDef(classname) \ + private: \ + static classname sInstance; \ + classname() = default; \ + friend class o2::utils::DLLoaderBase; + +#define O2DLLoaderImpl(classname) classname classname::sInstance; + +#endif // DLLOADER_H_ diff --git a/Common/Utils/include/CommonUtils/DebugStreamer.h b/Common/Utils/include/CommonUtils/DebugStreamer.h index 0c1ac9af6eef7..741afaabfdd60 100644 --- a/Common/Utils/include/CommonUtils/DebugStreamer.h +++ b/Common/Utils/include/CommonUtils/DebugStreamer.h @@ -19,6 +19,7 @@ #include "GPUCommonDef.h" #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) #include "CommonUtils/ConfigurableParamHelper.h" +#include #if defined(DEBUG_STREAMER) #include "CommonUtils/TreeStreamRedirector.h" #include @@ -30,15 +31,17 @@ namespace o2::utils /// struct defining the flags which can be used to check if a certain debug streamer is used enum StreamFlags { - streamdEdx = 1 << 0, ///< stream corrections and cluster properties used for the dE/dx - streamDigitFolding = 1 << 1, ///< stream ion tail and saturatio information - streamDigits = 1 << 2, ///< stream digit information - streamFastTransform = 1 << 3, ///< stream tpc fast transform - streamITCorr = 1 << 4, ///< stream ion tail correction information - streamDistortionsSC = 1 << 5, ///< stream distortions applied in the TPC space-charge class (used for example in the tpc digitizer) - streamUpdateTrack = 1 << 6, ///< stream update track informations - streamRejectCluster = 1 << 7, ///< stream cluster rejection informations - streamFlagsCount = 8 ///< total number of streamers + streamdEdx = 1 << 0, ///< stream corrections and cluster properties used for the dE/dx + streamDigitFolding = 1 << 1, ///< stream ion tail and saturatio information + streamDigits = 1 << 2, ///< stream digit information + streamFastTransform = 1 << 3, ///< stream tpc fast transform + streamITCorr = 1 << 4, ///< stream ion tail correction information + streamDistortionsSC = 1 << 5, ///< stream distortions applied in the TPC space-charge class (used for example in the tpc digitizer) + streamUpdateTrack = 1 << 6, ///< stream update track informations + streamRejectCluster = 1 << 7, ///< stream cluster rejection informations + streamMergeBorderTracksBest = 1 << 8, ///< stream MergeBorderTracks best track + streamMergeBorderTracksAll = 1 << 9, ///< stream MergeBorderTracks all tracks + streamFlagsCount = 10 ///< total number of streamers }; enum SamplingTypes { @@ -46,6 +49,8 @@ enum SamplingTypes { sampleRandom = 1, ///< sample randomly every n points sampleID = 2, ///< sample every n IDs (per example track) sampleIDGlobal = 3, ///< in case different streamers have access to the same IDs use this gloabl ID + sampleWeights = 4, ///< perform sampling on weights, defined where the streamer is called + sampleTsallis = 5, ///< perform sampling on tsallis pdf }; #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) @@ -121,6 +126,9 @@ class DebugStreamer /// \return returns sampling type and sampling frequency for given streamer static std::pair getSamplingTypeFrequency(const StreamFlags streamFlag); + /// \return returns sampling type and sampling frequency for given streamer + static float getSamplingFrequency(const StreamFlags streamFlag) { return getSamplingTypeFrequency(streamFlag).second; } + ///< return returns unique ID for each CPU thread to give each thread an own output file static size_t getCPUID(); @@ -134,7 +142,7 @@ class DebugStreamer std::string getUniqueTreeName(const char* tree, const size_t id = getCPUID()) const; /// set directly the debug level - static void setStreamFlags(const StreamFlags streamFlags) { o2::conf::ConfigurableParam::setValue("DebugStreamerParam", "streamLevel", static_cast(streamFlags)); } + static void setStreamFlags(const StreamFlags streamFlags) { o2::conf::ConfigurableParam::setValue("DebugStreamerParam", "streamLevel", static_cast(streamFlags)); } /// enable specific streamer flag static void enableStream(const StreamFlags streamFlag); @@ -144,7 +152,8 @@ class DebugStreamer /// check if streamer for specific flag is enabled /// \param samplingID optional index of the data which is streamed in to perform sampling on this index - static bool checkStream(const StreamFlags streamFlag, const size_t samplingID = -1); + /// \param weight weight which can be used to perform some weightes sampling + static bool checkStream(const StreamFlags streamFlag, const size_t samplingID = -1, const float weight = 1); /// merge trees with the same content structure, but different naming /// \param inpFile input file containing several trees with the same content @@ -155,10 +164,12 @@ class DebugStreamer /// \return returns integer index for given streamer flag static int getIndex(const StreamFlags streamFlag); + /// get random value between min and max + static float getRandom(float min = 0, float max = 1); + private: using StreamersPerFlag = tbb::concurrent_unordered_map>; StreamersPerFlag mTreeStreamer; ///< streamer which is used for the debugging - #else // empty implementation of the class for GPU or when the debug streamer is not build for CPU diff --git a/Common/Utils/include/CommonUtils/EnumFlags.h b/Common/Utils/include/CommonUtils/EnumFlags.h new file mode 100644 index 0000000000000..5032b6c0f1483 --- /dev/null +++ b/Common/Utils/include/CommonUtils/EnumFlags.h @@ -0,0 +1,820 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef O2_FRAMEWORK_FLAGS_H_ +#define O2_FRAMEWORK_FLAGS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef GPUCA_GPUCODE +#include "CommonUtils/StringUtils.h" +#endif + +namespace o2::utils +{ + +namespace details::enum_flags +{ + +// Require that an enum with an underlying unsigned type. +template +concept EnumFlagHelper = requires { + requires std::is_enum_v; + requires std::is_unsigned_v>; + requires std::same_as>; +}; + +// Static constexpr only helper struct to implement modicum of enum reflection +// functions and also check via concepts expected properties of the enum. +// This is very much inspired by much more extensive libraries like magic_enum. +// Inspiration by its c++20 version (https://github.com/fix8mt/conjure_enum). +// NOTE: Cannot detect if bit values past the underlying type are defined. +#ifndef GPUCA_GPUCODE +template +struct FlagsHelper final { + using U = std::underlying_type_t; + using UMax = uint64_t; // max represetable type + static_assert(std::numeric_limits::digits <= std::numeric_limits::digits, "Underlying type has more digits than max supported digits"); + + static constexpr bool isScoped() noexcept + { + return std::is_enum_v && !std::is_convertible_v>; + } + + // Return line at given position. + template + static consteval const char* tpeek() noexcept + { + return std::source_location::current().function_name(); + } + // string_view value of function above + template + static constexpr std::string_view tpeek_v{tpeek()}; + + // Compiler Specifics + static constexpr auto CSpecifics{std::to_array< + std::tuple>({ +#if defined __clang__ + {"e = ", ']', "(anonymous namespace)", '('}, + {"T = ", ']', "(anonymous namespace)", '('}, +#else // assuming __GNUC__ + {"e = ", ';', "", '<'}, + {"T = ", ']', "{anonymous}", '{'}, +#endif + })}; + enum class SVal : uint8_t { Start, + End, + AnonStr, + AnonStart }; + enum class SType : uint8_t { Enum_t, + Type_t, + eT0, + eT1, + eT2, + eT3 }; + // Extract a compiler specification. + template + static constexpr auto getSpec() noexcept + { + return std::get(v)>(CSpecifics[static_cast(t)]); + } + + // Range that is scanned by the compiler + static constexpr size_t MinScan{0}; + static constexpr size_t MarginScan{1}; // Scan one past to check for overpopulation + static constexpr size_t MaxUnderScan{std::numeric_limits::digits}; // Maximum digits the underlying type has + static constexpr size_t MaxScan{MaxUnderScan + MarginScan}; + + // Checks if a given 'location' contains an enum. + template + static constexpr bool isValid() noexcept + { + constexpr auto tp{tpeek_v.rfind(getSpec())}; + if constexpr (tp == std::string_view::npos) { + return false; + } +#if defined __clang__ + else if constexpr (tpeek_v[tp + getSpec().size()] == '(') { + if constexpr (tpeek_v[tp + getSpec().size() + 1] == '(') { + return false; + } + if constexpr (tpeek_v.find(getSpec(), tp + getSpec().size()) != std::string_view::npos) { + return true; + } + } else if constexpr (tpeek_v.find_first_of(getSpec(), tp + getSpec().size()) != std::string_view::npos) { + // check if this is an anonymous enum + return true; + } +#elif __GNUC__ + else if constexpr (tpeek_v[tp + getSpec().size()] != '(' && tpeek_v.find_first_of(getSpec(), tp + getSpec().size()) != std::string_view::npos) { + return true; + } +#else +#error Unsupported compiler +#endif + return false; + } + + // Extract which values are present in the enum by checking all values in + // the min-max-range above. + template + static constexpr auto getValues(std::index_sequence /*unused*/) noexcept + { + constexpr std::array valid{isValid(MinScan + I)>()...}; + constexpr auto count{std::count_if(valid.cbegin(), valid.cend(), [](bool v) noexcept { return v; })}; + static_assert(count > 0, "EnumFlag requires at least one enum value. Check that your enum has consecutive values starting from 0."); + static_assert(count <= MaxUnderScan, "Too many enum values for underlying type. Consider using a larger underlying type or fewer enum values."); + std::array values{}; + for (size_t idx{}, n{}; n < count; ++idx) { + if (valid[idx]) { + values[n++] = static_cast(MinScan + idx); + } + } + return values; + } + static constexpr auto Values{getValues(std::make_index_sequence())}; // Enum Values + static constexpr auto count() noexcept { return Values.size(); } // Number of enum members + static constexpr auto Min_v{Values.front()}; // Enum first entry + static constexpr auto Max_v{Values.back()}; // Enum last entry + static constexpr auto Min_u_v{static_cast(Min_v)}; // Enum first entry as size_t + static constexpr auto Max_u_v{static_cast(Max_v)}; // Enum last entry as size_t + static_assert(Max_u_v < std::numeric_limits::digits, "Max Bit is beyond allow range deferred from underlying type"); + static constexpr bool isContinuous() noexcept { return (Max_u_v - Min_u_v + 1) == count(); } // Is the enum continuous + static constexpr UMax makeMaxRep(size_t min, size_t max) + { + const size_t width = max - min + 1; + if (width >= std::numeric_limits::digits) { + return std::numeric_limits::max(); + } + return ((UMax(1) << width) - 1) << min; + } + static constexpr auto MaxRep{makeMaxRep(Min_u_v, Max_u_v)}; // largest representable value + + template + static constexpr std::string_view getName() + { + constexpr auto tp{tpeek_v.rfind(getSpec())}; + if constexpr (tp == std::string_view::npos) { + return {}; + } + if constexpr (tpeek_v[tp + getSpec().size()] == getSpec()) { +#if defined __clang__ + if constexpr (tpeek_v[tp + getSpec().size() + 1] == getSpec()) { + return {}; + } +#endif + if (constexpr auto lstr{tpeek_v.substr(tp + getSpec().size())}; lstr.find(getSpec()) != std::string_view::npos) { // is anon + if constexpr (constexpr auto lc{lstr.find_first_of(getSpec())}; lc != std::string_view::npos) { + return lstr.substr(getSpec().size() + 2, lc - (getSpec().size() + 2)); + } + } + } + constexpr std::string_view result{tpeek_v.substr(tp + getSpec().size())}; + if constexpr (constexpr auto lc{result.find_first_of(getSpec())}; lc != std::string_view::npos) { + return result.substr(0, lc); + } else { + return {}; + } + } + + static constexpr std::string_view removeScope(std::string_view s) + { + if (const auto lc{s.find_last_of(':')}; lc != std::string_view::npos) { + return s.substr(lc + 1); + } + return s; + } + + static constexpr std::string_view findScope(std::string_view s) + { + const auto pos1 = s.rfind("::"); + if (pos1 == std::string_view::npos) { + return s; + } + const auto pos2 = s.rfind("::", pos1 - 1); + if (pos2 == std::string_view::npos) { + return s.substr(0, pos1); + } + return s.substr(pos2 + 2, pos1 - pos2 - 2); + } + + template + static constexpr auto getNameValue{getName()}; + + template + static constexpr auto getNames(std::index_sequence /*unused*/) + { + if constexpr (with_scope) { + return std::array{getNameValue...}; + } else { + return std::array{removeScope(getNameValue)...}; + } + } + + static constexpr auto Names{getNames(std::make_index_sequence())}; // Enum names without scope + static constexpr auto NamesScoped{getNames(std::make_index_sequence())}; // Enum names with scope + static constexpr auto Scope{findScope(NamesScoped.front())}; // Enum scope + + static constexpr auto getLongestName() noexcept + { + size_t max{0}; + for (size_t i{0}; i < count(); ++i) { + max = std::max(max, Names[i].size()); + } + return max; + } + + static constexpr auto NamesLongest{getLongestName()}; // Size of longest name + + template + static constexpr std::string_view toString() noexcept + { + return getNameValue(); + } + + static constexpr std::optional fromString(std::string_view str) noexcept + { + for (size_t i{0}; i < count(); ++i) { + if (isIEqual(Names[i], str) || isIEqual(NamesScoped[i], str)) { + return Values[i]; + } + } + return std::nullopt; + } + + // Convert char to lower. + static constexpr unsigned char toLower(const unsigned char c) noexcept + { + return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; + } + + // Are these chars equal (case-insensitive). + static constexpr bool isIEqual(const unsigned char a, const unsigned char b) noexcept + { + return toLower(a) == toLower(b); + } + + // Case-insensitive comparison for string_view. + static constexpr bool isIEqual(std::string_view s1, std::string_view s2) noexcept + { + if (s1.size() != s2.size()) { + return false; + } + for (size_t i{0}; i < s1.size(); ++i) { + if (!isIEqual(s1[i], s2[i])) { + return false; + } + } + return true; + } + + static constexpr std::string_view None{"none"}; + static constexpr bool hasNone() noexcept + { + // check that enum does not contain member named 'none' + for (size_t i{0}; i < count(); ++i) { + if (isIEqual(Names[i], None)) { + return true; + } + } + return false; + } + + static constexpr std::string_view All{"all"}; + static constexpr bool hasAll() noexcept + { + // check that enum does not contain member named 'all' + for (size_t i{0}; i < count(); ++i) { + if (isIEqual(Names[i], All)) { + return true; + } + } + return false; + } +}; +#endif + +} // namespace details::enum_flags + +// Require an enum to fullfil what one would except from a bitset. +#ifndef GPUCA_GPUCODE +template +concept EnumFlag = requires { + // range checks + requires details::enum_flags::FlagsHelper::Min_u_v == 0; // the first bit should be at position 0 + requires details::enum_flags::FlagsHelper::Max_u_v < details::enum_flags::FlagsHelper::count(); // the maximum is less than the total + requires details::enum_flags::FlagsHelper::isContinuous(); // do not allow missing bits + + // type checks + requires !details::enum_flags::FlagsHelper::hasNone(); // added automatically + requires !details::enum_flags::FlagsHelper::hasAll(); // added automatically +}; +#else +template +concept EnumFlag = details::enum_flags::EnumFlagHelper; +#endif + +/** + * \brief Class to aggregate and manage enum-based on-off flags. + * + * This class manages flags as bits in the underlying type of an enum (upto 64 bits), allowing + * manipulation via enum member names. It supports operations akin to std::bitset + * but is fully constexpr and is ideal for aggregating multiple on-off booleans, + * e.g., enabling/disabling algorithm features. + * + * Example: + * enum class AlgoOptions { + * Feature1, + * Feature2, + * Feature3, + * }; + * ... + * EnumFlags opts; + * opts.set("Feature1 | Feature3"); // Set Feature1 and Feature3. + * if (opts[AlgoOptions::Feature1]) { // Do some work. } // Check if Feature1 is set. + * + * Additional examples of how to use this class are in testEnumFlags.cxx. + */ +template +class EnumFlags +{ + static constexpr int DefaultBase{2}; +#ifndef GPUCA_GPUCODE + using H = details::enum_flags::FlagsHelper; +#endif + using U = std::underlying_type_t; + U mBits{0}; + + // Converts enum to its underlying type. + constexpr auto to_underlying(E e) const noexcept + { + return static_cast(e); + } + + // Returns the bit representation of a flag. + constexpr auto to_bit(E e) const noexcept + { + return U(1) << to_underlying(e); + } + + public: + // Default constructor. + constexpr explicit EnumFlags() = default; + // Constructor to initialize with a single flag. + constexpr explicit EnumFlags(E e) : mBits(to_bit(e)) {} + // Copy constructor. + constexpr EnumFlags(const EnumFlags&) = default; + // Move constructor. + constexpr EnumFlags(EnumFlags&&) = default; + // Constructor to initialize with the underlying type. + constexpr explicit EnumFlags(U u) : mBits(u) {} + // Initialize with a list of flags. + constexpr EnumFlags(std::initializer_list flags) noexcept + { + for (const E f : flags) { + mBits |= to_bit(f); + } + } +#ifndef GPUCA_GPUCODE + // Init from a string. + // + explicit EnumFlags(const std::string& str, int base = DefaultBase) + { + set(str, base); + } +#endif + + static constexpr U None{0}; // Represents no flags set. +#ifndef GPUCA_GPUCODE + static constexpr U All{H::MaxRep}; // Represents all flags set. + + // Return list of all enum values + static constexpr auto getValues() noexcept + { + return H::Values; + } + + // Return list of all enum Names + static constexpr auto getNames() noexcept + { + return H::Names; + } + + // Sets flags from a string representation. + // This can be either from a number representation (binary or digits) or + // a concatenation of the enums members name e.g., 'Enum1|Enum2|...' + void set(const std::string& s, int base = DefaultBase) + { + if (s.empty()) { // no-op + return; + } + // on throw restore previous state and rethrow + const U prev = mBits; + reset(); + try { + setImpl(s, base); + } catch (const std::exception& e) { + mBits = prev; + throw; + } + } +#endif + // Returns the raw bitset value. + [[nodiscard]] constexpr auto value() const noexcept + { + return mBits; + } + + // Resets all flags. + constexpr void reset() noexcept + { + mBits = U(0); + } + + // Resets a specific flag. + template T> + constexpr void reset(T t) + { + mBits &= ~to_bit(t); + } + + // Tests if a specific flag is set. + template T> + [[nodiscard]] constexpr bool test(T t) const noexcept + { + return (mBits & to_bit(t)) != None; + } + + // Tests if all specified flags are set. + template ... Ts> + [[nodiscard]] constexpr bool test(Ts... flags) const noexcept + { + return ((test(flags) && ...)); + } + + // Sets a specific flag. + template T> + constexpr void set(T t) noexcept + { + mBits |= to_bit(t); + } + + // Sets multiple specific flags. + template ... Ts> + constexpr void set(Ts... flags) noexcept + { + (set(flags), ...); + } + + // Toggles a specific flag. + template T> + constexpr void toggle(T t) noexcept + { + mBits ^= to_bit(t); + } + + // Checks if any flag is set. + [[nodiscard]] constexpr bool any() const noexcept + { + return mBits != None; + } + + // Checks if all flags are set. +#ifndef GPUCA_GPUCODE + [[nodiscard]] constexpr bool all() const noexcept + { + return mBits == All; + } + + // Returns the bitset as a binary string. + [[nodiscard]] std::string string() const + { + std::ostringstream oss; + oss << std::bitset(mBits); + return oss.str(); + } + + // Returns the bitset as a pretty multiline binary string. + [[nodiscard]] std::string pstring(bool withNewline = false) const + { + std::ostringstream oss; + if (withNewline) { + oss << '\n'; + } + oss << "0b"; + const std::bitset bits(mBits); + oss << bits; + if constexpr (H::isScoped()) { + oss << " " << H::Scope; + } + oss << '\n'; + for (size_t i = 0; i < H::count(); ++i) { + oss << " "; + for (size_t j = 0; j < H::count() - i - 1; ++j) { + oss << "┃"; + } + oss << "┗"; + for (size_t a{2 + i}; --a != 0U;) { + oss << "━"; + } + oss << " " << std::setw(H::NamesLongest) << std::left + << H::Names[i] << " " << (bits[i] ? "[Active]" : "[Inactive]"); + if (i != H::count() - 1) { + oss << "\n"; + } + } + return oss.str(); + } +#endif + + // Checks if any flag is set (Boolean context). + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return any(); + } + + // Check if given flag is set. + template T> + [[nodiscard]] constexpr bool operator[](const T t) const noexcept + { + return test(t); + } + + // Checks if two flag sets are equal. + [[nodiscard]] constexpr bool operator==(const EnumFlags& o) const noexcept + { + return mBits == o.mBits; + } + + // Checks if two flag sets are not equal. + [[nodiscard]] constexpr bool operator!=(const EnumFlags& o) const noexcept + { + return mBits != o.mBits; + } + + // Copy assignment operator + constexpr EnumFlags& operator=(const EnumFlags& o) = default; + + // Move assignment operator + constexpr EnumFlags& operator=(EnumFlags&& o) = default; + + // Performs a bitwise OR with a flag. + template T> + constexpr EnumFlags& operator|=(T t) noexcept + { + mBits |= to_bit(t); + return *this; + } + + // Performs a bitwise AND with a flag. + template T> + constexpr EnumFlags& operator&=(T t) noexcept + { + mBits &= to_bit(t); + return *this; + } + + // Returns a flag set with a bitwise AND. + template T> + constexpr EnumFlags operator&(T t) const noexcept + { + return EnumFlags(mBits & to_bit(t)); + } + + // Returns a flag set with all bits inverted. + constexpr EnumFlags operator~() const noexcept + { + return EnumFlags(~mBits); + } + + // Performs a bitwise OR with another flag set. + constexpr EnumFlags operator|(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits | o.mBits); + } + + // Performs a bitwise OR assignment. + constexpr EnumFlags& operator|=(const EnumFlags& o) noexcept + { + mBits |= o.mBits; + return *this; + } + + // Performs a bitwise XOR with another flag set. + constexpr EnumFlags operator^(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits ^ o.mBits); + } + + // Performs a bitwise and with another flag set. + constexpr EnumFlags operator&(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits & o.mBits); + } + + // Performs a bitwise XOR assignment. + constexpr EnumFlags& operator^=(const EnumFlags& o) noexcept + { + mBits ^= o.mBits; + return *this; + } + + // Checks if all specified flags are set. + template + [[nodiscard]] constexpr bool all_of(Ts... flags) const noexcept + { + return test(flags...); + } + + // Checks if none of the specified flags are set. + template + [[nodiscard]] constexpr bool none_of(Ts... flags) const noexcept + { + return (!(test(flags) || ...)); + } + + // Serializes the flag set to a string. +#ifndef GPUCA_GPUCODE + [[nodiscard]] std::string serialize() const + { + return std::to_string(mBits); + } + + // Deserializes a string into the flag set. + void deserialize(const std::string& data) + { + typename H::UMax v = std::stoul(data); + if (v > H::MaxRep) { + throw std::out_of_range("Values exceeds enum range."); + } + mBits = static_cast(v); + } +#endif + + // Counts the number of set bits (active flags). + [[nodiscard]] constexpr size_t count() const noexcept + { + return std::popcount(mBits); + } + + // Returns the union of two flag sets. + [[nodiscard]] constexpr EnumFlags union_with(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits | o.mBits); + } + + // Returns the intersection of two flag sets. + [[nodiscard]] constexpr EnumFlags intersection_with(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits & o.mBits); + } + + // Checks if all flags in another Flags object are present in the current object. + [[nodiscard]] constexpr bool contains(const EnumFlags& other) const noexcept + { + return (mBits & other.mBits) == other.mBits; + } + + private: + // Set implementation, bits was zeroed before. +#ifndef GPUCA_GPUCODE + void setImpl(const std::string& s, int base = 2) + { + // Helper to check if character is valid for given base + auto isValidForBase = [](unsigned char c, int base) -> bool { + if (base == 2) { + return c == '0' || c == '1'; + } + if (base == 10) { + return std::isdigit(c); + } + if (base == 16) { + return std::isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + } + return false; + }; + + // hex + if (base == 16) { + std::string_view hex_str{s}; + // Strip optional 0x or 0X prefix + if (s.size() >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + hex_str.remove_prefix(2); + } + if (hex_str.empty()) { + throw std::invalid_argument("Empty hexadecimal string."); + } + if (!std::all_of(hex_str.begin(), hex_str.end(), [&](unsigned char c) { return isValidForBase(c, 16); })) { + throw std::invalid_argument("Invalid hexadecimal string."); + } + typename H::UMax v = std::stoul(std::string(hex_str), nullptr, 16); + if (v > H::MaxRep) { + throw std::out_of_range("Value exceeds enum range."); + } + mBits = static_cast(v); + return; + } + + // decimal and binary + if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); })) { + if (base == 2) { + // Binary: check only 0 and 1 + if (!std::all_of(s.begin(), s.end(), [&](unsigned char c) { return isValidForBase(c, 2); })) { + throw std::invalid_argument("Invalid binary string."); + } + } + typename H::UMax v = std::stoul(std::string(s), nullptr, base); + if (v > H::MaxRep) { + throw std::out_of_range("Value exceeds enum range."); + } + mBits = static_cast(v); + } + // enum name strings + else if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalnum(c) != 0 || c == '|' || c == ' ' || c == ':' || c == ',' || c == ';'; })) { + std::string cs{s}; + std::transform(cs.begin(), cs.end(), cs.begin(), [](unsigned char c) { return std::tolower(c); }); + + if (cs == H::All) { + mBits = All; + } else if (cs == H::None) { + mBits = None; + } else { + // Detect delimiter and ensure only one type is used + char token = ' '; + size_t pipePos = s.find('|'); + size_t commaPos = s.find(','); + size_t semiPos = s.find(';'); + + // Count how many different delimiters exist + int delimiterCount = (pipePos != std::string_view::npos ? 1 : 0) + + (commaPos != std::string_view::npos ? 1 : 0) + + (semiPos != std::string_view::npos ? 1 : 0); + + if (delimiterCount > 1) { + throw std::invalid_argument("Mixed delimiters not allowed!"); + } + + if (pipePos != std::string_view::npos) { + token = '|'; + } else if (commaPos != std::string_view::npos) { + token = ','; + } else if (semiPos != std::string_view::npos) { + token = ';'; + } + + for (const auto& tok : Str::tokenize(std::string(s), token)) { + if (auto e = H::fromString(tok)) { + mBits |= to_bit(*e); + } else { + throw std::invalid_argument(tok + " is not a valid enum value!"); + } + } + } + } else { + throw std::invalid_argument("Cannot parse string!"); + } + } +#endif +}; + +#ifndef GPUCA_GPUCODE +template +std::ostream& operator<<(std::ostream& os, const EnumFlags& f) +{ + os << f.pstring(true); + return os; +} +#endif + +} // namespace o2::utils + +#endif diff --git a/Common/Utils/include/CommonUtils/FIFO.h b/Common/Utils/include/CommonUtils/FIFO.h index 94942277daa0a..83afa0b2206c9 100644 --- a/Common/Utils/include/CommonUtils/FIFO.h +++ b/Common/Utils/include/CommonUtils/FIFO.h @@ -17,6 +17,7 @@ #include #include +#include namespace o2 { diff --git a/Common/Utils/include/CommonUtils/IRFrameSelector.h b/Common/Utils/include/CommonUtils/IRFrameSelector.h index 34b4017ec21f7..a4365030b6a12 100644 --- a/Common/Utils/include/CommonUtils/IRFrameSelector.h +++ b/Common/Utils/include/CommonUtils/IRFrameSelector.h @@ -29,23 +29,25 @@ class IRFrameSelector gsl::span getMatchingFrames(const o2::dataformats::IRFrame& fr); template - void setSelectedIRFrames(const SPAN& sp, size_t bwd = 0, size_t fwd = 0, bool removeOverlaps = true) + void setSelectedIRFrames(const SPAN& sp, size_t bwd = 0, size_t fwd = 0, long shift = 0, bool removeOverlaps = true) { mFrames = gsl::span(sp.data(), sp.size()); mIsSet = true; - applyMargins(bwd, fwd, removeOverlaps); + applyMargins(bwd, fwd, shift, removeOverlaps); mLastIRFrameChecked.getMin().clear(); // invalidate mLastBoundID = -1; } void clear(); size_t loadIRFrames(const std::string& fname); - void applyMargins(size_t bwd, size_t fwd, bool removeOverlaps = true); + void applyMargins(size_t bwd, size_t fwd, long shift, bool removeOverlaps = true); void print(bool lst = false) const; auto getIRFrames() const { return mFrames; } bool isSet() const { return mIsSet; } + void setOwnList(const std::vector& lst, bool toBeSorted); + private: gsl::span mFrames{}; // externally provided span of IRFrames, must be sorted in IRFrame.getMin() o2::dataformats::IRFrame mLastIRFrameChecked{}; // last frame which was checked diff --git a/Common/Utils/include/CommonUtils/NameConf.h b/Common/Utils/include/CommonUtils/NameConf.h index 8a09a903bf32f..8d4c0a2c1c4f8 100644 --- a/Common/Utils/include/CommonUtils/NameConf.h +++ b/Common/Utils/include/CommonUtils/NameConf.h @@ -100,9 +100,15 @@ class NameConf : public o2::conf::ConfigurableParamHelper // CTF Dictionary static std::string getCTFDictFileName(); + // O2 Raw TF Filename + static std::string getRawTFFileName(uint32_t run, uint32_t orb, uint32_t id, const std::string& host, const std::string_view prefix = "o2_rawtf_dump"); + // Default CCDB server static std::string getCCDBServer(); + // create name to dump config file + static std::string getConfigOutputFileName(const std::string& procName, const std::string& confName = "", bool json = true); + protected: // helper method to build filenames static std::string buildFileName(const std::string_view prefix, const std::string_view delimiter, const std::string_view defPrefix, const std::string_view defName, diff --git a/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h b/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h index 80545997af159..25205ce05ab40 100644 --- a/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h +++ b/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace o2 { @@ -39,14 +40,19 @@ class RootSerializableKeyValueStore /// Structure encapsulating the stored information: raw buffers and attached type information (combination of type_index_hash and TClass information) struct SerializedInfo { SerializedInfo() = default; - SerializedInfo(void* o, int N, char* b, TClass* cl, std::string const& s) : objptr(o), N(N), bufferptr(b), cl(cl), typeinfo_name(s) {} + SerializedInfo(int N, + std::unique_ptr buffer, TClass const* cl, std::string const& s) : N(N), cl(cl), typeinfo_name(s) + { + bufferptr = buffer.get(); + buffer.release(); + } SerializedInfo(SerializedInfo const& other) { // we do a deep copy N = other.N; bufferptr = new char[N]; + std::memcpy(bufferptr, other.bufferptr, sizeof(char) * N); - objptr = nullptr; cl = other.cl; typeinfo_name = other.typeinfo_name; } @@ -56,13 +62,18 @@ class RootSerializableKeyValueStore std::swap(*this, temp); return *this; } + ~SerializedInfo() + { + // we are the owner of this ... so delete it + delete bufferptr; + } - void* objptr = nullptr; //! pointer to existing object in memory + void* objptr = nullptr; //! pointer for "caching" Int_t N = 0; char* bufferptr = nullptr; //[N] pointer to serialized buffer // we use the TClass and/or the type_index_hash for type idendification - TClass* cl = nullptr; + TClass const* cl = nullptr; std::string typeinfo_name; // typeinfo name that can be used to store type if TClass not available (for PODs!) ClassDefNV(SerializedInfo, 1); }; @@ -141,8 +152,15 @@ class RootSerializableKeyValueStore mStore.clear(); } - /// print list of keys and type information - void print() const; + /// print list of keys, values (and optionally type information) + void print(bool includetypeinfo = false) const; + + /// resets store to the store of another object + void copyFrom(RootSerializableKeyValueStore const& other) + { + mStore.clear(); + mStore = other.mStore; + } private: std::map mStore; @@ -153,21 +171,21 @@ class RootSerializableKeyValueStore { // make sure we have a TClass for this // if there is a TClass, we'll use ROOT serialization to encode into the buffer - auto ptr = new T(value); + auto ptr = std::make_unique(T{value}); auto cl = TClass::GetClass(typeid(value)); if (!cl) { state = GetState::kNOTCLASS; return; } - char* bufferptr = nullptr; + std::unique_ptr bufferptr(nullptr); TBufferFile buff(TBuffer::kWrite); - buff.WriteObjectAny(ptr, cl); + buff.WriteObjectAny(ptr.get(), cl); int N = buff.Length(); - bufferptr = new char[N]; - memcpy(bufferptr, buff.Buffer(), sizeof(char) * N); + bufferptr.reset(new char[N]); + memcpy(bufferptr.get(), buff.Buffer(), sizeof(char) * N); auto name = std::type_index(typeid(value)).name(); - mStore.insert(std::pair(key, SerializedInfo((void*)ptr, N, (char*)bufferptr, cl, name))); + mStore.insert(std::pair(key, SerializedInfo(N, std::move(bufferptr), cl, name))); } // implementation for put for trivial types @@ -177,13 +195,13 @@ class RootSerializableKeyValueStore // we forbid pointers static_assert(!std::is_pointer::value); // serialization of trivial types is easy (not based on ROOT) - auto ptr = new T(value); + auto ptr = std::make_unique(T{value}); int N = sizeof(T); - auto bufferptr = new char[N]; - memcpy(bufferptr, (char*)ptr, sizeof(char) * N); + std::unique_ptr bufferptr(new char[N]); + memcpy(bufferptr.get(), (char*)ptr.get(), sizeof(char) * N); auto name = std::type_index(typeid(value)).name(); - mStore.insert(std::pair(key, SerializedInfo((void*)ptr, N, (char*)bufferptr, nullptr, name))); + mStore.insert(std::pair(key, SerializedInfo(N, std::move(bufferptr), nullptr, name))); } // generic implementation for get relying on TClass diff --git a/Common/Utils/include/CommonUtils/ShmManager.h b/Common/Utils/include/CommonUtils/ShmManager.h index 06dba283fec82..e7174b909b82c 100644 --- a/Common/Utils/include/CommonUtils/ShmManager.h +++ b/Common/Utils/include/CommonUtils/ShmManager.h @@ -23,10 +23,15 @@ #include #include +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) #include #include +#endif +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) && !defined(__APPLE__) +// this shared mem mode is meant for compiled stuff in o2-sim; not for ROOT sessions #define USESHM 1 +#endif namespace o2 { @@ -113,8 +118,10 @@ class ShmManager void* tryAttach(bool& success); size_t getPointerOffset(void* ptr) const { return (size_t)((char*)ptr - (char*)mBufferPtr); } +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) boost::interprocess::wmanaged_external_buffer* boostmanagedbuffer; boost::interprocess::allocator* boostallocator; +#endif }; } // namespace utils diff --git a/Common/Utils/include/CommonUtils/StringUtils.h b/Common/Utils/include/CommonUtils/StringUtils.h index 2402f3faa975c..cfe29e065a78e 100644 --- a/Common/Utils/include/CommonUtils/StringUtils.h +++ b/Common/Utils/include/CommonUtils/StringUtils.h @@ -20,8 +20,7 @@ #include #include #include -#include -#include +#include "GPUCommonRtypes.h" namespace o2 { @@ -41,6 +40,13 @@ struct Str { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); } + static inline void ltrim(std::string& s, const std::string& start) + { + if (beginsWith(s, start)) { + s.erase(0, start.size()); + } + } + /** Trim from end (in place) * * @param s @@ -50,6 +56,13 @@ struct Str { s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); } + static inline void rtrim(std::string& s, const std::string& ending) + { + if (endsWith(s, ending)) { + s.erase(s.size() - ending.size(), ending.size()); + } + } + /** * Trim from both ends (in place) * @param s @@ -72,6 +85,13 @@ struct Str { return ss; } + static inline std::string ltrim_copy(const std::string& s, const std::string& start) + { + std::string ss = s; + ltrim(ss, start); + return ss; + } + /** * Trim from end (copying) * @param s @@ -84,6 +104,13 @@ struct Str { return ss; } + static inline std::string rtrim_copy(const std::string& s, const std::string& ending) + { + std::string ss = s; + rtrim(ss, ending); + return ss; + } + /** * Trim from both sides (copying) * @param s @@ -109,6 +136,9 @@ struct Str { // return vector of tokens from the string with provided delimiter. If requested, trim the spaces from tokens static std::vector tokenize(const std::string& src, char delim, bool trimToken = true, bool skipEmpty = true); + // return vector of tokens from the string with provided delimiters. If requested, trim the spaces from tokens + static std::vector tokenize(const std::string& src, const std::string& delim, bool trimToken = true, bool skipEmpty = true); + // concatenate arbitrary number of strings template static std::string concat_string(Ts const&... ts) @@ -118,6 +148,9 @@ struct Str { return s.str(); } + // replace all occurencies of from by to, return count + static int replaceAll(std::string& s, const std::string& from, const std::string& to); + // generate random string of given length, suitable for file names static std::string getRandomString(int length); diff --git a/Common/Utils/include/CommonUtils/TreeStream.h b/Common/Utils/include/CommonUtils/TreeStream.h index 2c55f48c98d3a..d1d4527ffc99d 100644 --- a/Common/Utils/include/CommonUtils/TreeStream.h +++ b/Common/Utils/include/CommonUtils/TreeStream.h @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "GPUCommonDef.h" class TBranch; @@ -39,10 +41,79 @@ namespace utils /// /// See testTreeStream.cxx for functional example /// +namespace details +{ +template +struct IsTrivialRootType { + static constexpr bool value = + std::is_same_v || // Float_t + std::is_same_v || // Double_t + std::is_same_v || std::is_same_v || // ULong64_t or ULong_t + std::is_same_v || std::is_same_v || // Long64_t or Long_t + std::is_same_v || // UInt_t + std::is_same_v || // Int_t + std::is_same_v || // UShort_t + std::is_same_v || // Short_t + std::is_same_v || // UChar_t + std::is_same_v || std::is_same_v || std::is_same_v; // Char_t, int8_t, or Bool_t +}; + +template +struct IsTrivialRootType { + static constexpr bool value = IsTrivialRootType::value; +}; + +template +struct IsTrivialRootType { + static constexpr bool value = IsTrivialRootType::value; +}; + +template +concept TrivialRootType = IsTrivialRootType::value; + +template +concept ComplexRootType = !IsTrivialRootType::value; + +template +static constexpr char getRootTypeCode() +{ + if constexpr (std::is_array_v) { + return getRootTypeCode>(); + } else if constexpr (std::is_same_v) { + return 'F'; + } else if constexpr (std::is_same_v) { + return 'D'; + } else if constexpr (std::is_same_v || + std::is_same_v) { + return 'l'; + } else if constexpr (std::is_same_v || + std::is_same_v) { + return 'L'; + } else if constexpr (std::is_same_v) { + return 'i'; + } else if constexpr (std::is_same_v) { + return 'I'; + } else if constexpr (std::is_same_v) { + return 's'; + } else if constexpr (std::is_same_v) { + return 'S'; + } else if constexpr (std::is_same_v) { + return 'b'; + } else if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v) { + return 'B'; + } else { + static_assert(false, "unsupported type!"); + } +} +} // namespace details + class TreeStream { public: struct TreeDataElement { + int arsize = 1; ///< size of array char type = 0; ///< type of data element const TClass* cls = nullptr; ///< data type pointer const void* ptr = nullptr; ///< pointer to element @@ -63,81 +134,11 @@ class TreeStream const char* getName() const { return mTree.GetName(); } void setID(int id) { mID = id; } int getID() const { return mID; } - TreeStream& operator<<(const Bool_t& b) - { - CheckIn('B', &b); - return *this; - } - - TreeStream& operator<<(const Char_t& c) - { - CheckIn('B', &c); - return *this; - } - - TreeStream& operator<<(const UChar_t& c) - { - CheckIn('b', &c); - return *this; - } - - TreeStream& operator<<(const Short_t& h) - { - CheckIn('S', &h); - return *this; - } - - TreeStream& operator<<(const UShort_t& h) - { - CheckIn('s', &h); - return *this; - } - - TreeStream& operator<<(const Int_t& i) - { - CheckIn('I', &i); - return *this; - } - - TreeStream& operator<<(const UInt_t& i) - { - CheckIn('i', &i); - return *this; - } - - TreeStream& operator<<(const Long_t& l) - { - CheckIn('L', &l); - return *this; - } - - TreeStream& operator<<(const ULong_t& l) - { - CheckIn('l', &l); - return *this; - } - - TreeStream& operator<<(const Long64_t& l) - { - CheckIn('L', &l); - return *this; - } - - TreeStream& operator<<(const ULong64_t& l) - { - CheckIn('l', &l); - return *this; - } - - TreeStream& operator<<(const Float_t& f) - { - CheckIn('F', &f); - return *this; - } - TreeStream& operator<<(const Double_t& d) + template + TreeStream& operator<<(const T& t) { - CheckIn('D', &d); + CheckIn(details::getRootTypeCode(), &t); return *this; } @@ -150,7 +151,7 @@ class TreeStream return *this; } - template ::value, bool>::type* = nullptr> + template ::value, bool>::type* = nullptr> TreeStream& operator<<(const T& obj) { CheckIn(&obj); @@ -168,6 +169,7 @@ class TreeStream int mCurrentIndex = 0; ///< index of current element int mID = -1; ///< identifier of layout int mNextNameCounter = 0; ///< next name counter + int mNextArraySize = 0; ///< next array size int mStatus = 0; ///< status of the layout TString mNextName; ///< name for next entry @@ -184,8 +186,7 @@ Int_t TreeStream::CheckIn(const T* obj) } if (mCurrentIndex >= static_cast(mElements.size())) { - mElements.emplace_back(); - auto& element = mElements.back(); + auto& element = mElements.emplace_back(); element.cls = pClass; TString name = mNextName; if (name.Length()) { @@ -197,6 +198,8 @@ Int_t TreeStream::CheckIn(const T* obj) } element.name = name.Data(); element.ptr = obj; + element.arsize = mNextArraySize; + mNextArraySize = 1; // reset } else { auto& element = mElements[mCurrentIndex]; if (!element.cls) { diff --git a/Common/Utils/include/CommonUtils/TreeStreamRedirector.h b/Common/Utils/include/CommonUtils/TreeStreamRedirector.h index 8199009df400d..80858fecea87b 100644 --- a/Common/Utils/include/CommonUtils/TreeStreamRedirector.h +++ b/Common/Utils/include/CommonUtils/TreeStreamRedirector.h @@ -41,7 +41,7 @@ namespace utils class TreeStreamRedirector { public: - TreeStreamRedirector(const char* fname = "", const char* option = "update"); + TreeStreamRedirector(const char* fname = "", const char* option = "recreate"); virtual ~TreeStreamRedirector(); void Close(); TFile* GetFile() { return mDirectory->GetFile(); } diff --git a/Common/Utils/src/BoostHistogramUtils.cxx b/Common/Utils/src/BoostHistogramUtils.cxx index c5c9d97742976..279a709b97586 100644 --- a/Common/Utils/src/BoostHistogramUtils.cxx +++ b/Common/Utils/src/BoostHistogramUtils.cxx @@ -40,48 +40,6 @@ std::string createErrorMessageFitGaus(o2::utils::FitGausError_t errorcode) return "Gaus fit failed! Unknown error code"; } -boostHisto1d_VarAxis boosthistoFromRoot_1D(TH1D* inHist1D) -{ - // first setup the proper boost histogram - int nBins = inHist1D->GetNbinsX(); - std::vector binEdges; - for (int i = 0; i < nBins + 1; i++) { - binEdges.push_back(inHist1D->GetBinLowEdge(i + 1)); - } - boostHisto1d_VarAxis mHisto = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdges)); - - // trasfer the acutal values - for (Int_t x = 1; x < nBins + 1; x++) { - mHisto.at(x - 1) = inHist1D->GetBinContent(x); - } - return mHisto; -} - -boostHisto2d_VarAxis boostHistoFromRoot_2D(TH2D* inHist2D) -{ - // Get Xaxis binning - const int nBinsX = inHist2D->GetNbinsX(); - std::vector binEdgesX; - for (int i = 0; i < nBinsX + 1; i++) { - binEdgesX.push_back(inHist2D->GetXaxis()->GetBinLowEdge(i + 1)); - } - // Get Yaxis binning - const int nBinsY = inHist2D->GetNbinsY(); - std::vector binEdgesY; - for (int i = 0; i < nBinsY + 1; i++) { - binEdgesY.push_back(inHist2D->GetYaxis()->GetBinLowEdge(i + 1)); - } - - boostHisto2d_VarAxis mHisto = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdgesX), boost::histogram::axis::variable<>(binEdgesY)); - - // trasfer the acutal values - for (Int_t x = 1; x < nBinsX + 1; x++) { - for (Int_t y = 1; y < nBinsY + 1; y++) { - mHisto.at(x - 1, y - 1) = inHist2D->GetBinContent(x, y); - } - } - return mHisto; -} /// \brief Printing an error message when then fit returns an invalid result /// \param errorcode Error of the type FitGausError_t, thrown when fit result is invalid. std::string createErrorMessage(o2::utils::FitGausError_t errorcode) diff --git a/Common/Utils/src/CommonUtilsTestLinkDef.h b/Common/Utils/src/CommonUtilsTestLinkDef.h new file mode 100644 index 0000000000000..9ee67f62fd7d0 --- /dev/null +++ b/Common/Utils/src/CommonUtilsTestLinkDef.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::conf::test::TestParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::test::TestParam> + ; + +#endif diff --git a/Common/Utils/src/ConfigurableParam.cxx b/Common/Utils/src/ConfigurableParam.cxx index c36807aa6d8f7..4de478a6d0e9f 100644 --- a/Common/Utils/src/ConfigurableParam.cxx +++ b/Common/Utils/src/ConfigurableParam.cxx @@ -9,11 +9,15 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -//first version 8/2018, Sandro Wenzel +// first version 8/2018, Sandro Wenzel #include "CommonUtils/ConfigurableParam.h" +#include +#include "CommonUtils/ConfigurableParamHelper.h" #include "CommonUtils/StringUtils.h" #include "CommonUtils/KeyValParam.h" +#include "CommonUtils/ConfigurableParamReaders.h" +#define BOOST_BIND_GLOBAL_PLACEHOLDERS #include #include #include @@ -22,14 +26,21 @@ #include #include #include +#include +#include +#include +#include #include +#include #ifdef NDEBUG #undef NDEBUG #endif #include #include +#include #include #include +#include #include #include "TDataMember.h" #include "TDataType.h" @@ -37,6 +48,13 @@ #include "TEnum.h" #include "TEnumConstant.h" #include +#include +#include +#include +#include +#include +#include +#include namespace o2 { @@ -46,13 +64,17 @@ std::vector* ConfigurableParam::sRegisteredParamClasses = nu boost::property_tree::ptree* ConfigurableParam::sPtree = nullptr; std::map>* ConfigurableParam::sKeyToStorageMap = nullptr; std::map* ConfigurableParam::sValueProvenanceMap = nullptr; -std::string ConfigurableParam::sInputDir = ""; std::string ConfigurableParam::sOutputDir = ""; EnumRegistry* ConfigurableParam::sEnumRegistry = nullptr; bool ConfigurableParam::sIsFullyInitialized = false; bool ConfigurableParam::sRegisterMode = true; +namespace +{ +std::map sKeyToContainerTypeMap; +} // namespace + // ------------------------------------------------------------------ std::ostream& operator<<(std::ostream& out, ConfigurableParam const& param) @@ -76,6 +98,399 @@ bool keyInTree(boost::property_tree::ptree* pt, const std::string& key) return reply; } +// Convert a type info to the appropriate literal suffix +std::string getLiteralSuffixFromType(const std::type_info& type) +{ + if (type == typeid(float)) { + return "f"; + } + if (type == typeid(long double)) { + return "l"; + } + if (type == typeid(unsigned int)) { + return "u"; + } + if (type == typeid(unsigned long)) { + return "ul"; + } + if (type == typeid(long long)) { + return "ll"; + } + if (type == typeid(unsigned long long)) { + return "ull"; + } + return ""; +} + +namespace +{ + +struct ContainerHandler { + std::function parseAssign; + std::function serialize; + std::function assign; + std::function equal; +}; + +struct ContainerHandlerRegistry { + std::map byName; + std::map byType; +}; + +template +struct IsUnorderedSet : std::false_type { +}; + +template +struct IsUnorderedSet> : std::true_type { +}; + +template +struct IsUnorderedMap : std::false_type { +}; + +template +struct IsUnorderedMap> : std::true_type { +}; + +std::string normalizeContainerTypeName(std::string typeName) +{ + typeName = ContainerParser::trim(typeName); + for (size_t pos = typeName.find("std::"); pos != std::string::npos; pos = typeName.find("std::", pos)) { + typeName.erase(pos, 5); + } + + std::string out; + bool pendingSpace = false; + for (char c : typeName) { + if (std::isspace(static_cast(c))) { + if (!out.empty() && out.back() != '<' && out.back() != ',') { + pendingSpace = true; + } + continue; + } + if ((c == ',' || c == '>') && !out.empty() && out.back() == ' ') { + out.pop_back(); + } + if (pendingSpace && c != ',' && c != '>' && !out.empty() && out.back() != '<' && out.back() != ',') { + out += ' '; + } + out += c; + pendingSpace = false; + } + return out; +} + +template +struct TypeName; + +#define REGISTER_SCALAR_NAME(TYPE, NAME) \ + template <> \ + struct TypeName { \ + static constexpr const char* value = NAME; \ + } + +REGISTER_SCALAR_NAME(bool, "bool"); +REGISTER_SCALAR_NAME(char, "char"); +REGISTER_SCALAR_NAME(signed char, "signed char"); +REGISTER_SCALAR_NAME(unsigned char, "unsigned char"); +REGISTER_SCALAR_NAME(short, "short"); +REGISTER_SCALAR_NAME(unsigned short, "unsigned short"); +REGISTER_SCALAR_NAME(int, "int"); +REGISTER_SCALAR_NAME(unsigned int, "unsigned int"); +REGISTER_SCALAR_NAME(long, "long"); +REGISTER_SCALAR_NAME(unsigned long, "unsigned long"); +REGISTER_SCALAR_NAME(long long, "long long"); +REGISTER_SCALAR_NAME(unsigned long long, "unsigned long long"); +REGISTER_SCALAR_NAME(float, "float"); +REGISTER_SCALAR_NAME(double, "double"); +REGISTER_SCALAR_NAME(std::string, "string"); + +#undef REGISTER_SCALAR_NAME + +template +std::string scalarAsString(const T& value) +{ + if constexpr (std::is_same_v) { + return value ? "1" : "0"; + } else if constexpr (std::is_same_v || std::is_same_v) { + return std::to_string(static_cast(value)); + } else if constexpr (std::is_same_v) { + return std::to_string(static_cast(value)); + } else if constexpr (std::is_same_v) { + return value; + } else if constexpr (std::is_floating_point_v) { + std::ostringstream out; + out << std::setprecision(std::numeric_limits::max_digits10) << value; + return out.str(); + } else { + return std::to_string(value); + } +} + +template +std::string sequenceAsString(const ContainerT& container) +{ + using ValueType = typename ContainerT::value_type; + std::ostringstream out; + out << '['; + bool first = true; + std::vector unorderedValues; + if constexpr (IsUnorderedSet::value) { + for (const auto& value : container) { + unorderedValues.push_back(scalarAsString(static_cast(value))); + } + std::sort(unorderedValues.begin(), unorderedValues.end()); + } + const auto emitValue = [&out, &first](const std::string& value) { + if (!first) { + out << ','; + } + out << value; + first = false; + }; + if constexpr (IsUnorderedSet::value) { + for (const auto& value : unorderedValues) { + emitValue(value); + } + } else { + for (const auto& value : container) { + emitValue(scalarAsString(static_cast(value))); + } + } + out << ']'; + return out.str(); +} + +template +std::string mapAsString(const MapT& container) +{ + std::ostringstream out; + out << '{'; + bool first = true; + std::vector> unorderedValues; + if constexpr (IsUnorderedMap::value) { + for (const auto& [key, value] : container) { + unorderedValues.emplace_back(scalarAsString(key), scalarAsString(value)); + } + std::sort(unorderedValues.begin(), unorderedValues.end()); + } + const auto emitValue = [&out, &first](const std::string& key, const std::string& value) { + if (!first) { + out << ','; + } + out << key << ':' << value; + first = false; + }; + if constexpr (IsUnorderedMap::value) { + for (const auto& [key, value] : unorderedValues) { + emitValue(key, value); + } + } else { + for (const auto& [key, value] : container) { + emitValue(scalarAsString(key), scalarAsString(value)); + } + } + out << '}'; + return out.str(); +} + +template +ContainerHandler makeSequenceHandler() +{ + return { + [](void* target, const std::string& value) { + *static_cast(target) = ContainerParser::parse(value); + }, + [](const void* source) { + return sequenceAsString(*static_cast(source)); + }, + [](void* target, const void* source) { + *static_cast(target) = *static_cast(source); + }, + [](const void* lhs, const void* rhs) { + return *static_cast(lhs) == *static_cast(rhs); + }}; +} + +template +ContainerHandler makeMapHandler() +{ + return { + [](void* target, const std::string& value) { + *static_cast(target) = ContainerParser::parse(value); + }, + [](const void* source) { + return mapAsString(*static_cast(source)); + }, + [](void* target, const void* source) { + *static_cast(target) = *static_cast(source); + }, + [](const void* lhs, const void* rhs) { + return *static_cast(lhs) == *static_cast(rhs); + }}; +} + +template +void addHandler(ContainerHandlerRegistry& registry, const std::string& name, ContainerHandler handler) +{ + registry.byName.emplace(normalizeContainerTypeName(name), handler); + registry.byType.emplace(std::type_index(typeid(ContainerT)), std::move(handler)); +} + +template +void addSequenceHandlers(ContainerHandlerRegistry& registry) +{ + const std::string tname = TypeName::value; + addHandler>(registry, "vector<" + tname + ">", makeSequenceHandler>()); + addHandler>(registry, "list<" + tname + ">", makeSequenceHandler>()); + addHandler>(registry, "deque<" + tname + ">", makeSequenceHandler>()); + addHandler>(registry, "set<" + tname + ">", makeSequenceHandler>()); + addHandler>(registry, "unordered_set<" + tname + ">", makeSequenceHandler>()); +} + +template +void addMapHandlers(ContainerHandlerRegistry& registry) +{ + const std::string kname = TypeName::value; + const std::string vname = TypeName::value; + addHandler>(registry, "map<" + kname + "," + vname + ">", makeMapHandler>()); + addHandler>(registry, "unordered_map<" + kname + "," + vname + ">", makeMapHandler>()); +} + +template +void addMapHandlersForKey(ContainerHandlerRegistry& registry) +{ + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); + addMapHandlers(registry); +} + +const ContainerHandlerRegistry& containerHandlers() +{ + static const ContainerHandlerRegistry handlers = [] { + ContainerHandlerRegistry result; + + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + addSequenceHandlers(result); + + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + addMapHandlersForKey(result); + + return result; + }(); + return handlers; +} + +const ContainerHandler* getContainerHandler(const std::string& typeName) +{ + const auto normalized = normalizeContainerTypeName(typeName); + const auto& handlers = containerHandlers().byName; + auto iter = handlers.find(normalized); + return iter == handlers.end() ? nullptr : &iter->second; +} + +const ContainerHandler* getContainerHandler(const std::type_info& type) +{ + const auto& handlers = containerHandlers().byType; + auto iter = handlers.find(std::type_index(type)); + return iter == handlers.end() ? nullptr : &iter->second; +} + +std::pair splitConfigurableParamKey(std::string_view key) +{ + const auto separator = key.find('.'); + if (separator == std::string_view::npos) { + return {key, {}}; + } + return {key.substr(0, separator), key.substr(separator + 1)}; +} + +std::string findClosestConfigurableParamKey(const std::string& requestedKey, + const std::map>& storageMap) +{ + if (storageMap.empty()) { + return {}; + } + + const auto [requestedMainKey, requestedSubKey] = splitConfigurableParamKey(requestedKey); + bool mainKeyExists = false; + for (const auto& entry : storageMap) { + const auto mainKey = splitConfigurableParamKey(entry.first).first; + if (mainKey == requestedMainKey) { + mainKeyExists = true; + break; + } + } + + std::string closest; + std::size_t closestDistance = std::numeric_limits::max(); + for (const auto& entry : storageMap) { + const auto& key = entry.first; + const auto [mainKey, subKey] = splitConfigurableParamKey(key); + if (mainKeyExists && mainKey != requestedMainKey) { + continue; + } + const auto distance = mainKeyExists ? damerauLevenshteinDistance(requestedSubKey, subKey) : damerauLevenshteinDistance(requestedKey, key); + if (distance < closestDistance || (distance == closestDistance && (closest.empty() || key < closest))) { + closest = key; + closestDistance = distance; + } + } + return closest; +} + +std::string formatUnknownConfigurableParamKeyMessage(const std::string& prefix, const std::string& key, + const std::map>& storageMap) +{ + std::string message = prefix + key; + auto closest = findClosestConfigurableParamKey(key, storageMap); + if (!closest.empty()) { + message += ". Did you mean '" + closest + "'?"; + } + return message; +} + +} // namespace + // ------------------------------------------------------------------ void EnumRegistry::add(const std::string& key, const TDataMember* dm) @@ -121,7 +536,6 @@ std::string EnumRegistry::toString() const out.append("\n"); } - LOG(info) << out; return out; } @@ -168,6 +582,62 @@ int EnumLegalValues::getIntValue(const std::string& value) const // ----------------------------------------------------------------- +bool ConfigurableParam::isRegisteredContainerType(const std::string& typeName) +{ + return getContainerHandler(typeName) != nullptr; +} + +void ConfigurableParam::registerContainerType(const std::string& key, const std::string& typeName) +{ + sKeyToContainerTypeMap[key] = typeName; +} + +std::string ConfigurableParam::getRegisteredContainerType(const std::string& key) +{ + auto iter = sKeyToContainerTypeMap.find(key); + return iter == sKeyToContainerTypeMap.end() ? std::string{} : iter->second; +} + +bool ConfigurableParam::assignRegisteredContainer(const std::string& typeName, void* target, const void* source) +{ + if (const auto* handler = getContainerHandler(typeName)) { + handler->assign(target, source); + return true; + } + return false; +} + +bool ConfigurableParam::areRegisteredContainersEqual(const std::string& typeName, const void* lhs, const void* rhs) +{ + if (const auto* handler = getContainerHandler(typeName)) { + return handler->equal(lhs, rhs); + } + return false; +} + +std::string ConfigurableParam::registeredContainerAsString(const std::string& typeName, const void* source) +{ + if (const auto* handler = getContainerHandler(typeName)) { + return handler->serialize(source); + } + return {}; +} + +// ----------------------------------------------------------------- + +void ConfigurableParam::write(std::string const& filename, std::string const& keyOnly) +{ + if (o2::utils::Str::endsWith(filename, ".ini")) { + writeINI(filename, keyOnly); + } else if (o2::utils::Str::endsWith(filename, ".json")) { + writeJSON(filename, keyOnly); + } else { + throw std::invalid_argument(fmt::format("ConfigurabeParam output file name {} extension is neither .json nor .ini", filename)); + } +} + +// ----------------------------------------------------------------- + void ConfigurableParam::writeINI(std::string const& filename, std::string const& keyOnly) { if (sOutputDir == "/dev/null") { @@ -179,7 +649,10 @@ void ConfigurableParam::writeINI(std::string const& filename, std::string const& if (!keyOnly.empty()) { // write ini for selected key only try { boost::property_tree::ptree kTree; - kTree.add_child(keyOnly, sPtree->get_child(keyOnly)); + auto keys = o2::utils::Str::tokenize(keyOnly, " ,;", true, true); + for (const auto& k : keys) { + kTree.add_child(k, sPtree->get_child(k)); + } boost::property_tree::write_ini(outfilename, kTree); } catch (const boost::property_tree::ptree_bad_path& err) { LOG(fatal) << "non-existing key " << keyOnly << " provided to writeINI"; @@ -193,60 +666,65 @@ void ConfigurableParam::writeINI(std::string const& filename, std::string const& bool ConfigurableParam::configFileExists(std::string const& filepath) { - return std::filesystem::exists(o2::utils::Str::concat_string(sInputDir, filepath)); -} - -// ------------------------------------------------------------------ - -boost::property_tree::ptree ConfigurableParam::readConfigFile(std::string const& filepath) -{ - auto inpfilename = o2::utils::Str::concat_string(sInputDir, filepath); - if (!std::filesystem::exists(inpfilename)) { - LOG(fatal) << inpfilename << " : config file does not exist!"; - } - - boost::property_tree::ptree pt; - - if (boost::iends_with(inpfilename, ".ini")) { - pt = readINI(inpfilename); - } else if (boost::iends_with(inpfilename, ".json")) { - pt = readJSON(inpfilename); - } else { - LOG(fatal) << "Configuration file must have either .ini or .json extension"; - } - - return pt; + return std::filesystem::exists(o2::utils::Str::concat_string(ConfigurableParamReaders::getInputDir(), filepath)); } // ------------------------------------------------------------------ -boost::property_tree::ptree ConfigurableParam::readINI(std::string const& filepath) +void ConfigurableParam::setValue(std::string const& key, std::string const& valuestring) { - boost::property_tree::ptree pt; - try { - boost::property_tree::read_ini(filepath, pt); - } catch (const boost::property_tree::ptree_error& e) { - LOG(fatal) << "Failed to read INI config file " << filepath << " (" << e.what() << ")"; - } catch (...) { - LOG(fatal) << "Unknown error when reading INI config file "; + if (!sIsFullyInitialized) { + initialize(); } - - return pt; -} - -// ------------------------------------------------------------------ - -boost::property_tree::ptree ConfigurableParam::readJSON(std::string const& filepath) -{ - boost::property_tree::ptree pt; - + assert(sPtree); + auto setValueImpl = [&](std::string const& value) { + sPtree->put(key, value); + auto changed = updateThroughStorageMapWithConversion(key, value); + if (changed != EParamUpdateStatus::Failed) { + sValueProvenanceMap->find(key)->second = kRT; // set to runtime + } + }; try { - boost::property_tree::read_json(filepath, pt); - } catch (const boost::property_tree::ptree_error& e) { - LOG(fatal) << "Failed to read JSON config file " << filepath << " (" << e.what() << ")"; + if (sPtree->get_optional(key).is_initialized()) { + auto iter = sKeyToStorageMap->find(key); + if (iter != sKeyToStorageMap->end()) { + if (!getRegisteredContainerType(key).empty() || getContainerHandler(iter->second.first)) { + setContainerValue(key, valuestring); + return; + } + } + try { + // try first setting value without stripping a literal suffix + setValueImpl(valuestring); + } catch (...) { + // try second stripping the expected literal suffix value for fundamental types + auto iter = sKeyToStorageMap->find(key); + if (iter == sKeyToStorageMap->end()) { + std::cerr << "Error in setValue (string) key is not known\n"; + return; + } + const auto expectedSuffix = getLiteralSuffixFromType(iter->second.first); + if (!expectedSuffix.empty()) { + auto valuestringLower = valuestring; + std::transform(valuestring.cbegin(), valuestring.cend(), valuestringLower.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); + if (valuestringLower.ends_with(expectedSuffix)) { + std::string strippedValue = valuestringLower.substr(0, valuestringLower.length() - expectedSuffix.length()); + setValueImpl(strippedValue); + } else { + // check if it has a different suffix and throw + for (const auto& suffix : {"f", "l", "u", "ul", "ll", "ull"}) { + if (valuestringLower.ends_with(suffix) && suffix != expectedSuffix) { + throw std::invalid_argument("Wrong type suffix: expected " + expectedSuffix + " but got " + suffix); + } + } + throw; // just rethrow the original exception + } + } + } + } + } catch (std::exception const& e) { + std::cerr << "Error in setValue (string) " << e.what() << "\n"; } - - return pt; } // ------------------------------------------------------------------ @@ -257,12 +735,15 @@ void ConfigurableParam::writeJSON(std::string const& filename, std::string const LOG(info) << "ignoring writing of json file " << filename; return; } - initPropertyTree(); // update the boost tree before writing + initPropertyTree(); // update the boost tree before writing auto outfilename = o2::utils::Str::concat_string(sOutputDir, filename); if (!keyOnly.empty()) { // write ini for selected key only try { boost::property_tree::ptree kTree; - kTree.add_child(keyOnly, sPtree->get_child(keyOnly)); + auto keys = o2::utils::Str::tokenize(keyOnly, " ,;", true, true); + for (const auto& k : keys) { + kTree.add_child(k, sPtree->get_child(k)); + } boost::property_tree::write_json(outfilename, kTree); } catch (const boost::property_tree::ptree_bad_path& err) { LOG(fatal) << "non-existing key " << keyOnly << " provided to writeJSON"; @@ -274,6 +755,29 @@ void ConfigurableParam::writeJSON(std::string const& filename, std::string const // ------------------------------------------------------------------ +std::string ConfigurableParam::asJSON(std::string const& keyOnly) +{ + initPropertyTree(); // update the boost tree before writing + std::ostringstream os; + if (!keyOnly.empty()) { // write ini for selected key only + try { + boost::property_tree::ptree kTree; + auto keys = o2::utils::Str::tokenize(keyOnly, " ,;", true, true); + for (const auto& k : keys) { + kTree.add_child(k, sPtree->get_child(k)); + } + boost::property_tree::write_json(os, kTree); + } catch (const boost::property_tree::ptree_bad_path& err) { + LOG(fatal) << "non-existing key " << keyOnly << " provided to writeJSON"; + } + } else { + boost::property_tree::write_json(os, *sPtree); + } + return os.str(); +} + +// ------------------------------------------------------------------ + void ConfigurableParam::initPropertyTree() { sPtree->clear(); @@ -390,26 +894,10 @@ void ConfigurableParam::printAllRegisteredParamNames() // ------------------------------------------------------------------ -// Update the storage map of params from the given configuration file. -// It can be in JSON or INI format. -// If nonempty comma-separated paramsList is provided, only those params will -// be updated, absence of data for any of requested params will lead to fatal -// If unchangedOnly is true, then only those parameters whose provenance is kCODE will be updated -// (to allow prefernce of run-time settings) -void ConfigurableParam::updateFromFile(std::string const& configFile, std::string const& paramsList, bool unchangedOnly) +namespace +{ +void updateFromPropertyTree(boost::property_tree::ptree const& pt, std::string const& source, std::string const& paramsList, bool unchangedOnly) { - if (!sIsFullyInitialized) { - initialize(); - } - - auto cfgfile = o2::utils::Str::trim_copy(configFile); - - if (cfgfile.length() == 0) { - return; - } - - boost::property_tree::ptree pt = readConfigFile(cfgfile); - std::vector> keyValPairs; auto request = o2::utils::Str::tokenize(paramsList, ',', true); std::unordered_map requestMap; @@ -433,7 +921,7 @@ void ConfigurableParam::updateFromFile(std::string const& configFile, std::strin auto name = subKey.first; auto value = subKey.second.get_value(); std::string key = mainKey + "." + name; - if (!unchangedOnly || getProvenance(key) == kCODE) { + if (!unchangedOnly || ConfigurableParam::getProvenance(key) == ConfigurableParam::kCODE) { std::pair pair = std::make_pair(key, o2::utils::Str::trim_copy(value)); keyValPairs.push_back(pair); } @@ -448,16 +936,62 @@ void ConfigurableParam::updateFromFile(std::string const& configFile, std::strin // make sure all requested params were retrieved for (const auto& req : requestMap) { if (req.second == 0) { - throw std::runtime_error(fmt::format("Param {:s} was not found in {:s}", req.first, configFile)); + throw std::runtime_error(fmt::format("Param {:s} was not found in {:s}", req.first, source)); } } try { - setValues(keyValPairs); + ConfigurableParam::setValues(keyValPairs); } catch (std::exception const& error) { LOG(error) << "Error while setting values " << error.what(); } } +} // namespace + +// Update the storage map of params from the given configuration file. +// It can be in JSON or INI format. +// If nonempty comma-separated paramsList is provided, only those params will +// be updated, absence of data for any of requested params will lead to fatal +// If unchangedOnly is true, then only those parameters whose provenance is kCODE will be updated +// (to allow preference of run-time settings) +void ConfigurableParam::updateFromFile(std::string const& configFile, std::string const& paramsList, bool unchangedOnly) +{ + if (!sIsFullyInitialized) { + initialize(); + } + + auto cfgfile = o2::utils::Str::trim_copy(configFile); + + if (cfgfile.length() == 0) { + return; + } + + updateFromPropertyTree(ConfigurableParamReaders::readConfigFile(cfgfile), configFile, paramsList, unchangedOnly); +} + +// ------------------------------------------------------------------ + +void ConfigurableParam::updateFromJSONString(std::string const& configJSON, std::string const& paramsList, bool unchangedOnly) +{ + if (!sIsFullyInitialized) { + initialize(); + } + + auto json = o2::utils::Str::trim_copy(configJSON); + if (json.length() == 0) { + return; + } + + boost::property_tree::ptree pt; + std::istringstream input(json); + try { + boost::property_tree::read_json(input, pt); + } catch (const boost::property_tree::ptree_error& e) { + LOG(fatal) << "Failed to read JSON config string (" << e.what() << ")"; + } + + updateFromPropertyTree(pt, "provided JSON string", paramsList, unchangedOnly); +} // ------------------------------------------------------------------ // ------------------------------------------------------------------ @@ -479,14 +1013,12 @@ void ConfigurableParam::updateFromString(std::string const& configString) std::vector> pairs; for (auto& token : tokens) { - auto keyval = o2::utils::Str::tokenize(token, '='); - if (keyval.size() != 2) { + auto s = token.find('='); + if (s == 0 || s == std::string::npos || s == token.size() - 1) { LOG(fatal) << "Illegal command-line key/value string: " << token; continue; } - - std::pair pair = std::make_pair(keyval[0], o2::utils::Str::trim_copy(keyval[1])); - pairs.push_back(pair); + pairs.emplace_back(token.substr(0, s), token.substr(s + 1, token.size())); } return pairs; @@ -521,7 +1053,7 @@ void ConfigurableParam::updateFromString(std::string const& configString) const auto& kv = o2::conf::KeyValParam::Instance(); if (getProvenance("keyval.input_dir") != kCODE) { - sInputDir = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(kv.input_dir)); + ConfigurableParamReaders::setInputDir(o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(kv.input_dir))); } if (getProvenance("keyval.output_dir") != kCODE) { if (kv.output_dir == "/dev/null") { @@ -540,6 +1072,8 @@ void ConfigurableParam::setValues(std::vector 0 && (el.at(0) == '[') && (el.at(el.size() - 1) == ']'); }; + bool nonFatal = getenv("ALICEO2_CONFIGURABLEPARAM_WRONGKEYISNONFATAL") != nullptr; + // Take a vector of param key/value pairs // and update the storage map for each of them by calling setValue. // 1. For string/scalar types this is simple. @@ -552,7 +1086,19 @@ void ConfigurableParam::setValues(std::vectorfind(key); + if (iter != sKeyToStorageMap->end()) { + if (!getRegisteredContainerType(key).empty() || getContainerHandler(iter->second.first)) { + setContainerValue(key, value); + continue; + } } if (sEnumRegistry->contains(key)) { @@ -569,9 +1115,7 @@ void ConfigurableParam::setValues(std::vectorfind(key); + if (iter == sKeyToStorageMap->end()) { + LOG(error) << "Container parameter " << key << " not found"; + return; + } + void* targetAddress = iter->second.second; + const auto typeName = getRegisteredContainerType(key); + const auto* handler = typeName.empty() ? getContainerHandler(iter->second.first) : getContainerHandler(typeName); + if (!handler) { + LOG(error) << "Unsupported container configuration: " << (typeName.empty() ? iter->second.first.name() : typeName); + return; + } + try { + handler->parseAssign(targetAddress, value); + sPtree->put(key, handler->serialize(targetAddress)); + if (auto prov = sValueProvenanceMap->find(key); prov != sValueProvenanceMap->end()) { + prov->second = kRT; + } + } catch (const std::exception& e) { + LOG(error) << "Failed to parse container " << key << ": " << e.what(); + } +} + void ConfigurableParam::setEnumValue(const std::string& key, const std::string& value) { int val = (*sEnumRegistry)[key]->getIntValue(value); diff --git a/Common/Utils/src/ConfigurableParamHelper.cxx b/Common/Utils/src/ConfigurableParamHelper.cxx index 0fb213b722e26..e2637ccaca01e 100644 --- a/Common/Utils/src/ConfigurableParamHelper.cxx +++ b/Common/Utils/src/ConfigurableParamHelper.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -//first version 8/2018, Sandro Wenzel +// first version 8/2018, Sandro Wenzel #include "CommonUtils/ConfigurableParamHelper.h" #include "CommonUtils/ConfigurableParam.h" @@ -26,6 +26,8 @@ #include #include #include +#include +#include #ifdef NDEBUG #undef NDEBUG #endif @@ -35,16 +37,25 @@ using namespace o2::conf; // ---------------------------------------------------------------------- -std::string ParamDataMember::toString(std::string const& prefix, bool showProv) const +std::string ParamDataMember::toString(std::string const& prefix, bool showProv, size_t padding) const { - std::string nil = ""; - + const std::string label = prefix + "." + name + " : " + value; std::ostringstream out; - out << prefix << "." << name << " : " << value; + out << label; if (showProv) { - std::string prov = (provenance.compare("") == 0 ? nil : provenance); - out << "\t\t[ " + prov + " ]"; + std::string prov = (provenance.compare("") == 0 ? "" : provenance); + if (padding) { + size_t len = label.size() - prefix.size() - 5; // 4 four the extra chars + 1 for the maxpad + if (len < padding) { + out << std::string(padding - len, ' '); + } else { + out << ' '; + } + out << "[ " + prov + " ]"; + } else { + out << "\t\t[ " + prov + " ]"; + } } return out.str(); } @@ -62,6 +73,17 @@ bool isString(TDataMember const& dm) return strcmp(dm.GetTrueTypeName(), "string") == 0; } +std::string getContainerTypeName(TDataMember const& dm) +{ + if (auto* cl = dm.GetClass(); cl && ConfigurableParam::isRegisteredContainerType(cl->GetName())) { + return cl->GetName(); + } + if (ConfigurableParam::isRegisteredContainerType(dm.GetTrueTypeName())) { + return dm.GetTrueTypeName(); + } + return dm.GetFullTypeName(); +} + // ---------------------------------------------------------------------- // a generic looper of data members of a TClass; calling a callback @@ -86,6 +108,15 @@ void loopOverMembers(TClass* cl, void* obj, LOG(warning) << "Pointer types not supported in ConfigurableParams: " << dm->GetFullTypeName() << " " << dm->GetName(); continue; } + if (isContainer(*dm)) { + TClass* c = dm->GetClass(); + if (!c || !c->HasDictionary()) { + LOG(warning) << "Skipping container parameter '" << dm->GetName() << "' of type " << dm->GetTrueTypeName() << " - ROOT dictionary not found."; + continue; + } + callback(dm, 0, 1); + continue; + } if (!dm->IsBasic() && !isValidComplex()) { LOG(warning) << "Generic complex types not supported in ConfigurableParams: " << dm->GetFullTypeName() << " " << dm->GetName(); continue; @@ -128,7 +159,7 @@ size_t getSizeOfUnderlyingType(const TDataMember& dm) } else { // for now only catch std::string as other supported type auto tname = dm.GetFullTypeName(); - if (strcmp(tname, "string") == 0 || strcmp(tname, "std::string")) { + if (strcmp(tname, "string") == 0 || strcmp(tname, "std::string") == 0) { return sizeof(std::string); } LOG(error) << "ENCOUNTERED AN UNSUPPORTED TYPE " << tname << "IN A CONFIGURABLE PARAMETER"; @@ -179,22 +210,25 @@ std::string asString(TDataMember const& dm, char* pointer) else if (isString(dm)) { return ((std::string*)pointer)->c_str(); } + if (isContainer(dm)) { + return ConfigurableParam::registeredContainerAsString(getContainerTypeName(dm), pointer); + } // potentially other cases to be added here - LOG(error) << "COULD NOT REPRESENT AS STRING"; - return nullptr; + LOG(error) << "COULD NOT REPRESENT AS STRING: " << dm.GetFullTypeName(); + return std::string(); } // ---------------------------------------------------------------------- std::vector* _ParamHelper::getDataMembersImpl(std::string const& mainkey, TClass* cl, void* obj, - std::map const* provmap) + std::map const* provmap, size_t globaloffset) { std::vector* members = new std::vector; - auto toDataMember = [&members, obj, mainkey, provmap](const TDataMember* dm, int index, int size) { - auto TS = getSizeOfUnderlyingType(*dm); - char* pointer = ((char*)obj) + dm->GetOffset() + index * TS; + auto toDataMember = [&members, obj, mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { + auto TS = isContainer(*dm) ? 0 : getSizeOfUnderlyingType(*dm); + char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset; const std::string name = getName(dm, index, size); auto value = asString(*dm, pointer); @@ -269,7 +303,7 @@ std::type_info const& nameToTypeInfo(const char* tname, TDataType const* dt) } } // if we get here none of the above worked - if (strcmp(tname, "string") == 0 || strcmp(tname, "std::string")) { + if (strcmp(tname, "string") == 0 || strcmp(tname, "std::string") == 0) { return typeid(std::string); } LOG(error) << "ENCOUNTERED AN UNSUPPORTED TYPE " << tname << "IN A CONFIGURABLE PARAMETER"; @@ -280,17 +314,40 @@ std::type_info const& nameToTypeInfo(const char* tname, TDataType const* dt) void _ParamHelper::fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void* obj, boost::property_tree::ptree* tree, std::map>* keytostoragemap, - EnumRegistry* enumRegistry) + EnumRegistry* enumRegistry, size_t globaloffset) { boost::property_tree::ptree localtree; - auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry](const TDataMember* dm, int index, int size) { + using mapped_t = std::pair; + auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); - auto TS = getSizeOfUnderlyingType(*dm); - char* pointer = ((char*)obj) + dm->GetOffset() + index * TS; - localtree.put(name, asString(*dm, pointer)); + auto TS = isContainer(*dm) ? 0 : getSizeOfUnderlyingType(*dm); + char* pointer = ((char*)obj) + dm->GetOffset() + (index * TS) + globaloffset; + const auto key = mainkey + "." + name; + + if (isContainer(*dm)) { + const auto typeName = getContainerTypeName(*dm); + pointer = ((char*)obj) + dm->GetOffset() + globaloffset; + localtree.put(name, ConfigurableParam::registeredContainerAsString(typeName, pointer)); + TClass* containerClass = dm->GetClass(); + if (!containerClass) { + containerClass = TClass::GetClass(dm->GetFullTypeName()); + } + if (!containerClass) { + LOG(error) << "Cannot get TClass for container " << typeName; + return; + } + const std::type_info* tinfo = containerClass->GetTypeInfo(); + ConfigurableParam::registerContainerType(key, typeName); + keytostoragemap->insert(std::pair( + key, mapped_t(tinfo ? *tinfo : typeid(std::string), pointer))); + if (!tinfo) { + LOG(error) << "Cannot get type_info for container " << typeName; + } + return; + } - auto key = mainkey + "." + name; + localtree.put(name, asString(*dm, pointer)); // If it's an enum, we need to store separately all the legal // values so that we can map to them from the command line @@ -298,7 +355,6 @@ void _ParamHelper::fillKeyValuesImpl(std::string const& mainkey, TClass* cl, voi enumRegistry->add(key, dm); } - using mapped_t = std::pair; auto& ti = nameToTypeInfo(dm->GetTrueTypeName(), dt); keytostoragemap->insert(std::pair(key, mapped_t(ti, pointer))); }; @@ -308,23 +364,40 @@ void _ParamHelper::fillKeyValuesImpl(std::string const& mainkey, TClass* cl, voi // ---------------------------------------------------------------------- -void _ParamHelper::printMembersImpl(std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger) +void _ParamHelper::printMembersImpl(std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger, bool withPadding, bool showHash) { - _ParamHelper::outputMembersImpl(std::cout, mainkey, members, showProv, useLogger); + _ParamHelper::outputMembersImpl(std::cout, mainkey, members, showProv, useLogger, withPadding, showHash); } -void _ParamHelper::outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger) +void _ParamHelper::outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger, bool withPadding, bool showHash) { if (members == nullptr) { return; } + size_t maxpad{0}; + if (withPadding) { + for (auto& member : *members) { + maxpad = std::max(maxpad, member.name.size() + member.value.size()); + } + } + + if (showHash) { + std::string shash = std::format("{:07x}", getHashImpl(mainkey, members)); + shash = shash.substr(0, 7); + if (useLogger) { + LOG(info) << mainkey << " [Hash#" << shash << "]"; + } else { + out << mainkey << " [Hash#" << shash << "]\n"; + } + } + for (auto& member : *members) { if (useLogger) { - LOG(info) << member.toString(mainkey, showProv); + LOG(info) << member.toString(mainkey, showProv, maxpad); } else { - out << member.toString(mainkey, showProv) << "\n"; + out << member.toString(mainkey, showProv, maxpad) << "\n"; } } } @@ -355,14 +428,13 @@ bool isMemblockDifferent(char const* block1, char const* block2, int sizeinbytes // ---------------------------------------------------------------------- void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap) + std::map* provmap, size_t globaloffset) { - auto assignifchanged = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) { + auto assignifchanged = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); - auto dt = dm->GetDataType(); - auto TS = getSizeOfUnderlyingType(*dm); - char* pointerto = ((char*)to) + dm->GetOffset() + index * TS; - char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS; + auto TS = isContainer(*dm) ? 0 : getSizeOfUnderlyingType(*dm); + char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset; + char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset; // lambda to update the provenance auto updateProv = [&mainkey, name, provmap]() { @@ -378,6 +450,15 @@ void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* // TODO: this could dispatch to the same method used in ConfigurableParam::setValue // but will be slower + if (isContainer(*dm)) { + const auto typeName = getContainerTypeName(*dm); + if (!ConfigurableParam::areRegisteredContainersEqual(typeName, pointerto, pointerfrom)) { + updateProv(); + ConfigurableParam::assignRegisteredContainer(typeName, pointerto, pointerfrom); + } + return; + } + // test if a complicated case if (isString(*dm)) { std::string& target = *(std::string*)pointerto; @@ -402,14 +483,13 @@ void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* // ---------------------------------------------------------------------- void _ParamHelper::syncCCDBandRegistry(const std::string& mainkey, TClass* cl, void* to, void* from, - std::map* provmap) + std::map* provmap, size_t globaloffset) { - auto sync = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) { + auto sync = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); - auto dt = dm->GetDataType(); - auto TS = getSizeOfUnderlyingType(*dm); - char* pointerto = ((char*)to) + dm->GetOffset() + index * TS; - char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS; + auto TS = isContainer(*dm) ? 0 : getSizeOfUnderlyingType(*dm); + char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset; + char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset; // check current provenance auto key = mainkey + "." + name; @@ -423,6 +503,12 @@ void _ParamHelper::syncCCDBandRegistry(const std::string& mainkey, TClass* cl, v proviter->second = ConfigurableParam::EParamProvenance::kCCDB; }; + if (isContainer(*dm)) { + updateProv(); + ConfigurableParam::assignRegisteredContainer(getContainerTypeName(*dm), pointerto, pointerfrom); + return; + } + // test if a complicated case if (isString(*dm)) { std::string& target = *(std::string*)pointerto; diff --git a/Common/Utils/src/ConfigurableParamReaders.cxx b/Common/Utils/src/ConfigurableParamReaders.cxx new file mode 100644 index 0000000000000..2f36f8b27f5fa --- /dev/null +++ b/Common/Utils/src/ConfigurableParamReaders.cxx @@ -0,0 +1,76 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParamReaders.h" +#include "CommonUtils/StringUtils.h" +#include +#include + +#include +#include +#include + +namespace o2::conf +{ +// ------------------------------------------------------------------ + +boost::property_tree::ptree ConfigurableParamReaders::readINI(std::string const& filepath) +{ + boost::property_tree::ptree pt; + try { + boost::property_tree::read_ini(filepath, pt); + } catch (const boost::property_tree::ptree_error& e) { + LOG(fatal) << "Failed to read INI config file " << filepath << " (" << e.what() << ")"; + } catch (...) { + LOG(fatal) << "Unknown error when reading INI config file "; + } + + return pt; +} + +// ------------------------------------------------------------------ + +boost::property_tree::ptree ConfigurableParamReaders::readJSON(std::string const& filepath) +{ + boost::property_tree::ptree pt; + + try { + boost::property_tree::read_json(filepath, pt); + } catch (const boost::property_tree::ptree_error& e) { + LOG(fatal) << "Failed to read JSON config file " << filepath << " (" << e.what() << ")"; + } + + return pt; +} + +boost::property_tree::ptree ConfigurableParamReaders::readConfigFile(std::string const& filepath) +{ + auto inpfilename = o2::utils::Str::concat_string(sInputDir, filepath); + if (!std::filesystem::exists(inpfilename)) { + LOG(fatal) << inpfilename << " : config file does not exist!"; + } + + boost::property_tree::ptree pt; + + if (boost::iends_with(inpfilename, ".ini")) { + pt = ConfigurableParamReaders::readINI(inpfilename); + } else if (boost::iends_with(inpfilename, ".json")) { + pt = ConfigurableParamReaders::readJSON(inpfilename); + } else { + LOG(fatal) << "Configuration file must have either .ini or .json extension"; + } + + return pt; +} + +std::string ConfigurableParamReaders::sInputDir = ""; + +} // namespace o2::conf diff --git a/Common/Utils/src/ConfigurableParamTest.cxx b/Common/Utils/src/ConfigurableParamTest.cxx new file mode 100644 index 0000000000000..5115a8dfe889d --- /dev/null +++ b/Common/Utils/src/ConfigurableParamTest.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParamTest.h" +O2ParamImpl(o2::conf::test::TestParam); diff --git a/Common/Utils/src/DLLoaderBase.cxx b/Common/Utils/src/DLLoaderBase.cxx new file mode 100644 index 0000000000000..1e4ab97604794 --- /dev/null +++ b/Common/Utils/src/DLLoaderBase.cxx @@ -0,0 +1,12 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/DLLoaderBase.h" diff --git a/Common/Utils/src/DebugStreamer.cxx b/Common/Utils/src/DebugStreamer.cxx index 502c9797924c4..3a5401ca9b382 100644 --- a/Common/Utils/src/DebugStreamer.cxx +++ b/Common/Utils/src/DebugStreamer.cxx @@ -10,18 +10,16 @@ // or submit itself to any jurisdiction. #include "CommonUtils/DebugStreamer.h" -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) #include #include #include "TROOT.h" #include "TKey.h" #include #include "Framework/Logger.h" -#endif O2ParamImpl(o2::utils::ParameterDebugStreamer); -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) && defined(DEBUG_STREAMER) +#if defined(DEBUG_STREAMER) o2::utils::DebugStreamer::DebugStreamer() { @@ -55,7 +53,7 @@ void o2::utils::DebugStreamer::flush() } } -bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const size_t samplingID) +bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const size_t samplingID, const float weight) { const bool isStreamerSet = ((getStreamFlags() & streamFlag) == streamFlag); if (!isStreamerSet) { @@ -65,27 +63,22 @@ bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const s // check sampling frequency const auto sampling = getSamplingTypeFrequency(streamFlag); if (sampling.first != SamplingTypes::sampleAll) { - // init random number generator for each thread - static thread_local std::mt19937 generator(std::random_device{}()); - std::uniform_real_distribution<> distr(0, 1); - auto sampleTrack = [&]() { if (samplingID == -1) { - LOGP(fatal, "Sampling type sampleID not supported for stream flag {}", streamFlag); + LOGP(fatal, "Sampling type sampleID not supported for stream flag {}", (int)streamFlag); } - std::uniform_real_distribution<> distr(0, 1); // sample on samplingID (e.g. track level) static thread_local std::unordered_map> idMap; // in case of first call samplingID in idMap is 0 and always false and first ID rejected if (idMap[streamFlag].first != samplingID) { - idMap[streamFlag] = std::pair{samplingID, (distr(generator) < sampling.second)}; + idMap[streamFlag] = std::pair{samplingID, (getRandom() < sampling.second)}; } return idMap[streamFlag].second; }; if (sampling.first == SamplingTypes::sampleRandom) { // just sample randomly - return (distr(generator) < sampling.second); + return (getRandom() < sampling.second); } else if (sampling.first == SamplingTypes::sampleID) { return sampleTrack(); } else if (sampling.first == SamplingTypes::sampleIDGlobal) { @@ -104,11 +97,23 @@ bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const s refIDs[index][samplingID] = storeTrk; return storeTrk; } + } else if (sampling.first == SamplingTypes::sampleWeights) { + // sample with weight + return (weight * getRandom() < sampling.second); } } return true; } +float o2::utils::DebugStreamer::getRandom(float min, float max) +{ + // init random number generator for each thread + static thread_local std::mt19937 generator(std::random_device{}()); + std::uniform_real_distribution<> distr(min, max); + const float rnd = distr(generator); + return rnd; +} + int o2::utils::DebugStreamer::getIndex(const StreamFlags streamFlag) { // see: https://stackoverflow.com/a/71539401 @@ -145,19 +150,21 @@ int o2::utils::DebugStreamer::getNTrees(const size_t id) const { return isStream void o2::utils::DebugStreamer::mergeTrees(const char* inpFile, const char* outFile, const char* option) { TFile fInp(inpFile, "READ"); - std::unordered_map lists; + std::unordered_map lists; for (TObject* keyAsObj : *fInp.GetListOfKeys()) { const auto key = dynamic_cast(keyAsObj); TTree* tree = (TTree*)fInp.Get(key->GetName()); // perform simple check on the number of entries to merge only TTree with same content (ToDo: Do check on name of branches) const int entries = tree->GetListOfBranches()->GetEntries(); - lists[entries].Add(tree); + const std::string brName = key->GetName(); + const std::string nameBr = brName.substr(0, brName.find_last_of("_")); + lists[nameBr].Add(tree); } TFile fOut(outFile, "RECREATE"); for (auto& list : lists) { auto tree = TTree::MergeTrees(&list.second, option); - fOut.WriteObject(tree, tree->GetName()); + fOut.WriteObject(tree, list.first.data()); } } diff --git a/Common/Utils/src/FileFetcher.cxx b/Common/Utils/src/FileFetcher.cxx index 6492383d836f7..32c51ac704d4b 100644 --- a/Common/Utils/src/FileFetcher.cxx +++ b/Common/Utils/src/FileFetcher.cxx @@ -84,10 +84,17 @@ void FileFetcher::processInput(const std::vector& input) { for (auto inp : input) { o2::utils::Str::trim(inp); - if (fs::is_directory(inp)) { processDirectory(inp); } else if (mSelRegex && !std::regex_match(inp, *mSelRegex.get())) { // provided selector does not match, treat as a txt file with list + // Avoid reading a multigiB data file as a list of inputs + // bringing down the system. + std::filesystem::path p(inp); + if (std::filesystem::file_size(p) > 10000000) { + LOGP(error, "file list {} larger than 10MB. Is this a data file?", inp); + continue; + } + std::ifstream listFile(inp); if (!listFile.good()) { LOGP(error, "file {} pretends to be a list of inputs but does not exist", inp); @@ -97,7 +104,7 @@ void FileFetcher::processInput(const std::vector& input) std::vector newInput; while (getline(listFile, line)) { o2::utils::Str::trim(line); - if (line[0] == '#') { // ignore commented file + if (line[0] == '#' || line.empty()) { // ignore commented file or empty line continue; } newInput.push_back(line); @@ -322,6 +329,7 @@ bool FileFetcher::copyFile(size_t id) bool aliencpMode = false; std::string uuid{}; std::vector logsToClean; + std::string dbgset{}; if (mCopyCmd.find("alien") != std::string::npos) { if (!gGrid && !TGrid::Connect("alien://")) { LOG(error) << "Copy command refers to alien but connection to Grid failed"; @@ -332,15 +340,18 @@ bool FileFetcher::copyFile(size_t id) c = '_'; } } - gSystem->Setenv("ALIENPY_DEBUG", "1"); - logsToClean.push_back(fmt::format("log_alienpy_{}.txt", uuid)); - gSystem->Setenv("ALIENPY_DEBUG_FILE", logsToClean.back().c_str()); - gSystem->Setenv("XRD_LOGLEVEL", "Dump"); - logsToClean.push_back(fmt::format("log_xrd_{}.txt", uuid)); - gSystem->Setenv("XRD_LOGFILE", logsToClean.back().c_str()); + if (!(getenv("ALIENPY_DEBUG") && std::stoi(getenv("ALIENPY_DEBUG")) == 1)) { + logsToClean.push_back(fmt::format("log_alienpy_{}.txt", uuid)); + dbgset += fmt::format("ALIENPY_DEBUG=1 ALIENPY_DEBUG_FILE={} ", logsToClean.back()); + } + if (!(getenv("XRD_LOGLEVEL") && strcmp(getenv("XRD_LOGLEVEL"), "Dump") == 0)) { + logsToClean.push_back(fmt::format("log_xrd_{}.txt", uuid)); + dbgset += fmt::format("XRD_LOGLEVEL=Dump XRD_LOGFILE={} ", logsToClean.back()); + } + LOGP(debug, "debug setting for for {}: {}", mInputFiles[id].getOrigName(), dbgset); } auto realCmd = std::regex_replace(std::regex_replace(mCopyCmd, std::regex(R"(\?src)"), mInputFiles[id].getOrigName()), std::regex(R"(\?dst)"), mInputFiles[id].getLocalName()); - auto fullCmd = fmt::format(R"(sh -c "{}" >> {} 2>&1)", realCmd, mCopyCmdLogFile); + auto fullCmd = fmt::format(R"(sh -c "{}{}" >> {} 2>&1)", dbgset, realCmd, mCopyCmdLogFile); LOG(info) << "Executing " << fullCmd; const auto sysRet = gSystem->Exec(fullCmd.c_str()); if (sysRet != 0) { diff --git a/Common/Utils/src/FileSystemUtils.cxx b/Common/Utils/src/FileSystemUtils.cxx index 09541050325eb..44c0b06b4b03a 100644 --- a/Common/Utils/src/FileSystemUtils.cxx +++ b/Common/Utils/src/FileSystemUtils.cxx @@ -74,6 +74,12 @@ std::string expandShellVarsInFileName(std::string const& input) std::sregex_iterator iter; auto words_end = std::sregex_iterator(); // the end iterator (default) auto words_begin = std::sregex_iterator(input.begin(), input.end(), e); + + // check first of all if there is shell variable inside + if (words_end == words_begin) { + return input; + } + std::string tail; for (auto i = words_begin; i != words_end; ++i) { std::smatch match = *i; @@ -85,8 +91,11 @@ std::string expandShellVarsInFileName(std::string const& input) auto envlookup = getenv(m[0].str().c_str()); if (envlookup) { finalstr += match.prefix().str() + std::string(envlookup); - tail = match.suffix().str(); + } else { + // in case of non existance we keep the env part unreplaced + finalstr += match.prefix().str() + "${" + m[0].str().c_str() + "}"; } + tail = match.suffix().str(); } } finalstr += tail; diff --git a/Common/Utils/src/IRFrameSelector.cxx b/Common/Utils/src/IRFrameSelector.cxx index 19a032ac919c9..abc0ee1ee6ce3 100644 --- a/Common/Utils/src/IRFrameSelector.cxx +++ b/Common/Utils/src/IRFrameSelector.cxx @@ -149,13 +149,10 @@ size_t IRFrameSelector::loadIRFrames(const std::string& fname) bcRanges->SetBranchAddress("fBCend", &maxBC); for (int i = 0; i < (int)bcRanges->GetEntries(); i++) { bcRanges->GetEntry(i); - if (mOwnList.size()) { - auto& last = mOwnList.back(); - toBeSorted |= last.getMin() < minBC; - } mOwnList.emplace_back(InteractionRecord::long2IR(minBC), InteractionRecord::long2IR(maxBC)); } done = true; + toBeSorted = true; } } @@ -170,6 +167,16 @@ size_t IRFrameSelector::loadIRFrames(const std::string& fname) return mOwnList.size(); } +void IRFrameSelector::setOwnList(const std::vector& lst, bool toBeSorted) +{ + clear(); + mOwnList.insert(mOwnList.end(), lst.begin(), lst.end()); + if (toBeSorted) { + std::sort(mOwnList.begin(), mOwnList.end(), [](const auto& a, const auto& b) { return a.getMin() < b.getMin(); }); + } + setSelectedIRFrames(mOwnList, 0, 0, 0, false); +} + void IRFrameSelector::print(bool lst) const { LOGP(info, "Last query stopped at entry {} for IRFrame {}:{}", mLastBoundID, @@ -186,20 +193,36 @@ void IRFrameSelector::clear() { mIsSet = false; mOwnList.clear(); + mLastIRFrameChecked.getMin().clear(); // invalidate + mLastBoundID = -1; mFrames = {}; } -void IRFrameSelector::applyMargins(size_t bwd, size_t fwd, bool removeOverlaps) +void IRFrameSelector::applyMargins(size_t bwd, size_t fwd, long shift, bool removeOverlaps) { - // apply margin to all IRFrames by converting them to IRFrame.getMin()-fwd, IRFrame.getMax()-bwd - if ((!fwd && !bwd) || !mIsSet || !mFrames.size()) { + // apply shift and margin to all IRFrames by converting them to IRFrame.getMin() - fwd + shift, IRFrame.getMax() + bwd + shift + LOGP(debug, "applyMargins({},{},{})", bwd, fwd, removeOverlaps); + if ((!fwd && !bwd && !shift) || !mIsSet || !mFrames.size()) { return; } mLastBoundID = -1; std::vector lst; + long shiftBwd = bwd - shift, shiftFwd = fwd + shift; + for (const auto& fr : mFrames) { - auto irmin = fr.getMin().toLong() > bwd ? fr.getMin() - bwd : o2::InteractionRecord{0, 0}; - auto irmax = (o2::InteractionRecord::MaxGlobalBCs - fr.getMax().toLong()) > fwd ? fr.getMax() + fwd : o2::InteractionRecord::getIRMaxBC(); + auto irmin = fr.getMin(); + auto irmax = fr.getMax(); + if (shiftBwd > 0) { + irmin = fr.getMin().toLong() > shiftBwd ? fr.getMin() - shiftBwd : o2::InteractionRecord{0, 0}; + } else { + irmin = (o2::InteractionRecord::MaxGlobalBCs - fr.getMin().toLong()) > -shiftBwd ? fr.getMin() - shiftBwd : o2::InteractionRecord::getIRMaxBC(); + } + if (shiftFwd > 0) { + irmax = (o2::InteractionRecord::MaxGlobalBCs - fr.getMax().toLong()) > shiftFwd ? fr.getMax() + shiftFwd : o2::InteractionRecord::getIRMaxBC(); + } else { + irmax = fr.getMax().toLong() > -shiftFwd ? fr.getMax() + shiftFwd : o2::InteractionRecord{0, 0}; + } + LOGP(debug, "before removerlap: {}:{} -> {}:{}", fr.getMin().toLong(), fr.getMax().toLong(), irmin.toLong(), irmax.toLong()); if (removeOverlaps && lst.size() && lst.back().getMax() >= irmin) { lst.back().setMax(irmax); } else { @@ -207,5 +230,5 @@ void IRFrameSelector::applyMargins(size_t bwd, size_t fwd, bool removeOverlaps) } } mOwnList.swap(lst); - setSelectedIRFrames(mOwnList); + mFrames = gsl::span(mOwnList.data(), mOwnList.size()); } diff --git a/Common/Utils/src/NameConf.cxx b/Common/Utils/src/NameConf.cxx index 5a5f644f2da39..48cefacaf14c7 100644 --- a/Common/Utils/src/NameConf.cxx +++ b/Common/Utils/src/NameConf.cxx @@ -95,6 +95,11 @@ std::string NameConf::getCTFFileName(uint32_t run, uint32_t orb, uint32_t id, co return o2::utils::Str::concat_string(prefix, '_', fmt::format("run{:08d}_orbit{:010d}_tf{:010d}_{}", run, orb, id, host), ".root"); } +std::string NameConf::getRawTFFileName(uint32_t run, uint32_t orb, uint32_t id, const std::string& host, const std::string_view prefix) +{ + return o2::utils::Str::concat_string(prefix, '_', fmt::format("run{:08d}_orbit{:010d}_tf{:010d}_{}", run, orb, id, host), ".tf"); +} + std::string NameConf::getCTFDictFileName() { return o2::utils::Str::concat_string(CTFDICT, ".root"); @@ -111,3 +116,12 @@ std::string NameConf::getCCDBServer() { return Instance().mCCDBServer; } + +std::string NameConf::getConfigOutputFileName(const std::string& procName, const std::string& confName, bool json) +{ + std::string nm = procName; + if (!confName.empty()) { + nm += '_' + confName; + } + return fmt::format("ConfigParam_{}.{}", nm, json ? "json" : "ini"); +} diff --git a/Common/Utils/src/RootSerializableKeyValueStore.cxx b/Common/Utils/src/RootSerializableKeyValueStore.cxx index 7bc424300674e..0e0902f6cc4f2 100644 --- a/Common/Utils/src/RootSerializableKeyValueStore.cxx +++ b/Common/Utils/src/RootSerializableKeyValueStore.cxx @@ -14,11 +14,57 @@ using namespace o2::utils; -void RootSerializableKeyValueStore::print() const +namespace +{ +template +std::string stringFromType(char* buffer) +{ + T value; + std::memcpy(&value, buffer, sizeof(T)); + return std::to_string(value); +} +} // namespace + +void RootSerializableKeyValueStore::print(bool includetypeinfo) const { for (auto& p : mStore) { const auto& key = p.first; const auto info = p.second; - std::cout << "key: " << key << " of-type: " << info.typeinfo_name << "\n"; + auto tinfo = info.typeinfo_name; + + std::string value("unknown-value"); + // let's try to decode the value as a string if we can + if (tinfo == typeid(int).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(unsigned int).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(short).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(unsigned short).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(double).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(float).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(std::string).name()) { + value = *(get(key)); + } + std::cout << "key: " << key << " value: " << value; + if (includetypeinfo) { + std::cout << " type: " << info.typeinfo_name; + } + std::cout << "\n"; } } diff --git a/Common/Utils/src/ShmManager.cxx b/Common/Utils/src/ShmManager.cxx index cb3af5dedf380..26b30be062220 100644 --- a/Common/Utils/src/ShmManager.cxx +++ b/Common/Utils/src/ShmManager.cxx @@ -119,8 +119,8 @@ bool ShmManager::createGlobalSegment(int nsegments) return false; } - LOG(info) << "CREATING SIM SHARED MEM SEGMENT FOR " << nsegments << " WORKERS"; #ifdef USESHM + LOG(info) << "CREATING SIM SHARED MEM SEGMENT FOR " << nsegments << " WORKERS"; // LOG(info) << "SIZEOF ShmMetaInfo " << sizeof(ShmMetaInfo); const auto totalsize = sizeof(ShmMetaInfo) + SHMPOOLSIZE * nsegments; if ((mShmID = shmget(IPC_PRIVATE, totalsize, IPC_CREAT | 0666)) == -1) { diff --git a/Common/Utils/src/StringUtils.cxx b/Common/Utils/src/StringUtils.cxx index 8466df31dd57c..89f834d9a8f2e 100644 --- a/Common/Utils/src/StringUtils.cxx +++ b/Common/Utils/src/StringUtils.cxx @@ -12,7 +12,12 @@ #include "CommonUtils/StringUtils.h" #include #include +#ifndef GPUCA_STANDALONE #include +#include +#endif +#include +#include using namespace o2::utils; @@ -33,13 +38,57 @@ std::vector Str::tokenize(const std::string& src, char delim, bool return tokens; } +std::vector Str::tokenize(const std::string& src, const std::string& delim, bool trimToken, bool skipEmpty) +{ + std::string inptStr{src}; + char* input = inptStr.data(); + auto mystrtok = [&]() -> char* { + input += std::strspn(input, delim.c_str()); + if (*input == '\0') { + return nullptr; + } + char* const token = input; + input += std::strcspn(input, delim.c_str()); + if (*input != '\0') { + *input++ = '\0'; + } + return token; + }; + std::vector tokens; + while (*input != '\0') { + std::string token = mystrtok(); + if (trimToken) { + trim(token); + } + if (!token.empty() || !skipEmpty) { + tokens.push_back(std::move(token)); + } + } + return tokens; +} + +// replace all occurencies of from by to, return count +int Str::replaceAll(std::string& s, const std::string& from, const std::string& to) +{ + int count = 0; + size_t pos = 0; + while ((pos = s.find(from, pos)) != std::string::npos) { + s.replace(pos, from.length(), to); + pos += to.length(); // Handles case where 'to' is a substring of 'from' + count++; + } + return count; +} + // generate random string of given lenght, suitable for file names std::string Str::getRandomString(int lenght) { - auto nextAllowed = []() { + int pid = (int)getpid(); + auto nextAllowed = [pid]() { constexpr char chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; constexpr size_t L = sizeof(chars) - 1; - return chars[std::rand() % L]; + int rn = std::rand() | pid; + return chars[rn % L]; }; std::string str(lenght, 0); std::generate_n(str.begin(), lenght, nextAllowed); @@ -61,6 +110,7 @@ std::string Str::getFullPath(const std::string_view p) return std::filesystem::canonical(std::string{p}).string(); } +#ifndef GPUCA_STANDALONE std::string Str::rectifyDirectory(const std::string_view p) { std::string dir(p); @@ -88,6 +138,7 @@ std::string Str::rectifyDirectory(const std::string_view p) } return dir; } +#endif // Create unique non-existing path name starting with prefix. Loose equivalent of boost::filesystem::unique_path() // The prefix can be either existing directory or just a string to add in front of the random part diff --git a/Common/Utils/src/TreeStream.cxx b/Common/Utils/src/TreeStream.cxx index da25f25ad2eb1..cd0641a11d043 100644 --- a/Common/Utils/src/TreeStream.cxx +++ b/Common/Utils/src/TreeStream.cxx @@ -29,8 +29,7 @@ int TreeStream::CheckIn(Char_t type, const void* pointer) // Insert object if (mCurrentIndex >= static_cast(mElements.size())) { - mElements.emplace_back(); - auto& element = mElements.back(); + auto& element = mElements.emplace_back(); element.type = type; TString name = mNextName; if (name.Length()) { @@ -42,6 +41,8 @@ int TreeStream::CheckIn(Char_t type, const void* pointer) } element.name = name.Data(); element.ptr = pointer; + element.arsize = mNextArraySize; + mNextArraySize = 1; // reset } else { auto& element = mElements[mCurrentIndex]; if (element.type != type) { @@ -89,7 +90,13 @@ void TreeStream::BuildTree() } if (element.type > 0) { - TString nameC = TString::Format("%s/%c", name.Data(), element.type); + TString nameC; + if (element.arsize > 1) { + nameC = TString::Format("%s[%d]/%c", name.Data(), element.arsize, + element.type); + } else { + nameC = TString::Format("%s/%c", name.Data(), element.type); + } br = mTree.Branch(name.Data(), const_cast(element.ptr), nameC.Data()); if (entriesFilled) { br->SetAddress(nullptr); @@ -148,28 +155,43 @@ TreeStream& TreeStream::Endl() TreeStream& TreeStream::operator<<(const Char_t* name) { // Stream the branch name - // if (name[0] == '\n') { return Endl(); } - // + // if tree was already defined ignore if (mTree.GetEntries() > 0) { return *this; } + + int arsize = 1; + // check branch name if tree was not - // Int_t last = 0; for (last = 0;; last++) { if (name[last] == 0) { break; } } - if (last > 0 && name[last - 1] == '=') { mNextName = name; - mNextName[last - 1] = 0; + mNextName[last - 1] = 0; // remove '=' from string mNextNameCounter = 0; + + TString inName{name}; + auto brkStaPos = inName.Index('['); + + if (brkStaPos != kNPOS) { + auto brkEndPos = inName.Index(']'); + if (brkEndPos != kNPOS && brkEndPos > brkStaPos + 1) { + TString size = inName(brkStaPos + 1, brkEndPos - brkStaPos - 1); + arsize = size.Atoi(); + mNextName = inName(0, brkStaPos); // use parsed name + } + } } + + mNextArraySize = arsize; + return *this; } diff --git a/Common/Utils/src/TreeStreamRedirector.cxx b/Common/Utils/src/TreeStreamRedirector.cxx index 86e0eb5a37552..06fb3d65678c4 100644 --- a/Common/Utils/src/TreeStreamRedirector.cxx +++ b/Common/Utils/src/TreeStreamRedirector.cxx @@ -111,11 +111,13 @@ TreeStream& TreeStreamRedirector::operator<<(const char* name) void TreeStreamRedirector::Close() { // flush and close - + if (!mDirectory) { + return; + } TDirectory* backup = gDirectory; mDirectory->cd(); for (auto& layout : mDataLayouts) { - layout->getTree().Write(layout->getName()); + layout->getTree().Write(layout->getName(), TObject::kOverwrite); } mDataLayouts.clear(); if (backup) { diff --git a/Common/Utils/src/fpu.cxx b/Common/Utils/src/fpu.cxx new file mode 100644 index 0000000000000..505935e15c36e --- /dev/null +++ b/Common/Utils/src/fpu.cxx @@ -0,0 +1,44 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// To enable check for problematic LIBXML version uncomment this +// #define _PROTECT_LIBXML_ + +#ifdef __linux +#ifdef _PROTECT_LIBXML_ +#include +#if LIBXML_VERSION > 20912 +#define _DUMMY_FEE_TRAP_ +#endif // LIBXML_VERSION > 20912 +#endif // _PROTECT_LIBXML_ +#else // __linux +#define _DUMMY_FEE_TRAP_ +#endif // __linux + +#ifdef _DUMMY_FEE_TRAP_ +void trapfpe() {} +#else // _DUMMY_FEE_TRAP_ +#define _GNU_SOURCE 1 +#include +#include +static void __attribute__((constructor)) trapfpe() +{ + // allows to disable set of particular FE's by setting corresponding bit of the O2_DISABLE_FPE_TRAP, + // i.e. to enable only FE_DIVBYZERO use O2_DISABLE_FPE_TRAP=9 + // const char* ev = std::getenv("O2_DISABLE_FPE_TRAP"); + // int enabledFE = (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW) & ~(ev ? atoi(ev) : 0); + const char* ev = std::getenv("O2_ENABLE_FPE_TRAP"); + int enabledFE = (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW) & (ev ? atoi(ev) : 0); + if (enabledFE) { + feenableexcept(enabledFE); + } +} +#endif // _DUMMY_FEE_TRAP_ diff --git a/Common/Utils/test/testBoostSerializer.cxx b/Common/Utils/test/testBoostSerializer.cxx deleted file mode 100644 index 520bc7dda8b08..0000000000000 --- a/Common/Utils/test/testBoostSerializer.cxx +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// @author Gabriele Gaetano Fronzé - -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MAIN - -#include -#include -#include -#include -#include "CommonUtils/BoostSerializer.h" -#include - -using namespace o2::utils; - -struct TestCluster { - uint8_t deId; ///< Detection element ID - float xCoor; ///< Local x coordinate - float yCoor; ///< Local y coordinate - float sigmaX2; ///< Square of dispersion along x - float sigmaY2; ///< Square of dispersion along y - - friend class boost::serialization::access; - - /// Serializes the struct - template - void serialize(Archive& ar, const unsigned int version) - { - ar& deId& xCoor& yCoor& sigmaX2& sigmaY2; - } -}; - -BOOST_AUTO_TEST_SUITE(testDPLSerializer) - -// BOOST_AUTO_TEST_CASE(testTrivialTypeVect) -// { -// using contType = std::vector; - -// contType inputV{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - -// auto msgStr = BoostSerialize(inputV).str(); -// auto inputV2 = BoostDeserialize(msgStr); - -// BOOST_TEST(inputV.size() == inputV2.size()); - -// size_t i = 0; -// for (auto const& test : inputV) { -// BOOST_TEST(test == inputV2[i]); -// i++; -// } -// } - -// BOOST_AUTO_TEST_CASE(testTrivialTypeArray) -// { -// using contType = std::array; - -// contType inputV{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - -// auto msgStr = BoostSerialize(inputV).str(); -// auto inputV2 = BoostDeserialize(msgStr); - -// BOOST_TEST(inputV.size() == inputV2.size()); - -// size_t i = 0; -// for (auto const& test : inputV) { -// BOOST_TEST(test == inputV2[i]); -// i++; -// } -// } - -// BOOST_AUTO_TEST_CASE(testTrivialTypeList) -// { -// using contType = std::list; - -// contType inputV{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - -// auto msgStr = BoostSerialize(inputV).str(); -// auto inputV2 = BoostDeserialize(msgStr); - -// BOOST_TEST(inputV.size() == inputV2.size()); - -// size_t i = 0; -// for (auto const& test : inputV) { -// auto value = std::next(std::begin(inputV2), i).operator*(); -// BOOST_TEST(test == value); -// i++; -// } -// } - -BOOST_AUTO_TEST_CASE(testBoostSerialisedType) -{ - using contType = std::vector; - - contType inputV; - - for (size_t i = 0; i < 17; i++) { - float iFloat = (float)i; - inputV.emplace_back(TestCluster{(uint8_t)i, 0.3f * iFloat, 0.5f * iFloat, 0.7f / iFloat, 0.9f / iFloat}); - } - - auto msgStr = BoostSerialize(inputV).str(); - auto inputV2 = BoostDeserialize(msgStr); - - BOOST_TEST(inputV.size() == inputV2.size()); - - size_t i = 0; - for (auto const& test : inputV) { - BOOST_TEST(test.deId == inputV2[i].deId); - BOOST_TEST(test.xCoor == inputV2[i].xCoor); - BOOST_TEST(test.yCoor == inputV2[i].yCoor); - BOOST_TEST(test.sigmaX2 == inputV2[i].sigmaX2); - BOOST_TEST(test.sigmaY2 == inputV2[i].sigmaY2); - i++; - } -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/Common/Utils/test/testConfigurableParam.cxx b/Common/Utils/test/testConfigurableParam.cxx new file mode 100644 index 0000000000000..6fd0344cdd1be --- /dev/null +++ b/Common/Utils/test/testConfigurableParam.cxx @@ -0,0 +1,334 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#define BOOST_TEST_MODULE Test ConfigurableParams +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CommonUtils/ConfigurableParamTest.h" + +using namespace o2::conf; +using namespace o2::conf::test; + +BOOST_AUTO_TEST_CASE(ConfigurableParam_Basic) +{ + // Tests the default parameters and also getter helpers. + auto& param = TestParam::Instance(); + BOOST_CHECK_EQUAL(param.iValue, 42); + BOOST_CHECK_EQUAL(param.dValue, 3.14); + BOOST_CHECK_EQUAL(param.bValue, true); + BOOST_CHECK_EQUAL(param.sValue, "default"); + BOOST_CHECK_EQUAL(static_cast(param.eValue), 2); + + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.iValue"), 42); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.dValue"), 3.14); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.bValue"), true); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.sValue"), "default"); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_SG_Fundamental) +{ + // tests runtime setting and getting for fundamental types + ConfigurableParam::setValue("TestParam.iValue", "100"); + ConfigurableParam::setValue("TestParam.dValue", "2.718"); + ConfigurableParam::setValue("TestParam.bValue", "0"); + ConfigurableParam::setValue("TestParam.sValue", "modified"); + ConfigurableParam::setValue("TestParam.eValue", "0"); + + auto& param = TestParam::Instance(); + BOOST_CHECK_EQUAL(param.iValue, 100); + BOOST_CHECK_EQUAL(param.dValue, 2.718); + BOOST_CHECK_EQUAL(param.bValue, false); + BOOST_CHECK_EQUAL(param.sValue, "modified"); + BOOST_CHECK_EQUAL(static_cast(param.eValue), 0); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_SG_CArray) +{ + // tests setting and getting for a c-style array type + auto& param = TestParam::Instance(); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[0]"), 0); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[1]"), 1); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[2]"), 2); + + ConfigurableParam::setValue("TestParam.caValue[1]", "99"); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[1]"), 99); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_SG_STD) +{ + // tests setting and getting for a std type + ConfigurableParam::setValue("TestParam.vec", "[10,20,30,40]"); + auto& param = TestParam::Instance(); + BOOST_CHECK_EQUAL(param.vec.size(), 4); + BOOST_CHECK_EQUAL(param.vec[0], 10); + BOOST_CHECK_EQUAL(param.vec[1], 20); + BOOST_CHECK_EQUAL(param.vec[2], 30); + BOOST_CHECK_EQUAL(param.vec[3], 40); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.vec"), "[10,20,30,40]"); + + ConfigurableParam::setValue("TestParam.u8vec", "[1,2,255]"); + BOOST_CHECK_EQUAL(param.u8vec.size(), 3); + BOOST_CHECK_EQUAL(static_cast(param.u8vec[0]), 1); + BOOST_CHECK_EQUAL(static_cast(param.u8vec[1]), 2); + BOOST_CHECK_EQUAL(static_cast(param.u8vec[2]), 255); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.u8vec"), "[1,2,255]"); + + ConfigurableParam::setValues({{"TestParam.map", "{0:1,10:42}"}}); + BOOST_CHECK_EQUAL(param.map.size(), 2); + BOOST_CHECK(param.map.contains(0)); + BOOST_CHECK_EQUAL(param.map.at(0), 1); + BOOST_CHECK(param.map.contains(10)); + BOOST_CHECK_EQUAL(param.map.at(10), 42); + BOOST_CHECK_THROW(param.map.at(33), std::out_of_range); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_Provenance) +{ + // tests correct setting of provenance + BOOST_CHECK_EQUAL(ConfigurableParam::getProvenance("TestParam.iValueProvenanceTest"), ConfigurableParam::EParamProvenance::kCODE); + ConfigurableParam::setValue("TestParam.iValueProvenanceTest", "123"); + BOOST_CHECK_EQUAL(ConfigurableParam::getProvenance("TestParam.iValueProvenanceTest"), ConfigurableParam::EParamProvenance::kRT); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_Ini) +{ + // test for ini file serialization + const std::string testFileName = "test_config.ini"; + auto iValueBefore = TestParam::Instance().iValue; + auto sValueBefore = TestParam::Instance().sValue; + ConfigurableParam::setValue("TestParam.vec", "[7,8]"); + const std::vector vecBefore = TestParam::Instance().vec; + ConfigurableParam::writeINI(testFileName); + ConfigurableParam::setValue("TestParam.iValue", "999"); + ConfigurableParam::setValue("TestParam.sValue", testFileName); + ConfigurableParam::setValue("TestParam.vec", "[1]"); + ConfigurableParam::updateFromFile(testFileName); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore); + BOOST_CHECK_EQUAL_COLLECTIONS(TestParam::Instance().vec.begin(), TestParam::Instance().vec.end(), vecBefore.begin(), vecBefore.end()); + std::remove(testFileName.c_str()); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_Json) +{ + // test for json file serialization + const std::string testFileName = "test_config.json"; + auto iValueBefore = TestParam::Instance().iValue; + auto sValueBefore = TestParam::Instance().sValue; + ConfigurableParam::setValues({{"TestParam.map", "{3:4,5:6}"}}); + const std::map mapBefore = TestParam::Instance().map; + ConfigurableParam::writeJSON(testFileName); + ConfigurableParam::setValue("TestParam.iValue", "999"); + ConfigurableParam::setValue("TestParam.sValue", testFileName); + ConfigurableParam::setValues({{"TestParam.map", "{1:2}"}}); + ConfigurableParam::updateFromFile(testFileName); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore); + BOOST_CHECK_EQUAL(TestParam::Instance().map.size(), mapBefore.size()); + BOOST_CHECK_EQUAL(TestParam::Instance().map.at(3), mapBefore.at(3)); + BOOST_CHECK_EQUAL(TestParam::Instance().map.at(5), mapBefore.at(5)); + std::remove(testFileName.c_str()); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_JSONString_UnchangedOnly) +{ + ConfigurableParam::setValue("TestParam.ulValue", "2"); + ConfigurableParam::setProvenance("TestParam", "lValue", ConfigurableParam::kCODE); + ConfigurableParam::setProvenance("TestParam", "ulValue", ConfigurableParam::kRT); + ConfigurableParam::updateFromJSONString(R"json({"TestParam":{"lValue":"77","ulValue":"88"}})json", "TestParam", true); + + BOOST_CHECK_EQUAL(TestParam::Instance().lValue, 77); + BOOST_CHECK_EQUAL(TestParam::Instance().ulValue, 2); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_JSONString_FromAsJSON) +{ + ConfigurableParam::setValue("TestParam.iValue", "321"); + ConfigurableParam::setValue("TestParam.sValue", "json-source"); + ConfigurableParam::setValues({{"TestParam.map", "{7:8,9:10}"}}); + const auto mapBefore = TestParam::Instance().map; + const auto json = ConfigurableParam::asJSON("TestParam"); + + ConfigurableParam::setValue("TestParam.iValue", "999"); + ConfigurableParam::setValue("TestParam.sValue", "json-modified"); + ConfigurableParam::setValues({{"TestParam.map", "{1:2}"}}); + ConfigurableParam::updateFromJSONString(json, "TestParam"); + + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, 321); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, "json-source"); + BOOST_CHECK_EQUAL(TestParam::Instance().map.size(), mapBefore.size()); + BOOST_CHECK_EQUAL(TestParam::Instance().map.at(7), mapBefore.at(7)); + BOOST_CHECK_EQUAL(TestParam::Instance().map.at(9), mapBefore.at(9)); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_JSONString_ParamsListMissing) +{ + BOOST_CHECK_THROW(ConfigurableParam::updateFromJSONString(ConfigurableParam::asJSON("TestParam"), "MissingParam"), std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_ROOT) +{ + // test for root file serialization + const std::string testFileName = "test_config.root"; + auto iValueBefore = TestParam::Instance().iValue; + auto sValueBefore = TestParam::Instance().sValue; + TFile* testFile = TFile::Open(testFileName.c_str(), "RECREATE"); + TestParam::Instance().serializeTo(testFile); + testFile->Close(); + ConfigurableParam::setValue("TestParam.iValue", "999"); + ConfigurableParam::setValue("TestParam.sValue", testFileName); + ConfigurableParam::fromCCDB(testFileName); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore); + std::remove(testFileName.c_str()); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_Cli) +{ + // test setting values from as a cli arg string + ConfigurableParam::updateFromString("TestParam.iValue=55;TestParam.sValue=cli"); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, 55); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, "cli"); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_LiteralSuffix) +{ + // test setting values with the correct literal suffix + ConfigurableParam::updateFromString("TestParam.fValue=42.f"); + BOOST_CHECK_EQUAL(TestParam::Instance().fValue, 42.f); + + ConfigurableParam::setValue("TestParam.ullValue", "999ull"); + BOOST_CHECK_EQUAL(TestParam::Instance().ullValue, 999ULL); + // check using wrong literal suffix fails, prints error to std + ConfigurableParam::setValue("TestParam.ullValue", "888u"); + BOOST_CHECK_NE(TestParam::Instance().ullValue, 888); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_ContainerParserVector) +{ + auto v = ContainerParser::parse>("[1,2,3,4,5]"); + BOOST_CHECK_EQUAL(v.size(), 5); + BOOST_CHECK_EQUAL(v[0], 1); + BOOST_CHECK_EQUAL(v[4], 5); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_ContainerParserMap) +{ + auto m = ContainerParser::parse>("{alpha:0.5,beta:0.3,gamma:0.2}"); + BOOST_CHECK_EQUAL(m.size(), 3); + BOOST_CHECK_EQUAL(m["alpha"], 0.5); + BOOST_CHECK_EQUAL(m["beta"], 0.3); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_ContainerParserSetAndSequenceTypes) +{ + // All non-map containers go through the single parseSequence path. Verify the + // set containers (which previously took a separate, doubly-parsing parseSet + // branch) still parse and de-duplicate correctly. + auto se = ContainerParser::parse>("[2,3,2,3,2,5]"); + BOOST_CHECK_EQUAL(se.size(), 3); + BOOST_CHECK(se == (std::set{2, 3, 5})); + + auto us = ContainerParser::parse>("[1,2,3,3]"); + BOOST_CHECK_EQUAL(us.size(), 3); + BOOST_CHECK(us.count(1) && us.count(2) && us.count(3)); + + auto li = ContainerParser::parse>("[7,8]"); + BOOST_CHECK(li == (std::list{7, 8})); + + auto dq = ContainerParser::parse>("[9,10]"); + BOOST_CHECK(dq == (std::deque{9, 10})); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_ContainerParserRejectsEmptyToken) +{ + // A stray delimiter must be reported, not silently swallowed: "[1,,3]" used to + // parse to a 2-element vector with no error, masking malformed configuration. + BOOST_CHECK_THROW(ContainerParser::parse>("[1,,3]"), std::exception); + BOOST_CHECK_THROW(ContainerParser::parse>("[1,2,]"), std::exception); + // ... and on the map side, an empty value/entry is rejected too. + BOOST_CHECK_THROW((ContainerParser::parse>("{1:2,,3:4}")), std::exception); + BOOST_CHECK_THROW((ContainerParser::parse>("{1:}")), std::exception); + // Well-formed input is unaffected. + auto v = ContainerParser::parse>("[1,2,3]"); + BOOST_CHECK_EQUAL(v.size(), 3); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_DamerauLevenshteinDistance) +{ + BOOST_CHECK_EQUAL(damerauLevenshteinDistance("TestParam.iValue", "TestParam.iValue"), 0); + BOOST_CHECK_EQUAL(damerauLevenshteinDistance("TestParam.iValu", "TestParam.iValue"), 1); + BOOST_CHECK_EQUAL(damerauLevenshteinDistance("TestParam.jValue", "TestParam.iValue"), 1); + BOOST_CHECK_EQUAL(damerauLevenshteinDistance("TestParam.iVaule", "TestParam.iValue"), 1); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_UnknownKeySuggestionsNonFatal) +{ + setenv("ALICEO2_CONFIGURABLEPARAM_WRONGKEYISNONFATAL", "1", 1); + ConfigurableParam::setValue("TestParam.iValue", "222"); + ConfigurableParam::setValue("TestParam.caValue[1]", "33"); + ConfigurableParam::setValues({{"TestParam.iValu", "777"}, + {"TstParam.iValue", "888"}, + {"TestParam.caValue[", "999"}}); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, 222); + BOOST_CHECK_EQUAL(TestParam::Instance().caValue[1], 33); + unsetenv("ALICEO2_CONFIGURABLEPARAM_WRONGKEYISNONFATAL"); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_Container_FileIO_ROOT) +{ + // test for root file serialization + const std::string testFileName = "test_config.root"; + TFile* testFile = TFile::Open(testFileName.c_str(), "RECREATE"); + ConfigurableParam::setValue("TestParam.vec", "[1,2,3]"); + ConfigurableParam::setValue("TestParam.u8vec", "[4,5,6]"); + ConfigurableParam::setValue("TestParam.map", "{1:16,2:9,3:23456}"); + ConfigurableParam::setValue("TestParam.smap", "{a:16,b:9,c:23456}"); + ConfigurableParam::setValue("TestParam.set", "[2,3,2,3,2,5]"); + TestParam::Instance().serializeTo(testFile); + testFile->Close(); + ConfigurableParam::setValue("TestParam.vec", "[9]"); + ConfigurableParam::fromCCDB(testFileName); + const auto& tp = TestParam::Instance(); + const std::vector v = {1, 2, 3}; + BOOST_CHECK_EQUAL_COLLECTIONS(tp.vec.begin(), tp.vec.end(), v.begin(), v.end()); + const std::vector v8 = {4, 5, 6}; + BOOST_CHECK_EQUAL_COLLECTIONS(tp.u8vec.begin(), tp.u8vec.end(), v8.begin(), v8.end()); + std::map map{{1, 16}, {2, 9}, {3, 23456}}; + auto testMapEqual = [](const auto& m1, const auto& m2) { + for (const auto& [k, v] : m1) { + BOOST_CHECK(m2.find(k) != m2.end()); + BOOST_CHECK_EQUAL(v, m2.at(k)); + } + }; + testMapEqual(map, tp.map); + std::map smap{{"a", 16}, {"b", 9}, {"c", 23456}}; + testMapEqual(smap, tp.smap); + std::set set{2, 3, 5}; + BOOST_CHECK_EQUAL(set.size(), tp.set.size()); + for (const auto& s : set) { + BOOST_CHECK(tp.set.contains(s)); + } + std::remove(testFileName.c_str()); +} diff --git a/Common/Utils/test/testEnumFlags.cxx b/Common/Utils/test/testEnumFlags.cxx new file mode 100644 index 0000000000000..9101ffb97fdfe --- /dev/null +++ b/Common/Utils/test/testEnumFlags.cxx @@ -0,0 +1,653 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test Flags +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include +#include + +#include +#include + +#include "CommonUtils/EnumFlags.h" + +// Example enum to use with EnumFlags +enum class TestEnum : uint8_t { + Bit1 = 0, + Bit2, + Bit3, + Bit4, + Bit5VeryLongName, +}; + +// Very long enum +// to test that it works beyond 32 bits upto 64 bits +#define ENUM_BIT_NAME(n) Bit##n +#define ENUM_BIT_NAME_EXPAND(n) ENUM_BIT_NAME(n) +#define ENUM_BIT(z, n, _) ENUM_BIT_NAME_EXPAND(BOOST_PP_INC(n)) = (n), +enum class TestEnumLong : uint64_t { + BOOST_PP_REPEAT(64, ENUM_BIT, _) +}; +#undef ENUM_BIT +#undef ENUM_BIT_NAME +#undef ENUM_BIT_NAME_EXPAND + +BOOST_AUTO_TEST_CASE(Flags_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test default initialization + EFlags flags; + BOOST_TEST(flags.None == 0); + BOOST_TEST(flags.All == 31); + BOOST_TEST(flags.value() == 0); + BOOST_TEST(!flags.any()); + + // Test initialization with a single flag + EFlags flag1(TestEnum::Bit1); + BOOST_TEST(flag1.test(TestEnum::Bit1)); + BOOST_TEST(!flag1.test(TestEnum::Bit2)); + BOOST_TEST(flag1.value() == (1 << static_cast(TestEnum::Bit1))); + + // Test initialization with initializer list + EFlags multipleFlags({TestEnum::Bit1, TestEnum::Bit3}); + BOOST_TEST(multipleFlags.test(TestEnum::Bit1)); + BOOST_TEST(multipleFlags.test(TestEnum::Bit3)); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit2)); + BOOST_TEST(multipleFlags.any()); + + // Test reset + multipleFlags.reset(TestEnum::Bit1); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit1)); + BOOST_TEST(multipleFlags.test(TestEnum::Bit3)); + multipleFlags.reset(); + BOOST_TEST(!multipleFlags.any()); + + // Test multiset + multipleFlags.reset(); + multipleFlags.set(TestEnum::Bit2, TestEnum::Bit4); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit1)); + BOOST_TEST(multipleFlags.test(TestEnum::Bit2)); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit3)); + BOOST_TEST(multipleFlags.test(TestEnum::Bit4)); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit5VeryLongName)); + + // Test operator| + EFlags combinedFlags = flag1 | EFlags(TestEnum::Bit2); + BOOST_TEST(combinedFlags.test(TestEnum::Bit1)); + BOOST_TEST(combinedFlags.test(TestEnum::Bit2)); + BOOST_TEST(!combinedFlags.test(TestEnum::Bit3)); + combinedFlags |= TestEnum::Bit5VeryLongName; + BOOST_TEST(combinedFlags.test(TestEnum::Bit5VeryLongName)); + + // Test operator[] + BOOST_TEST(combinedFlags[TestEnum::Bit1]); + BOOST_TEST(combinedFlags[TestEnum::Bit2]); + BOOST_TEST(!combinedFlags[TestEnum::Bit3]); + + // Test operator|= + combinedFlags |= TestEnum::Bit3; + BOOST_TEST(combinedFlags.test(TestEnum::Bit3)); + + // Test operator& + EFlags intersection = combinedFlags & TestEnum::Bit1; + BOOST_TEST(intersection.test(TestEnum::Bit1)); + BOOST_TEST(!intersection.test(TestEnum::Bit2)); + BOOST_TEST(intersection.value() == (1 << static_cast(TestEnum::Bit1))); + + // Test operator&= + combinedFlags &= TestEnum::Bit1; + BOOST_TEST(combinedFlags.test(TestEnum::Bit1)); + BOOST_TEST(!combinedFlags.test(TestEnum::Bit2)); + BOOST_TEST(!combinedFlags.test(TestEnum::Bit3)); + + // Test operator~ (complement) + EFlags complement = ~EFlags(TestEnum::Bit1); + BOOST_TEST(!complement.test(TestEnum::Bit1)); + BOOST_TEST(complement.test(TestEnum::Bit2)); + BOOST_TEST(complement.test(TestEnum::Bit3)); + + // Test string() method + { + std::string flagString = flag1.string(); + BOOST_TEST(flagString.back() == '1'); // Ensure the least significant bit is set for flag1 + } + + // Test set with binary string + { + std::string binaryStr = "101"; + flags.set(binaryStr, 2); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(!flags.test(TestEnum::Bit2)); + BOOST_TEST(flags.test(TestEnum::Bit3)); + } + + // Test invalid binary string in set + BOOST_CHECK_THROW(flags.set(std::string("invalid"), 2), std::invalid_argument); + + // Test range validation in set + BOOST_CHECK_THROW(flags.set(std::string("100000000"), 2), std::out_of_range); + + { // Test that return lists are sensible + const auto n = flags.getNames(); + const auto v = flags.getValues(); + BOOST_CHECK(n.size() == v.size()); + } + + { // print test + std::cout << flags; + } + + // Test flag tokenization and parsing + { + { // only one scoped flag + std::string str = "TestEnum::Bit2"; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(flags.none_of(TestEnum::Bit1, TestEnum::Bit3, TestEnum::Bit4)); + } + + { // test with ws-triming and scope mixing + std::string str = "Bit4|TestEnum::Bit2 | Bit1 "; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(!flags.test(TestEnum::Bit3)); + BOOST_TEST(flags.test(TestEnum::Bit4)); + } + + { // test with , delimiter + std::string str = "Bit4,TestEnum::Bit2 , Bit1 "; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(!flags.test(TestEnum::Bit3)); + BOOST_TEST(flags.test(TestEnum::Bit4)); + } + + { // test with ; delimiter + std::string str = "Bit4;TestEnum::Bit2 ; Bit1 "; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(!flags.test(TestEnum::Bit3)); + BOOST_TEST(flags.test(TestEnum::Bit4)); + } + + { // throw test with mixed delimiter + std::string str = "Bit4|TestEnum::Bit2 , Bit1 "; + BOOST_CHECK_THROW(flags.set(str), std::invalid_argument); + } + + { // test throw + std::string str = "Invalid"; + BOOST_CHECK_THROW(flags.set(str), std::invalid_argument); + } + } + + // Test all_of and none_of + { + EFlags allFlags({TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}); + BOOST_TEST(allFlags.all_of(TestEnum::Bit1, TestEnum::Bit2)); + BOOST_TEST(!allFlags.all_of(TestEnum::Bit4)); + BOOST_TEST(allFlags.none_of(TestEnum::Bit4)); + } + + // Test toggle + { + EFlags toggleFlags; + toggleFlags.toggle(TestEnum::Bit4); + BOOST_TEST(toggleFlags.test(TestEnum::Bit4)); + toggleFlags.toggle(TestEnum::Bit4); + BOOST_TEST(!toggleFlags.test(TestEnum::Bit4)); + } + + // Create a flag set and serialize it + { + EFlags serializedFlags{TestEnum::Bit1, TestEnum::Bit3}; + std::string serialized = serializedFlags.serialize(); + BOOST_CHECK_EQUAL(serialized, "5"); // 5 in binary is 0101, meaning Bit1 and Bit3 are set. + + // Deserialize back into a flag set + EFlags deserializedFlags; + deserializedFlags.deserialize(serialized); + BOOST_CHECK(deserializedFlags == serializedFlags); // Ensure the deserialized flags match the original + } + + // Test with an empty flag set + { + EFlags emptyFlags; + std::string serialized = emptyFlags.serialize(); + BOOST_CHECK_EQUAL(serialized, "0"); + + EFlags deserialized; + deserialized.deserialize(serialized); + BOOST_CHECK(deserialized == emptyFlags); + + // Test with all flags set + EFlags allFlags(EFlags::All); + serialized = allFlags.serialize(); + BOOST_CHECK_EQUAL(serialized, std::to_string(EFlags::All)); + + deserialized.deserialize(serialized); + BOOST_CHECK(deserialized == allFlags); + } + + // check throw deserializng out of range + { + EFlags flag; + std::string str = "999999"; + BOOST_CHECK_THROW(flag.deserialize(str), std::out_of_range); + } + + // Create two flag sets + { + EFlags flags1{TestEnum::Bit1, TestEnum::Bit2}; + EFlags flags2{TestEnum::Bit3, TestEnum::Bit4}; + + // Perform a union operation + EFlags unionFlags = flags1.union_with(flags2); + BOOST_CHECK(unionFlags.test(TestEnum::Bit1)); + BOOST_CHECK(unionFlags.test(TestEnum::Bit2)); + BOOST_CHECK(unionFlags.test(TestEnum::Bit3)); + BOOST_CHECK(unionFlags.test(TestEnum::Bit4)); + BOOST_CHECK_EQUAL(unionFlags.value(), 15); // 1111 in binary + } + + // Create two overlapping flag sets + { + EFlags flags3{TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}; + EFlags flags4{TestEnum::Bit2, TestEnum::Bit3, TestEnum::Bit4}; + + // test xor + auto flagsXOR = flags3 ^ flags4; + BOOST_CHECK(flagsXOR.test(TestEnum::Bit1, TestEnum::Bit4)); + + // test and + auto flagsAND = flags3 & flags4; + BOOST_CHECK(flagsAND.test(TestEnum::Bit2, TestEnum::Bit3)); + + // Perform an intersection operation + EFlags intersectionFlags = flags3.intersection_with(flags4); + BOOST_CHECK(intersectionFlags.test(TestEnum::Bit2)); + BOOST_CHECK(intersectionFlags.test(TestEnum::Bit3)); + BOOST_CHECK(!intersectionFlags.test(TestEnum::Bit1)); + BOOST_CHECK(!intersectionFlags.test(TestEnum::Bit4)); + BOOST_CHECK_EQUAL(intersectionFlags.value(), 6); // 0110 in binary + } + + { + // Check special flag names. + EFlags flag("all"); + BOOST_CHECK(flag.all()); + flag.set("none"); + BOOST_CHECK(!flag.any()); + } + + { + // Create two flag sets + EFlags flags1{TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}; + EFlags flags2{TestEnum::Bit2, TestEnum::Bit3}; + + // Check containment + BOOST_CHECK(flags1.contains(flags2)); // flags1 contains all flags in flags2 + BOOST_CHECK(!flags2.contains(flags1)); // flags2 does not contain all flags in flags1 + + // Test with disjoint sets + EFlags flags3{TestEnum::Bit4}; + BOOST_CHECK(!flags1.contains(flags3)); // flags1 does not contain flags3 + } + + { + // Test compilation using an enum with more than 32 bits + // Also tests space delimiter and construction from string. + o2::utils::EnumFlags test("Bit32 Bit34"); + BOOST_CHECK(test.test(TestEnumLong::Bit32, TestEnumLong::Bit34)); + BOOST_CHECK(!test.test(TestEnumLong::Bit1, TestEnumLong::Bit23)); + } +} + +BOOST_AUTO_TEST_CASE(Flags_case_insensitive_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test case-insensitive flag names + { + EFlags flags("bit1"); // lowercase + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(!flags.test(TestEnum::Bit2)); + } + + { + EFlags flags("BIT2"); // uppercase + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(!flags.test(TestEnum::Bit1)); + } + + { + EFlags flags("BiT3"); // mixed case + BOOST_CHECK(flags.test(TestEnum::Bit3)); + } + + { + EFlags flags("bit1|BIT2|BiT3"); // mixed case with delimiter + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + } + + // Test special keywords case-insensitive + { + EFlags flags("ALL"); + BOOST_CHECK(flags.all()); + } + + { + EFlags flags("None"); + BOOST_CHECK(!flags.any()); + } +} + +BOOST_AUTO_TEST_CASE(Flags_error_recovery_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test that previous state is restored on exception + { + EFlags flags({TestEnum::Bit1, TestEnum::Bit2}); + auto previousValue = flags.value(); + + // Try to set with invalid string + BOOST_CHECK_THROW(flags.set("InvalidFlag"), std::invalid_argument); + + // Verify state was restored + BOOST_CHECK_EQUAL(flags.value(), previousValue); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + } + + { + EFlags flags({TestEnum::Bit3, TestEnum::Bit4}); + auto previousValue = flags.value(); + + // Try to set with out-of-range value + BOOST_CHECK_THROW(flags.set("999999", 10), std::out_of_range); + + // Verify state was restored + BOOST_CHECK_EQUAL(flags.value(), previousValue); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + BOOST_CHECK(flags.test(TestEnum::Bit4)); + } + + { + EFlags flags(TestEnum::Bit5VeryLongName); + auto previousValue = flags.value(); + + // Try to set with invalid binary string + BOOST_CHECK_THROW(flags.set("10102", 2), std::invalid_argument); + + // Verify state was restored + BOOST_CHECK_EQUAL(flags.value(), previousValue); + BOOST_CHECK(flags.test(TestEnum::Bit5VeryLongName)); + } +} + +BOOST_AUTO_TEST_CASE(Flags_whitespace_handling_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test leading/trailing whitespace + { + EFlags flags(" Bit1 "); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + } + + { + EFlags flags(" Bit1 | Bit2 "); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + } + + // Test excessive whitespace between flags + { + EFlags flags("Bit1 | Bit3"); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + BOOST_CHECK(!flags.test(TestEnum::Bit2)); + } + + // Test tabs and other whitespace (should work with space delimiter) + { + EFlags flags("Bit1 Bit2 Bit3"); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + } +} + +BOOST_AUTO_TEST_CASE(Flags_count_bits_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test counting set bits + { + EFlags flags; + BOOST_CHECK_EQUAL(flags.count(), 0); + } + + { + EFlags flags(TestEnum::Bit1); + BOOST_CHECK_EQUAL(flags.count(), 1); + } + + { + EFlags flags({TestEnum::Bit1, TestEnum::Bit2}); + BOOST_CHECK_EQUAL(flags.count(), 2); + } + + { + EFlags flags({TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3, TestEnum::Bit4}); + BOOST_CHECK_EQUAL(flags.count(), 4); + } + + { + EFlags flags(EFlags::All); + BOOST_CHECK_EQUAL(flags.count(), 5); // TestEnum has 5 members + } + + // Test count after operations + { + EFlags flags({TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}); + BOOST_CHECK_EQUAL(flags.count(), 3); + + flags.reset(TestEnum::Bit2); + BOOST_CHECK_EQUAL(flags.count(), 2); + + flags.set(TestEnum::Bit4); + BOOST_CHECK_EQUAL(flags.count(), 3); + + flags.toggle(TestEnum::Bit1); + BOOST_CHECK_EQUAL(flags.count(), 2); + } +} + +BOOST_AUTO_TEST_CASE(Flags_mixed_delimiter_validation_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test that mixed delimiters throw an error + { + BOOST_CHECK_THROW(EFlags("Bit1|Bit2,Bit3"), std::invalid_argument); + } + + { + BOOST_CHECK_THROW(EFlags("Bit1;Bit2|Bit3"), std::invalid_argument); + } + + { + BOOST_CHECK_THROW(EFlags("Bit1,Bit2;Bit3"), std::invalid_argument); + } + + { + BOOST_CHECK_THROW(EFlags("Bit1|Bit2,Bit3;Bit4"), std::invalid_argument); + } + + // Test that single delimiter types work + { + EFlags flags1("Bit1|Bit2|Bit3"); + BOOST_CHECK_EQUAL(flags1.count(), 3); + } + + { + EFlags flags2("Bit1,Bit2,Bit3"); + BOOST_CHECK_EQUAL(flags2.count(), 3); + } + + { + EFlags flags3("Bit1;Bit2;Bit3"); + BOOST_CHECK_EQUAL(flags3.count(), 3); + } +} + +BOOST_AUTO_TEST_CASE(Flags_empty_and_edge_cases_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test empty string + { + EFlags flags({TestEnum::Bit1, TestEnum::Bit2}); + flags.set(""); // Should be no-op + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + } + + // Test with only whitespace + { + EFlags flags({TestEnum::Bit1}); + flags.set(" "); // Should result in empty after tokenization + // Depending on implementation, this might clear or throw + // Adjust expectation based on actual behavior + } + + // Test duplicate flags (should work, setting same bit twice is idempotent) + { + EFlags flags("Bit1|Bit1|Bit1"); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK_EQUAL(flags.count(), 1); + } + + // Test scoped and unscoped mixed + { + EFlags flags("Bit1|TestEnum::Bit2"); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + } +} + +BOOST_AUTO_TEST_CASE(Flags_binary_decimal_parsing_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test binary parsing + { + EFlags flags("101", 2); + BOOST_CHECK(flags.test(TestEnum::Bit1)); // bit 0 + BOOST_CHECK(!flags.test(TestEnum::Bit2)); // bit 1 + BOOST_CHECK(flags.test(TestEnum::Bit3)); // bit 2 + } + + // Test decimal parsing + { + EFlags flags("7", 10); // 7 = 0b111 + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + BOOST_CHECK(!flags.test(TestEnum::Bit4)); + } + + // Test hexadecimal parsing + { + EFlags flags("F", 16); // 15 = 0b1111 + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + BOOST_CHECK(flags.test(TestEnum::Bit4)); + BOOST_CHECK(!flags.test(TestEnum::Bit5VeryLongName)); + } + + // Test hexadecimal with 0x prefix + { + EFlags flags("0xA", 16); // 10 = 0b1010 + BOOST_CHECK(!flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(!flags.test(TestEnum::Bit3)); + BOOST_CHECK(flags.test(TestEnum::Bit4)); + } + + // Test hexadecimal with 0X prefix (uppercase) + { + EFlags flags("0X1F", 16); // 31 = all 5 bits + BOOST_CHECK(flags.all()); + } + + // Test lowercase hex digits + { + EFlags flags("0xa", 16); + BOOST_CHECK_EQUAL(flags.value(), 10); + } + + // Test thros + { + BOOST_CHECK_THROW(EFlags("0xAbCd", 16), std::out_of_range); + } + + // Test invalid binary string (contains 2) + { + BOOST_CHECK_THROW(EFlags("1012", 2), std::invalid_argument); + } + + // Test out of range for base + { + BOOST_CHECK_THROW(EFlags("100000", 2), std::out_of_range); + } +} + +BOOST_AUTO_TEST_CASE(Flags_operator_bool_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test explicit bool conversion + { + EFlags empty; + BOOST_CHECK(!static_cast(empty)); + } + + { + EFlags withFlag(TestEnum::Bit1); + BOOST_CHECK(static_cast(withFlag)); + } + + // Test in conditional + { + EFlags flags; + if (flags) { + BOOST_FAIL("Empty flags should be false"); + } + + flags.set(TestEnum::Bit1); + if (!flags) { + BOOST_FAIL("Non-empty flags should be true"); + } + } +} diff --git a/Common/Utils/test/testMemFileHelper.cxx b/Common/Utils/test/testMemFileHelper.cxx index 2a55d67b9884a..79fa7e86f96e9 100644 --- a/Common/Utils/test/testMemFileHelper.cxx +++ b/Common/Utils/test/testMemFileHelper.cxx @@ -40,3 +40,35 @@ BOOST_AUTO_TEST_CASE(test_memfile_helper) BOOST_CHECK(rvec); BOOST_CHECK(*rvec == vec); } + +#include "CommonUtils/FileSystemUtils.h" +#include + +BOOST_AUTO_TEST_CASE(test_expandenv) +{ + { + std::string noenv("simple_file.root"); + auto expandedFileName = o2::utils::expandShellVarsInFileName(noenv); + BOOST_CHECK(expandedFileName.size() > 0); + BOOST_CHECK_EQUAL(expandedFileName, noenv); + } + + { + std::string withenv("${PWD}/simple_file.root"); + auto expandedFileName = o2::utils::expandShellVarsInFileName(withenv); + BOOST_CHECK(expandedFileName.size() > 0); + } + + { + setenv("FOO_123", "BAR", 0); + std::string withenv("/tmp/${FOO_123}/simple_file.root"); + auto expandedFileName = o2::utils::expandShellVarsInFileName(withenv); + BOOST_CHECK_EQUAL(expandedFileName, "/tmp/BAR/simple_file.root"); + } + + { // what if the variable doesn't exist --> should return unmodified string + std::string withenv("/tmp/${FOO_DOESNOTEXIST}/simple_file.root"); + auto expandedFileName = o2::utils::expandShellVarsInFileName(withenv); + BOOST_CHECK_EQUAL(expandedFileName, withenv); + } +} \ No newline at end of file diff --git a/Common/Utils/test/testTreeStream.cxx b/Common/Utils/test/testTreeStream.cxx index 7ff6f165a1d37..2491fea7f6efd 100644 --- a/Common/Utils/test/testTreeStream.cxx +++ b/Common/Utils/test/testTreeStream.cxx @@ -53,12 +53,28 @@ BOOST_AUTO_TEST_CASE(TreeStream_test) tstStream << "TrackTreeR" << "id=" << i << "x=" << x << "track=" << trc << "\n"; } + + // test for c-arrays + int iArray[6] = {1, 2, 3, 4, 5, 6}; + float fArray[6] = {1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f}; + for (int i{0}; i < nit; ++i) { + for (int j{0}; j < 6; ++j) { + iArray[j] += i; + fArray[j] += (float)i; + } + tstStream << "ArrayTree" + << "id=" << i + << "iArray[6]=" << iArray + << "fArray[6]=" << fArray + << "\n"; + } + // on destruction of tstTreem the trees will be stored, but we can also force it by tstStream.Close(); } // - LOG(info) << "Testing reading back tree maid by the TreeStream "; - // read back tracks + LOG(info) << "Testing reading back tree made by the TreeStream "; + // read back tracks and arrays { TFile inpf(outFName.data()); BOOST_CHECK(!inpf.IsZombie()); @@ -80,6 +96,27 @@ BOOST_AUTO_TEST_CASE(TreeStream_test) trc->printParam(); BOOST_CHECK(std::abs(x - trc->getX()) < 1e-4); } + + // check arrays + tree = (TTree*)inpf.GetObjectChecked("ArrayTree", "TTree"); + BOOST_CHECK(tree); + nent = tree->GetEntries(); + BOOST_CHECK(nent == nit); + int iArray[6]; + float fArray[6]; + BOOST_CHECK(!tree->SetBranchAddress("id", &id)); + BOOST_CHECK(!tree->SetBranchAddress("iArray", iArray)); + BOOST_CHECK(!tree->SetBranchAddress("fArray", fArray)); + for (int i = 0; i < nit; i++) { + BOOST_CHECK(tree->GetEntry(i) > 0); + BOOST_CHECK(id == i); + for (int j = 0; j < 6; j++) { + BOOST_CHECK(iArray[j] == (1 + j + i * (i + 1) / 2)); + } + for (int j = 0; j < 6; j++) { + BOOST_CHECK_CLOSE(fArray[j], (1.f + j + i * (i + 1) / 2.f + 0.1 * (j + 1)), 1e-5); + } + } } LOG(info) << "Testing loading tree via RootChain"; @@ -104,7 +141,6 @@ BOOST_AUTO_TEST_CASE(TreeStream_test) nit = 1000; BOOST_CHECK(UnitTestSparse(0.5, nit)); BOOST_CHECK(UnitTestSparse(0.1, nit)); - // } //_________________________________________________ diff --git a/DEBUGGING.md b/DEBUGGING.md index cda0d2a94ed81..c782b99987ef9 100644 --- a/DEBUGGING.md +++ b/DEBUGGING.md @@ -1,3 +1,7 @@ + + # How do I run in debug mode? By default, O2 builds with optimizations (`-O2`) turned on, while leaving debug symbols available. diff --git a/DataFormats/Calibration/CMakeLists.txt b/DataFormats/Calibration/CMakeLists.txt index 7c905ac350fab..c9a05747c20c0 100644 --- a/DataFormats/Calibration/CMakeLists.txt +++ b/DataFormats/Calibration/CMakeLists.txt @@ -11,9 +11,12 @@ o2_add_library(DataFormatsCalibration SOURCES src/MeanVertexObject.cxx + SOURCES src/MeanVertexBiasParam.cxx PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats - O2::Framework) + O2::Framework + O2::CommonUtils) o2_target_root_dictionary(DataFormatsCalibration - HEADERS include/DataFormatsCalibration/MeanVertexObject.h) + HEADERS include/DataFormatsCalibration/MeanVertexObject.h + include/DataFormatsCalibration/MeanVertexBiasParam.h) diff --git a/DataFormats/Calibration/include/DataFormatsCalibration/MeanVertexBiasParam.h b/DataFormats/Calibration/include/DataFormatsCalibration/MeanVertexBiasParam.h new file mode 100644 index 0000000000000..19b08fc2532e8 --- /dev/null +++ b/DataFormats/Calibration/include/DataFormatsCalibration/MeanVertexBiasParam.h @@ -0,0 +1,38 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author ruben.shahoyan@cern.ch + +/// parameters to bias precalibrated mean vertex, e.g after the alignment shift + +#ifndef ALICEO2_MEANVERTEX_BIAS_PARAM_H +#define ALICEO2_MEANVERTEX_BIAS_PARAM_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace dataformats +{ + +struct MeanVertexBiasParam : public o2::conf::ConfigurableParamHelper { + float xyz[3] = {}; // position bias + float slopeX = 0.f; // x slope bias + float slopeY = 0.f; // y slope bias + + O2ParamDef(MeanVertexBiasParam, "mvbias"); +}; + +} // namespace dataformats +} // end namespace o2 + +#endif diff --git a/DataFormats/Calibration/include/DataFormatsCalibration/MeanVertexObject.h b/DataFormats/Calibration/include/DataFormatsCalibration/MeanVertexObject.h index 46c885ae1a18d..96cece929a745 100644 --- a/DataFormats/Calibration/include/DataFormatsCalibration/MeanVertexObject.h +++ b/DataFormats/Calibration/include/DataFormatsCalibration/MeanVertexObject.h @@ -15,6 +15,7 @@ #include #include "Framework/Logger.h" #include "ReconstructionDataFormats/Vertex.h" +#include "DataFormatsCalibration/MeanVertexBiasParam.h" namespace o2 { @@ -22,24 +23,37 @@ namespace dataformats { class MeanVertexObject : public VertexBase { - public: MeanVertexObject(float x, float y, float z, float sigmax, float sigmay, float sigmaz, float slopeX, float slopeY) { + if (!gMVBias) { + checkExternalBias(); + } setXYZ(x, y, z); setSigma({sigmax, sigmay, sigmaz}); mSlopeX = slopeX; mSlopeY = slopeY; } + MeanVertexObject(std::array pos, std::array sigma, float slopeX, float slopeY) { + if (!gMVBias) { + checkExternalBias(); + } math_utils::Point3D p(pos[0], pos[1], pos[2]); setPos(p); setSigma(sigma); mSlopeX = slopeX; mSlopeY = slopeY; } - MeanVertexObject() = default; + + MeanVertexObject() + { + if (!gMVBias) { + checkExternalBias(); + } + } + ~MeanVertexObject() = default; MeanVertexObject(const MeanVertexObject& other) = default; MeanVertexObject(MeanVertexObject&& other) = default; @@ -57,14 +71,28 @@ class MeanVertexObject : public VertexBase void setSlopeX(float val) { mSlopeX = val; } void setSlopeY(float val) { mSlopeY = val; } - math_utils::Point3D& getPos() { return getXYZ(); } + // getting the cartesian coordinates and errors + float getX() const { return VertexBase::getX() + gMVBias->xyz[0]; } + float getY() const + { + return VertexBase::getY() + gMVBias->xyz[1]; + ; + } + float getZ() const + { + return VertexBase::getZ() + gMVBias->xyz[2]; + ; + } + float getR() const { return gpu::CAMath::Hypot(getX(), getY()); } + + math_utils::Point3D getXYZ() const { return {getX(), getY(), getZ()}; } math_utils::Point3D getPos() const { return getXYZ(); } - float getSlopeX() const { return mSlopeX; } - float getSlopeY() const { return mSlopeY; } + float getSlopeX() const { return mSlopeX + gMVBias->slopeX; } + float getSlopeY() const { return mSlopeY + gMVBias->slopeY; } - float getXAtZ(float z) const { return getX() + mSlopeX * (z - getZ()); } - float getYAtZ(float z) const { return getY() + mSlopeY * (z - getZ()); } + float getXAtZ(float z) const { return getX() + getSlopeX() * (z - getZ()); } + float getYAtZ(float z) const { return getY() + getSlopeY() * (z - getZ()); } void print() const; std::string asString() const; @@ -82,21 +110,25 @@ class MeanVertexObject : public VertexBase void setMeanXYVertexAtZ(VertexBase& v, float z) const { - float dz = z - getZ(); - v.setX(getX() + mSlopeX * dz); - v.setY(getY() + mSlopeY * dz); + v = *this; + v.setX(getXAtZ(z)); + v.setY(getYAtZ(z)); v.setZ(z); } - const VertexBase& getMeanVertex() const + const VertexBase getMeanVertex() const { - return (const VertexBase&)(*this); + return getMeanVertex(getZ()); } + static void checkExternalBias(); + private: float mSlopeX{0.f}; // slope of x = f(z) float mSlopeY{0.f}; // slope of y = f(z) + static const MeanVertexBiasParam* gMVBias; + ClassDefNV(MeanVertexObject, 2); }; diff --git a/DataFormats/Calibration/src/DataFormatsCalibrationLinkDef.h b/DataFormats/Calibration/src/DataFormatsCalibrationLinkDef.h index fa8a44a2f12fd..cda970ac395e7 100644 --- a/DataFormats/Calibration/src/DataFormatsCalibrationLinkDef.h +++ b/DataFormats/Calibration/src/DataFormatsCalibrationLinkDef.h @@ -15,6 +15,8 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ struct o2::dataformats::MeanVertexObject + ; +#pragma link C++ class o2::dataformats::MeanVertexObject + ; +#pragma link C++ class o2::dataformats::MeanVertexBiasParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::dataformats::MeanVertexBiasParam> + ; #endif diff --git a/DataFormats/Calibration/src/MeanVertexBiasParam.cxx b/DataFormats/Calibration/src/MeanVertexBiasParam.cxx new file mode 100644 index 0000000000000..4eb1b82fab609 --- /dev/null +++ b/DataFormats/Calibration/src/MeanVertexBiasParam.cxx @@ -0,0 +1,18 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author ruben.shahoyan@cern.ch + +/// parameters to bias precalibrated mean vertex, e.g after the alignment shift + +#include "DataFormatsCalibration/MeanVertexBiasParam.h" + +O2ParamImpl(o2::dataformats::MeanVertexBiasParam); diff --git a/DataFormats/Calibration/src/MeanVertexObject.cxx b/DataFormats/Calibration/src/MeanVertexObject.cxx index 167be39b5e1bd..7ffd71ad294c8 100644 --- a/DataFormats/Calibration/src/MeanVertexObject.cxx +++ b/DataFormats/Calibration/src/MeanVertexObject.cxx @@ -12,10 +12,13 @@ #include "DataFormatsCalibration/MeanVertexObject.h" #include "TRandom.h" +#include + namespace o2 { namespace dataformats { +const MeanVertexBiasParam* MeanVertexObject::gMVBias = nullptr; void MeanVertexObject::set(int icoord, float val) { @@ -45,7 +48,9 @@ void MeanVertexObject::setSigma(int icoord, float val) std::string MeanVertexObject::asString() const { - return VertexBase::asString() + fmt::format(" Slopes {{{:+.4e},{:+.4e}}}", mSlopeX, mSlopeY); + return fmt::format("Vtx {{{:+.4e},{:+.4e},{:+.4e}}} Cov.:{{{{{:.3e}..}},{{{:.3e},{:.3e}..}},{{{:.3e},{:.3e},{:.3e}}}}} | bias: XYZ: {:.4f},{:.4f},{:.4f} SlopeXY: {:.3e},{:.3e}", + getX(), getY(), getZ(), mCov[0], mCov[1], mCov[2], mCov[3], mCov[4], mCov[5], + gMVBias->xyz[0], gMVBias->xyz[1], gMVBias->xyz[2], gMVBias->slopeX, gMVBias->slopeY); } std::ostream& operator<<(std::ostream& os, const o2::dataformats::MeanVertexObject& o) @@ -70,5 +75,16 @@ math_utils::Point3D MeanVertexObject::sample() const return math_utils::Point3D(x, y, z); } +void MeanVertexObject::checkExternalBias() +{ + // posibility to globally bias all data members with the proper env.var + if (const auto* biasString = std::getenv("O2_DPL_MVBIAS"); biasString && *biasString) { + o2::conf::ConfigurableParam::updateFromString(biasString); + } + gMVBias = &MeanVertexBiasParam::Instance(); + LOGP(info, "Mean vertex is biased by: XYZ: {:.4f},{:.4f},{:.4f} SlopeXY: {:.3e},{:.3e}", + gMVBias->xyz[0], gMVBias->xyz[1], gMVBias->xyz[2], gMVBias->slopeX, gMVBias->slopeY); +} + } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h b/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h index 975279b7dadfd..2f8a88072a43d 100644 --- a/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h +++ b/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h @@ -115,20 +115,21 @@ class CpvTrailer } } CpvTrailer(unsigned short wordCounter, uint16_t bunchCrossing, bool isAllDataSent) - { // writing - mBytes[0] = bunchCrossing & 0x00ff; // bits 11 - 0 bunch crossing - mBytes[1] = ((bunchCrossing & 0x0f00) >> 8) // bits 11 - 0 bunch crossing - + ((wordCounter & 0x0f) << 4); // bits 20 - 12 wordCounter - mBytes[2] = (wordCounter & 0b111110000) >> 4; // bits 20 - 12 wordCounter + { // writing + mBytes[0] = bunchCrossing & 0x00ff; // bits 11 - 0 bunch crossing + mBytes[1] = ((bunchCrossing & 0x0f00) >> 8) // bits 11 - 0 bunch crossing + + ((wordCounter & 0x0f) << 4); // bits 20 - 12 wordCounter + mBytes[2] = (wordCounter & 0b1111110000) >> 4; // bits 21 - 12 wordCounter + for (int i = 3; i < 8; i++) { - mBytes[i] = 0; // bits 70 - 21 reserved + mBytes[i] = 0; // bits 70 - 22 reserved } mBytes[8] = isAllDataSent * 0b10000000; // bit 71 all data is sent for current trigger mBytes[9] = char(0xf0); // word ID of cpv trailer } ~CpvTrailer() = default; bool isOK() const { return (mBytes[9] == 0xf0); } - uint16_t wordCounter() const { return (mBytes[1] >> 4) + ((mBytes[2] & 0b00011111) << 4); } + uint16_t wordCounter() const { return (mBytes[1] >> 4) + ((mBytes[2] & 0b00111111) << 4); } bool isAllDataSent() const { return (mBytes[8] & 0b10000000); } uint16_t bc() const { return mBytes[0] + ((mBytes[1] & 0x0f) << 8); } diff --git a/DataFormats/Detectors/CTP/CMakeLists.txt b/DataFormats/Detectors/CTP/CMakeLists.txt index c9e8a371e4eb1..37c5af7b07b6e 100644 --- a/DataFormats/Detectors/CTP/CMakeLists.txt +++ b/DataFormats/Detectors/CTP/CMakeLists.txt @@ -8,17 +8,19 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. - o2_add_library(DataFormatsCTP SOURCES src/Digits.cxx src/Configuration.cxx src/Scalers.cxx src/CTF.cxx src/TriggerOffsetsParam.cxx + src/LumiInfo.cxx + src/CTPRateFetcher.cxx PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers - O2::SimulationDataFormat O2::CommonUtils + O2::DetectorsCommonDataFormats + O2::DataFormatsParameters O2::CommonConstants) o2_target_root_dictionary(DataFormatsCTP HEADERS include/DataFormatsCTP/Digits.h @@ -26,5 +28,6 @@ o2_target_root_dictionary(DataFormatsCTP include/DataFormatsCTP/Configuration.h include/DataFormatsCTP/Scalers.h include/DataFormatsCTP/LumiInfo.h + include/DataFormatsCTP/CTPRateFetcher.h include/DataFormatsCTP/TriggerOffsetsParam.h) diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h index 8481db8f6d7e5..3635b8ede44db 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h @@ -29,13 +29,16 @@ namespace ctp struct CTFHeader : public o2::ctf::CTFDictHeader { uint64_t lumiCounts = 0; /// FT0 Luminosity counts moving average over lumiNHBFs orbits uint64_t lumiCountsFV0 = 0; /// FV0 Luminosity counts moving average over lumiNHBFs orbits - uint32_t lumiNHBFs = 0; /// Number of HBFs over which lumi is integrated - uint32_t lumiOrbit = 0; /// 1st orbit of TF where lumi was updated, can be compared with firstOrbit - uint32_t nTriggers = 0; /// number of triggers - uint32_t firstOrbit = 0; /// orbit of 1st trigger - uint16_t firstBC = 0; /// bc of 1st trigger - - ClassDefNV(CTFHeader, 3); + uint32_t lumiNHBFs = 0; /// Number of HBFs over which lumi is integrated + uint32_t lumiNHBFsFV0 = 0; /// Number of FV0 HBFs over which lumi is integrated + uint32_t lumiOrbit = 0; /// 1st orbit of TF where lumi was updated, can be compared with firstOrbit + uint32_t nTriggers = 0; /// number of triggers + uint32_t firstOrbit = 0; /// orbit of 1st trigger + uint16_t firstBC = 0; /// bc of 1st trigger + uint16_t inp1 = 0; /// lumiCounts input ID + uint16_t inp2 = 0; /// lumiCountsFV0 input ID + + ClassDefNV(CTFHeader, 5); }; /// wrapper for the Entropy-encoded trigger inputs and classes of the TF diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTPRateFetcher.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTPRateFetcher.h new file mode 100644 index 0000000000000..78c4245b16b20 --- /dev/null +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTPRateFetcher.h @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef COMMON_CCDB_CTPRATEFETCHER_H_ +#define COMMON_CCDB_CTPRATEFETCHER_H_ + +#include + +#include "DataFormatsParameters/GRPLHCIFData.h" +#include "DataFormatsCTP/Configuration.h" +#include "DataFormatsCTP/Scalers.h" + +namespace o2::ccdb +{ +class BasicCCDBManager; +} + +namespace o2::ctp +{ + +class CTPRateFetcher +{ + public: + CTPRateFetcher() = default; + double fetch(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, const std::string sourceName); + double fetchNoPuCorr(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, const std::string sourceName); + void setupRun(int runNumber, o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, bool initScalers); + void updateScalers(ctp::CTPRunScalers& scalers); + int getRates(std::array& rates, o2::ccdb::BasicCCDBManager* ccdb, int runNumber, const std::string sourceName); // rates at start,stop and middle of the run + double getLumi(o2::ccdb::BasicCCDBManager* ccdb, int runNumber, const std::string sourceName, int puCorr = 0); // total lumi for a run + double getLumiNoPuCorr(const std::string& classname, int type = 1); + double getLumiWPuCorr(const std::string& classname, int type = 1); + void setOrbit(bool orb) { mOrbit = orb; } // use orbit instead of time + void setOutsideLimits(bool qc) { mOutsideLimits = qc; } // return first/last rate of time outside of run + + private: + double fetchCTPratesInputs(uint64_t timeStamp, int input); + double fetchCTPratesClasses(uint64_t timeStamp, const std::string& className, int inputType = 1); + double fetchCTPratesInputsNoPuCorr(uint64_t timeStamp, int input); + double fetchCTPratesClassesNoPuCorr(uint64_t timeStamp, const std::string& className, int inputType = 1); + double getLumi(const std::string& classname, int type = 1, int puCorr = 0); + double pileUpCorrection(double rate); + int mRunNumber = -1; + bool mOutsideLimits = 0; + bool mOrbit = 0; + o2::ctp::CTPConfiguration mConfig{}; + o2::ctp::CTPRunScalers mScalers{}; + o2::parameters::GRPLHCIFData mLHCIFdata{}; + ClassDefNV(CTPRateFetcher, 1); +}; +} // namespace o2::ctp + + +#endif // COMMON_CCDB_CTPRATEFETCHER_H_ diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h index 2708fc72f0cd4..ff1462084d53d 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include namespace o2 { namespace ctp @@ -39,6 +39,7 @@ struct BCMask { std::string name = ""; std::string mask = ""; std::bitset BCmask; + int setBCmask(std::vector& tokens); void printStream(std::ostream& stream) const; ClassDefNV(BCMask, 1); }; @@ -62,7 +63,7 @@ struct CTPInput { std::uint64_t inputMask = 0; o2::detectors::DetID::ID detID = 16; // CTP bool neg = 1; - int getIndex() const { return ((inputMask > 0) ? 1 + log2(inputMask) : 0xff); } + uint32_t getIndex() const { return ((inputMask > 0) ? 1 + log2(inputMask) : 0xff); } std::string getInputDetName() const { return o2::detectors::DetID::getName(detID); } void setRun3DetName(std::string& run2Name); void printStream(std::ostream& strem) const; @@ -107,7 +108,7 @@ struct CTPClass { std::uint64_t classMask = 0; CTPDescriptor const* descriptor = nullptr; CTPCluster const* cluster = nullptr; - int clusterIndex = 0; + int clusterIndex = 0xff; int descriptorIndex = 0xff; uint32_t downScale = 1; std::vector BCClassMask; @@ -118,11 +119,13 @@ struct CTPClass { struct CTPInputsConfiguration { CTPInputsConfiguration() = default; std::vector CTPInputs; + static const std::vector CTPInputsDefault; int createInputsConfigFromFile(std::string& filename); void printStream(std::ostream& strem) const; static CTPInputsConfiguration defaultInputConfig; static void initDefaultInputConfig(); - static std::string getInputNameFromIndex(int index); + static std::string getInputNameFromIndex100(uint32_t index); + static std::string getInputNameFromIndex(uint32_t index); static int getInputIndexFromName(std::string& name); ClassDefNV(CTPInputsConfiguration, 0); }; @@ -137,10 +140,12 @@ class CTPConfiguration static bool isNumber(const std::string& s); int addInput(std::string& inp, int clsindex, std::map>& descInputsIndex); enum ConfigPart { START, + VERSION, RUN, INPUTS, MASKS, GENS, + DESCRIPTORS, LTG, LTGitems, CLUSTER, @@ -148,35 +153,44 @@ class CTPConfiguration UNKNOWN }; int loadConfigurationRun3(const std::string& ctpconfiguartion); void printStream(std::ostream& stream) const; + void setRunNumber(uint32_t runnumber) { mRunNumber = runnumber; } std::vector& getCTPInputs() { return mInputs; } std::vector& getCTPClasses() { return mCTPClasses; } const std::vector& getCTPInputs() const { return mInputs; } // Read-only interface const std::vector& getCTPClasses() const { return mCTPClasses; } // Read-only interface uint64_t getInputMask(const std::string& name) const; int getInputIndex(const std::string& name) const; + std::string getClassNameFromIndex(int index); + std::string getClassNameFromHWIndex(int index); + const CTPClass* getCTPClassFromHWIndex(const int index) const; bool isMaskInInputs(const uint64_t& mask) const; bool isBCMaskInConfig(const std::string maskname) const; + const BCMask* isBCMaskInConfigP(const std::string bcmask) const; const CTPInput* isInputInConfig(const std::string inpname) const; - const CTPInput* isInputInConfig(const int index) const; + const CTPInput* isInputInConfig(const uint32_t index) const; + const CTPDescriptor* isDescriptorInConfig(const std::string descname, int& index) const; void createInputsInDecriptorsFromNames(); uint64_t getDecrtiptorInputsMask(const std::string& name) const; std::map> getDet2InputMap(); uint64_t getTriggerClassMask() const; + uint64_t getTriggerClassMaskWInputs() const; + uint64_t getTriggerClassMaskWInputsNoTrgDets() const; std::vector getTriggerClassList() const; uint32_t getRunNumber() { return mRunNumber; }; std::vector getDetectorList() const; o2::detectors::DetID::mask_t getDetectorMask() const; uint64_t getClassMaskForInputMask(uint64_t inputMask) const; - void printConfigString() { std::cout << mConfigString << std::endl; }; + void printConfigString(); std::string getConfigString() { return mConfigString; }; CTPDescriptor* getDescriptor(int index) { return &mDescriptors[index]; }; int assignDescriptors(); + int checkConfigConsistency() const; private: std::string mConfigString = ""; uint32_t mRunNumber = 0; std::string mName = ""; - std::string mVersion = "1"; + std::string mVersion = "0"; std::vector mBCMasks; std::vector mGenerators; std::vector mInputs; @@ -185,52 +199,24 @@ class CTPConfiguration std::vector mClusters; std::vector mCTPClasses; int processConfigurationLineRun3(std::string& line, int& level, std::map>& descInputsIndex); + int processConfigurationLineRun3v2(std::string& line, int& level, std::map>& descInputsIndex); ClassDefNV(CTPConfiguration, 6); }; -// Run Manager -struct CTPActiveRun { - CTPActiveRun() = default; - long timeStart; - long timeStop; - CTPConfiguration cfg; - CTPRunScalers scalers; -}; -class CTPRunManager -{ - public: - CTPRunManager() = default; - void init(); - int loadRun(const std::string& cfg); - int startRun(const std::string& cfg); - int stopRun(uint32_t irun, long timeStamp); - int addScalers(uint32_t irun, std::time_t time); - int processMessage(std::string& topic, const std::string& message); - void printActiveRuns() const; - int saveRunScalersToCCDB(int i); - int saveRunConfigToCCDB(CTPConfiguration* cfg, long timeStart); - static CTPConfiguration getConfigFromCCDB(long timestamp, std::string run); - CTPRunScalers getScalersFromCCDB(long timestamp, std::string, bool& ok); - int loadScalerNames(); - // void setCCDBPathConfig(std::string path) { mCCDBPathCTPConfig = path;}; - void setCCDBPathScalers(std::string path) { mCCDBPathCTPScalers = path; }; - static void setCCDBHost(std::string host) { mCCDBHost = host; }; - void printCounters(); - private: - /// Database constants - // std::string mCCDBHost = "http://ccdb-test.cern.ch:8080"; - static std::string mCCDBHost; - std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; - std::array mActiveRuns; - std::array mActiveRunNumbers; - std::array mCounters; - std::map mScalerName2Position; - std::map mRunsLoaded; - int mEOX = 0; // redundancy check - int mNew = 1; // 1 - no CCDB: used for QC - ClassDefNV(CTPRunManager, 5); -}; +std::ostream& operator<<(std::ostream& in, const CTPConfiguration& conf); +struct CtpCfg { + CtpCfg() = default; + std::string filename = "ctp.cfg"; + int readAndSave(std::string& path); + uint32_t TFOrbits = 0; + int ccdb = -1; // -1 means def constructor was called + uint32_t orbitShift = 0; + uint32_t irInputs_1_24 = 0; + uint32_t irInputs_25_48 = 0; + std::vector listOfUsedInputs(); + ClassDefNV(CtpCfg, 2) +}; } // namespace ctp } // namespace o2 #endif //_CTP_CONFIGURATION_H_ diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Digits.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Digits.h index 6514f344b0ea3..f52e63c24f36e 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Digits.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Digits.h @@ -51,10 +51,12 @@ struct CTPDigit { o2::InteractionRecord intRecord; std::bitset CTPInputMask; std::bitset CTPClassMask; - CTPDigit() = default; void printStream(std::ostream& stream) const; void setInputMask(gbtword80_t mask); void setClassMask(gbtword80_t mask); + bool isInputEmpty() const { return CTPInputMask.count() == 0; } + bool isClassEmpty() const { return CTPClassMask.count() == 0; } + bool isEmpty() const { return isInputEmpty() && isClassEmpty(); } bool operator==(const CTPDigit& d) const { return intRecord == d.intRecord && CTPInputMask == d.CTPInputMask && CTPClassMask == d.CTPClassMask; @@ -69,7 +71,6 @@ struct CTPInputDigit { o2::InteractionRecord intRecord; std::bitset inputsMask; o2::detectors::DetID::ID detector; - CTPInputDigit() = default; ClassDefNV(CTPInputDigit, 1) }; } // namespace ctp diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h index 27f67ba6e92a8..c75fcc32ddaf4 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h @@ -13,6 +13,7 @@ #define _ALICEO2_CTP_LUMIINFO_H_ #include "CommonConstants/LHCConstants.h" #include +#include /// \brief Luminosity information as a moving average over certain number of TFs @@ -21,17 +22,21 @@ namespace o2 namespace ctp { struct LumiInfo { - LumiInfo() = default; uint32_t orbit = 0; // orbit of TF when was updated uint32_t nHBFCounted = 0; // length of interval in HB uint32_t nHBFCountedFV0 = 0; uint64_t counts = 0; // counts in the interval for the nominal lumi detector (FT0) uint64_t countsFV0 = 0; // connts for FV0 (less reliable) + int inp1 = 3; // TVX + int inp2 = 6; // VBA float getLumi() const { return nHBFCounted > 0 ? float(counts / (nHBFCounted * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } float getLumiFV0() const { return nHBFCountedFV0 > 0 ? float(countsFV0 / (nHBFCountedFV0 * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } + float getLumiAlt() const { return getLumiFV0(); } float getLumiError() const { return nHBFCounted > 0 ? float(std::sqrt(counts) / (nHBFCounted * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } float getLumiFV0Error() const { return nHBFCountedFV0 > 0 ? float(std::sqrt(countsFV0) / (nHBFCountedFV0 * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } - ClassDefNV(LumiInfo, 2); + float getLumiAltError() const { return getLumiFV0Error(); } + void printInputs() const; + ClassDefNV(LumiInfo, 3); }; } // namespace ctp diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h index cef39358d6acb..45d54b034f8d9 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h @@ -33,6 +33,7 @@ struct errorCounters { void printStream(std::ostream& stream) const; uint32_t lmB = 0, l0B = 0, l1B = 0, lmA = 0, l0A = 0, l1A = 0; // decreasing counters uint32_t lmBlmA = 0, lmAl0B = 0, l0Bl0A = 0, l0Al1B = 0, l1Bl1A = 0; // between levels countres + uint32_t lmBlmAd1 = 0, lmAl0Bd1 = 0, l0Bl0Ad1 = 0, l0Al1Bd1 = 0, l1Bl1Ad1 = 0; // between levels countres - diff =1 - just warning uint32_t MAXPRINT = 3; }; struct CTPScalerRaw { @@ -67,31 +68,44 @@ struct CTPScalerRecordRaw { o2::InteractionRecord intRecord; double_t epochTime; std::vector scalers; - std::vector scalersDets; + // std::vector scalersDets; + std::vector scalersInps; void printStream(std::ostream& stream) const; - ClassDefNV(CTPScalerRecordRaw, 3); + ClassDefNV(CTPScalerRecordRaw, 4); }; struct CTPScalerRecordO2 { CTPScalerRecordO2() = default; o2::InteractionRecord intRecord; double_t epochTime; std::vector scalers; - std::vector scalersDets; + // std::vector scalersDets; + std::vector scalersInps; void printStream(std::ostream& stream) const; void printFromZero(std::ostream& stream, CTPScalerRecordO2& record0) const; - ClassDefNV(CTPScalerRecordO2, 3); + ClassDefNV(CTPScalerRecordO2, 4); }; class CTPRunScalers { public: + // + // static constexpr uint32_t NCOUNTERS = 1052; + // v1 + // static constexpr uint32_t NCOUNTERS = 1070; + // v2 - orbitid added at the end + static constexpr uint32_t NCOUNTERSv2 = 1071; + static constexpr uint32_t NCOUNTERS = 1085; + static std::vector scalerNames; CTPRunScalers() = default; - void printStream(std::ostream& stream) const; void printO2(std::ostream& stream) const; void printFromZero(std::ostream& stream) const; void printClasses(std::ostream& stream) const; std::vector getClassIndexes() const; + uint32_t getRunNumber() { return mRunNumber; }; + int getScalerIndexForClass(uint32_t cls) const; std::vector& getScalerRecordO2() { return mScalerRecordO2; }; + std::vector& getScalerRecordRaw() { return mScalerRecordRaw; }; + void setEpochTime(std::time_t tt, int index) { mScalerRecordRaw[index].epochTime = tt; }; int readScalers(const std::string& rawscalers); int convertRawToO2(); int checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& scal1, errorCounters& eCnts) const; @@ -100,16 +114,59 @@ class CTPRunScalers void setDetectorMask(o2::detectors::DetID::mask_t mask) { mDetectorMask = mask; }; void setRunNumber(uint32_t rnumber) { mRunNumber = rnumber; }; void addScalerRacordRaw(CTPScalerRecordRaw& scalerrecordraw) { mScalerRecordRaw.push_back(scalerrecordraw); }; - uint32_t getRunNUmber() { return mRunNumber; }; int printRates(); int printIntegrals(); + int printInputRateAndIntegral(int inp); + int printClassBRateAndIntegralII(int icls); + int printClassBRateAndIntegral(int iclsinscalers); // - // static constexpr uint32_t NCOUNTERS = 1052; - // v1 - // static constexpr uint32_t NCOUNTERS = 1070; - // v2 - orbitid added at the end - static constexpr uint32_t NCOUNTERS = 1071; - static std::vector scalerNames; + int addOrbitOffset(uint32_t offset); + // + void printLMBRateVsT() const; // prints LMB interaction rate vs time for debugging + // returns the pair of global (levelled) interaction rate, as well as interpolated + // rate in Hz at a certain orbit number within the run + std::pair getRate(uint32_t orbit, int classindex, int type, bool qc = 0) const; + + /// same with absolute timestamp (not orbit) as argument + std::pair getRateGivenT(double timestamp, int classindex, int type, bool qc = 0) const; + + /// retrieves integral for class + std::array getIntegralForClass(int i) const + { + return { + mScalerRecordO2[0].scalers[i].classIndex, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].lmBefore - mScalerRecordO2[0].scalers[i].lmBefore, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].lmAfter - mScalerRecordO2[0].scalers[i].lmAfter, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].l0Before - mScalerRecordO2[0].scalers[i].l0Before, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].l0After - mScalerRecordO2[0].scalers[i].l0After, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].l1Before - mScalerRecordO2[0].scalers[i].l1Before, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].l1After - mScalerRecordO2[0].scalers[i].l1After, + }; + } + /// retrieves integral - same interface as getRate, no pileup correction + uint64_t getLumiNoPuCorr(int classindex, int type) const; + /// retrieves vector of counters - same interface as getRate, needed for + std::vector> getRatesForIndex(int classindex, int type) const; + /// retrieves time boundaries of this scaler object from O2 scalers + std::pair getTimeLimit() const + { + return std::make_pair((unsigned long)mScalerRecordO2[0].epochTime * 1000, (unsigned long)mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime * 1000); + } + /// retrieves time boundaries of this scaler object from Raw: should be same as from O2 and can be used without convertRawToO2 call + std::pair getTimeLimitFromRaw() const + { + return std::make_pair((unsigned long)mScalerRecordRaw[0].epochTime * 1000, (unsigned long)mScalerRecordRaw[mScalerRecordRaw.size() - 1].epochTime * 1000); + } + /// retrieves orbit boundaries of this scaler object from O2 + std::pair getOrbitLimit() const + { + return std::make_pair((unsigned long)mScalerRecordO2[0].intRecord.orbit, (unsigned long)mScalerRecordO2[mScalerRecordO2.size() - 1].intRecord.orbit); + } + /// retrieves orbit boundaries of this scaler object from Raw: should be same as from O2 and can be used without convertRawToO2 call + std::pair getOrbitLimitFromRaw() const + { + return std::make_pair((unsigned long)mScalerRecordRaw[0].intRecord.orbit, (unsigned long)mScalerRecordRaw[mScalerRecordRaw.size() - 1].intRecord.orbit); + } private: // map from class index to overflow @@ -122,10 +179,11 @@ class CTPRunScalers o2::detectors::DetID::mask_t mDetectorMask; std::vector mScalerRecordRaw; std::vector mScalerRecordO2; - int processScalerLine(const std::string& line, int& level, int& nclasses); - int copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows); + int processScalerLine(const std::string& line, int& level, uint32_t& nclasses); + int copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows, std::array& overflows); int updateOverflows(const CTPScalerRecordRaw& rec0, const CTPScalerRecordRaw& rec1, overflows_t& classesoverflows) const; int updateOverflows(const CTPScalerRaw& scal0, const CTPScalerRaw& scal1, std::array& overflow) const; + int updateOverflowsInps(const CTPScalerRecordRaw& rec0, const CTPScalerRecordRaw& rec1, std::array& overflow) const; ClassDefNV(CTPRunScalers, 2); }; } // namespace ctp diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h index ec9f003d8f94c..063336e5461ce 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h @@ -24,8 +24,10 @@ namespace ctp struct TriggerOffsetsParam : public o2::conf::ConfigurableParamHelper { static constexpr int MaxNDet = 32; // take with margin to account for possible changes / upgrades int64_t LM_L0 = 15; - int64_t L0_L1 = 280; + int64_t L0_L1 = 281; // trigger input latency + int64_t globalInputsShift = 0; // Global shift of inps; customOffset[CTP] is global shift of classes int64_t customOffset[MaxNDet] = {}; + int64_t L0_L1_classes = 280; // trigger input latency O2ParamDef(TriggerOffsetsParam, "TriggerOffsetsParam"); // boilerplate stuff + make principal key }; } // namespace ctp diff --git a/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx b/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx new file mode 100644 index 0000000000000..939203d168604 --- /dev/null +++ b/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx @@ -0,0 +1,272 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsCTP/CTPRateFetcher.h" +#include "CCDB/BasicCCDBManager.h" + +#include +#include +#include "CommonConstants/LHCConstants.h" + +using namespace o2::ctp; +double CTPRateFetcher::fetch(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, std::string sourceName) +{ + auto triggerRate = fetchNoPuCorr(ccdb, timeStamp, runNumber, sourceName); + if (triggerRate >= 0) { + return pileUpCorrection(triggerRate); + } + return -1; +} +double CTPRateFetcher::fetchNoPuCorr(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, std::string sourceName) +{ + setupRun(runNumber, ccdb, timeStamp, 1); + if (sourceName.find("ZNC") != std::string::npos) { + if (runNumber < 544448) { + return fetchCTPratesInputsNoPuCorr(timeStamp, 25) / (sourceName.find("hadronic") != std::string::npos ? 28. : 1.); + } else { + return fetchCTPratesClassesNoPuCorr(timeStamp, "C1ZNC-B-NOPF-CRU", 6) / (sourceName.find("hadronic") != std::string::npos ? 28. : 1.); + } + } else if (sourceName == "T0CE") { + return fetchCTPratesClassesNoPuCorr(timeStamp, "CMTVXTCE-B-NOPF"); + } else if (sourceName == "T0SC") { + return fetchCTPratesClassesNoPuCorr(timeStamp, "CMTVXTSC-B-NOPF"); + } else if (sourceName == "T0VTX") { + if (runNumber < 534202) { + return fetchCTPratesClassesNoPuCorr(timeStamp, "minbias_TVX_L0", 3); // 2022 + } else { + double ret = fetchCTPratesClassesNoPuCorr(timeStamp, "CMTVX-B-NOPF"); + if (ret == -2.) { + LOG(info) << "Trying different class"; + ret = fetchCTPratesClassesNoPuCorr(timeStamp, "CMTVX-NONE"); + if (ret < 0) { + LOG(error) << "None of the classes used for lumi found"; + return -1.; + } + } + return ret; + } + } + LOG(error) << "CTP rate for " << sourceName << " not available"; + return -1.; +} +void CTPRateFetcher::updateScalers(ctp::CTPRunScalers& scalers) +{ + mScalers = scalers; + mScalers.convertRawToO2(); +} +// +int CTPRateFetcher::getRates(std::array& rates, o2::ccdb::BasicCCDBManager* ccdb, int runNumber, const std::string sourceName) // rates at start,stop and middle of the run +{ + setupRun(runNumber, ccdb, 0, 1); + mOrbit = 1; + mOutsideLimits = 1; + auto orbitlimits = mScalers.getOrbitLimit(); + // std::cout << "1st orbit:" << orbitlimits.first << " last:" << orbitlimits.second << " Middle:" << (orbitlimits.first + orbitlimits.second)/2 << std::endl; + double rate0 = fetch(ccdb, orbitlimits.first, mRunNumber, sourceName); + double rateLast = fetch(ccdb, orbitlimits.second, mRunNumber, sourceName); + double rateM = fetch(ccdb, (orbitlimits.first + orbitlimits.second) / 2, mRunNumber, sourceName); + // std::cout << rate0 << " " << rateLast << " " << rateM << std::endl; + rates[0] = rate0; + rates[1] = rateLast; + rates[2] = rateM; + return 0; +} +double CTPRateFetcher::getLumiNoPuCorr(const std::string& classname, int type) +{ + if (classname == "zncinp") { + return mScalers.getLumiNoPuCorr(26, 7); + } + std::vector& ctpcls = mConfig.getCTPClasses(); + std::vector clslist = mConfig.getTriggerClassList(); + int classIndex = -1; + for (size_t i = 0; i < clslist.size(); i++) { + if (ctpcls[i].name.find(classname) != std::string::npos) { + classIndex = i; + break; + } + } + if (classIndex == -1) { + LOG(warn) << "Trigger class " << classname << " not found in CTPConfiguration"; + return -1; + } + return mScalers.getLumiNoPuCorr(classIndex, type); +} +double CTPRateFetcher::getLumiWPuCorr(const std::string& classname, int type) +{ + std::vector> scals; + if (classname == "zncinp") { + scals = mScalers.getRatesForIndex(26, 7); + } else { + std::vector& ctpcls = mConfig.getCTPClasses(); + std::vector clslist = mConfig.getTriggerClassList(); + int classIndex = -1; + for (size_t i = 0; i < clslist.size(); i++) { + if (ctpcls[i].name.find(classname) != std::string::npos) { + classIndex = i; + break; + } + } + if (classIndex == -1) { + LOG(warn) << "Trigger class " << classname << " not found in CTPConfiguration"; + return -1; + } + scals = mScalers.getRatesForIndex(classIndex, type); + } + double lumi = 0; + for (auto const& ss : scals) { + // std::cout << ss.first << " " << ss.second << " " << pileUpCorrection(ss.first/ss.second) << std::endl; + lumi += pileUpCorrection(ss.first / ss.second) * ss.second; + } + return lumi; +} +double CTPRateFetcher::getLumi(const std::string& classname, int type, int puCorr) +{ + if (puCorr) { + return getLumiWPuCorr(classname, type); + } else { + return getLumiNoPuCorr(classname, type); + } +} + +double CTPRateFetcher::getLumi(o2::ccdb::BasicCCDBManager* ccdb, int runNumber, const std::string sourceName, int puCorr) +{ + // setupRun(runNumber, ccdb, timeStamp, 1); + if (sourceName.find("ZNC") != std::string::npos) { + if (runNumber < 544448) { + return getLumi("zncinp", 1, puCorr) / (sourceName.find("hadronic") != std::string::npos ? 28. : 1.); + } else { + return getLumi("C1ZNC-B-NOPF-CRU", 6, puCorr) / (sourceName.find("hadronic") != std::string::npos ? 28. : 1.); + } + } else if (sourceName == "T0CE") { + return getLumi("CMTVXTCE-B-NOPF", 1, puCorr); + } else if (sourceName == "T0SC") { + return getLumi("CMTVXTSC-B-NOPF", 1, puCorr); + } else if (sourceName == "T0VTX") { + if (runNumber < 534202) { + return getLumi("minbias_TVX_L0", 3, puCorr); // 2022 + } else { + double ret = getLumi("CMTVX-B-NOPF", 1, puCorr); + if (ret == -1.) { + LOG(info) << "Trying different class"; + ret = getLumi("CMTVX-NONE", 1, puCorr); + if (ret < 0) { + LOG(fatal) << "None of the classes used for lumi found"; + } + } + return ret; + } + } + LOG(error) << "CTP Lumi for " << sourceName << " not available"; + return 0; +} +// +double CTPRateFetcher::fetchCTPratesClasses(uint64_t timeStamp, const std::string& className, int inputType) +{ + auto triggerRate = fetchCTPratesClassesNoPuCorr(timeStamp, className, inputType); + if (triggerRate >= 0) { + return pileUpCorrection(triggerRate); + } + return -1; +} +double CTPRateFetcher::fetchCTPratesClassesNoPuCorr(uint64_t timeStamp, const std::string& className, int inputType) +{ + std::vector& ctpcls = mConfig.getCTPClasses(); + std::vector clslist = mConfig.getTriggerClassList(); + int classIndex = -1; + for (size_t i = 0; i < clslist.size(); i++) { + if (ctpcls[i].name.find(className) != std::string::npos) { + classIndex = i; + break; + } + } + if (classIndex == -1) { + LOG(warn) << "Trigger class " << className << " not found in CTPConfiguration"; + return -2.; + } + if (mOrbit) { + auto rate{mScalers.getRate((uint32_t)timeStamp, classIndex, inputType, mOutsideLimits)}; + return rate.second; + } else { + auto rate{mScalers.getRateGivenT(timeStamp * 1.e-3, classIndex, inputType, mOutsideLimits)}; + return rate.second; + } +} +double CTPRateFetcher::fetchCTPratesInputs(uint64_t timeStamp, int input) +{ + std::vector& recs = mScalers.getScalerRecordO2(); + if (recs[0].scalersInps.size() == 48) { + if (mOrbit) { + return pileUpCorrection(mScalers.getRate((uint32_t)timeStamp, input, 7, mOutsideLimits).second); + } else { + return pileUpCorrection(mScalers.getRateGivenT(timeStamp * 1.e-3, input, 7, mOutsideLimits).second); + } + } else { + LOG(error) << "Inputs not available"; + return -1.; + } +} +double CTPRateFetcher::fetchCTPratesInputsNoPuCorr(uint64_t timeStamp, int input) +{ + std::vector& recs = mScalers.getScalerRecordO2(); + if (recs[0].scalersInps.size() == 48) { + if (mOrbit) { + return mScalers.getRate((uint32_t)timeStamp, input, 7, mOutsideLimits).second; + } else { + return mScalers.getRateGivenT(timeStamp * 1.e-3, input, 7, mOutsideLimits).second; // qc flag implemented only for time + } + } else { + LOG(error) << "Inputs not available"; + return -1.; + } +} + +double CTPRateFetcher::pileUpCorrection(double triggerRate) +{ + if (mLHCIFdata.getFillNumber() == 0) { + LOG(fatal) << "No filling" << std::endl; + } + double nbc = mLHCIFdata.getBunchFilling().getPattern().count(); + double nTriggersPerFilledBC = triggerRate / nbc / constants::lhc::LHCRevFreq; + double mu = -std::log(1 - nTriggersPerFilledBC); + return mu * nbc * constants::lhc::LHCRevFreq; +} + +void CTPRateFetcher::setupRun(int runNumber, o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, bool initScalers) +{ + if (runNumber == mRunNumber) { + return; + } + mRunNumber = runNumber; + LOG(info) << "Setting up CTP scalers for run " << mRunNumber << " and timestamp : " << timeStamp; + auto ptrLHCIFdata = ccdb->getSpecific("GLO/Config/GRPLHCIF", timeStamp); + if (ptrLHCIFdata == nullptr) { + LOG(error) << "GRPLHCIFData not in database, timestamp:" << timeStamp; + return; + } + mLHCIFdata = *ptrLHCIFdata; + std::map metadata; + metadata["runNumber"] = std::to_string(mRunNumber); + auto ptrConfig = ccdb->getSpecific("CTP/Config/Config", timeStamp, metadata); + if (ptrConfig == nullptr) { + LOG(error) << "CTPRunConfig not in database, timestamp:" << timeStamp; + return; + } + mConfig = *ptrConfig; + if (initScalers) { + auto ptrScalers = ccdb->getSpecific("CTP/Calib/Scalers", timeStamp, metadata); + if (ptrScalers) { + mScalers = *ptrScalers; + mScalers.convertRawToO2(); + } else { + LOG(error) << "CTPRunScalers not in database, timestamp:" << timeStamp; + } + } +} diff --git a/DataFormats/Detectors/CTP/src/Configuration.cxx b/DataFormats/Detectors/CTP/src/Configuration.cxx index ef06692900de5..98458ef06d1d3 100644 --- a/DataFormats/Detectors/CTP/src/Configuration.cxx +++ b/DataFormats/Detectors/CTP/src/Configuration.cxx @@ -19,10 +19,10 @@ #include #include "CommonUtils/StringUtils.h" #include +#include using namespace o2::ctp; // -std::string CTPRunManager::mCCDBHost = "http://o2-ccdb.internal"; const std::map CTPInput::run2DetToRun3Det = {{"T", "FT0"}, {"V", "FV0"}, {"U", "FDD"}, {"E", "EMC"}, {"D", "EMC"}, {"H", "TRD"}, {"O", "TOF"}, {"P", "PHS"}, {"Z", "ZDC"}}; const std::map CTPConfiguration::detName2LTG = {{"FV0", "1"}, {"FT0", "2"}, {"FDD", "3"}, {"ITS", "4"}, {"TOF", "5"}, {"MFT", "6"}, {"TPC", "7"}, {"MCH", "8"}, {"MID", "9"}, {"TST", "10"}, {"TRD", "13"}, {"HMP", "14"}, {"ZDC", "15"}, {"PHS", "16"}, {"EMC", "17"}, {"CPV", "18"}}; // @@ -48,6 +48,67 @@ bool CTPConfiguration::isNumber(const std::string& s) s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end(); } // +int BCMask::setBCmask(std::vector& tokens) +{ + BCmask.reset(); + name = tokens[1]; + bool coded = tokens[2].find("L") != std::string::npos; + coded |= tokens[2].find("H") != std::string::npos; + std::cout << "coded:" << coded << std::endl; + if (coded) { + // jusko notation + std::string bcmaskstr = tokens[2]; + size_t pos = 0; + size_t posnext = 0; + int bccur = 0; + while (bccur < 3564) { + // std::cout << "pos:" << pos << std::endl; + size_t posH = bcmaskstr.find('H', pos); + size_t posL = bcmaskstr.find('L', pos); + // std::cout << "H:" << posH << " L:" << posL << std::endl; + bool b = 1; + posnext = posH; + if (posL < posH) { + posnext = posL; + b = 0; + } + std::string bcsub = bcmaskstr.substr(pos, posnext - pos); + // std::cout << "bcsub:" << bcsub << " b:" << b << std::endl; + int bcrange = 0; + try { + bcrange = std::stoull(bcsub); + } catch (...) { + LOG(warning) << "problem in bcmask decoding H:" << posH << " posL:" << posL << " bcsub:" << bcsub; + return 1; + } + if (b) { + for (int bc = bccur; bc < bccur + bcrange; bc++) { + try { + BCmask.set(bc, 1); + } catch (...) { + LOG(warning) << "BC mask decoding to big bc:" << bc; + } + } + } + bccur += bcrange; + pos = posnext + 1; + // std::cout << "bccur:" << bccur << std::endl; + } + } else { + // list of integers + for (uint32_t i = 2; i < tokens.size(); i++) { + uint32_t bc; + try { + bc = std::stoull(tokens[i]); + } catch (...) { + LOG(info) << "mask syntax:" << i << ":" << tokens[i]; + continue; + } + BCmask.set(bc, 1); + } + } + return 0; +} void BCMask::printStream(std::ostream& stream) const { stream << "CTP BC mask:" << name << ":" << mask; /// << std::endl; @@ -149,7 +210,7 @@ int CTPConfiguration::addInput(std::string& inp, int clsindex, std::map>& descInputsIndex) { LOG(info) << "Processing line"; LOG(info) << "line:" << line << " lev:" << level; - if (line.size() == 0) { - return 0; - } - if (line.at(0) == '#') { - return 0; - } // std::vector tokens = o2::utils::Str::tokenize(line, ' '); size_t ntokens = tokens.size(); @@ -244,7 +327,11 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level LOG(info) << "Level:" << level; switch (level) { case RUN: { - mRunNumber = std::stoul(tokens[1]); + try { + mRunNumber = std::stoul(tokens[1]); + } catch (...) { + LOG(error) << "RUN:" << tokens[1] << std::endl; + } level = RUN; break; } @@ -279,7 +366,7 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level // jusko notation } else { // list of integers - for (int i = 2; i < ntokens; i++) { + for (uint32_t i = 2; i < ntokens; i++) { uint32_t bc; try { bc = std::stoull(tokens[i]); @@ -334,7 +421,7 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level LOG(info) << "Cluster:" << line; cluster.name = tokens[2]; o2::detectors::DetID::mask_t mask; - for (int item = 3; item < ntokens; item++) { + for (uint32_t item = 3; item < ntokens; item++) { std::string detname = tokens[item]; capitaliseString(detname); // LOG(info) << "Detector:" << detname; @@ -369,7 +456,7 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level CTPDescriptor desc; desc.name = "d" + cls.name; // LOG(info) << "point:" << cls.cluster << " " << &mClusters.front(); - for (int i = 2; i < tokens.size(); i++) { + for (uint32_t i = 2; i < tokens.size(); i++) { std::string token = tokens[i]; bool isGenerator = 0; for (auto const& gen : CTPGenerator::Generators) { @@ -393,7 +480,7 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level cls.downScale = std::stoul(token, nullptr, 16); } else if (token.find("bcm") != std::string::npos) { // bcmask // std::cout << "Mask" << std::endl; - int i = 0; + uint32_t i = 0; for (auto const& bcm : mBCMasks) { if (bcm.name == token) { cls.BCClassMask.push_back(&bcm); @@ -421,19 +508,243 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level } return 0; } +int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& level, std::map>& descInputsIndex) +{ + LOG(debug) << "Processing line"; + LOG(debug) << "line:" << line << " lev:" << level; + // + std::vector tokens = o2::utils::Str::tokenize(line, ' '); + size_t ntokens = tokens.size(); + if (ntokens == 0) { + LOG(warning) << "# of tokens zero in line:" << line; + return 0; + } + size_t first; + if (((first = line.find("ver")) != std::string::npos) && (level == START)) { + mVersion = line; + // std::cout << "debug:" << mVersion << std::endl; + level = VERSION; + return 0; + } else if (((first = line.find("run")) != std::string::npos) && (level == VERSION)) { + level = RUN; + } else if ((line.find("INPUTS") != std::string::npos) && (level == RUN)) { + level = INPUTS; + return 0; + } else if ((line.find("inp") != std::string::npos) && (level == INPUTS)) { + level = INPUTS; + } else if ((line.find("BCMASKS") != std::string::npos) && ((level == INPUTS) || (level == RUN))) { + level = MASKS; + return 0; + } else if (((first = line.find("bcm")) != std::string::npos) && (level == MASKS)) { + level = MASKS; + } else if (line.find("GENS") != std::string::npos) { + level = GENS; + return 0; + } else if (CTPGenerator::Generators.count(tokens[0]) && (level == GENS)) { + level = GENS; + } else if (line.find("DESCRIPTORS") != std::string::npos) { + level = DESCRIPTORS; + return 0; + } else if ((tokens[0][0] == 'D') && (level == DESCRIPTORS)) { + level = DESCRIPTORS; + } else if ((first = line.find("LTG")) != std::string::npos) { + level = LTG; + } else if ((first = line.find("cluster")) != std::string::npos) { + level = CLUSTER; + } else { + bool knownlevels = ((level == LTGitems) || (level == CLASS)); + if (knownlevels == false) { + level = UNKNOWN; + } + } + LOG(debug) << "Level:" << level; + switch (level) { + case VERSION: { + break; + } + case RUN: { + try { + mRunNumber = std::stoul(tokens[1]); + } catch (...) { + LOG(error) << "RUN line:" << line; + } + level = RUN; + break; + } + case INPUTS: { + level = INPUTS; + if (tokens.size() != 3) { + LOG(error) << "Wrong input line:" << line; + return 1; + } + CTPInput ctpinp; + ctpinp.name = tokens[1]; + ctpinp.level = tokens[1][0]; + std::string run2Name{tokens[1][1]}; + ctpinp.setRun3DetName(run2Name); + uint32_t index = std::stoul(tokens[2]); + ctpinp.inputMask = (1ull << (index - 1)); + mInputs.push_back(ctpinp); + LOG(debug) << "Input:" << ctpinp.name << " index:" << index; + break; + } + case MASKS: { + BCMask bcmask; + if (tokens.size() < 3) { + LOG(error) << "Wrong bc mask:" << line; + break; + } + bcmask.setBCmask(tokens); + mBCMasks.push_back(bcmask); + LOG(debug) << "BC mask added:" << bcmask.name; + break; + } + case GENS: { + CTPGenerator gen; + gen.name = tokens[0]; + gen.frequency = tokens[1]; + mGenerators.push_back(gen); + LOG(debug) << "Gen added:" << line; + break; + } + case DESCRIPTORS: { + if ((tokens.size() < 2)) { + if (line.find("TRUE") != std::string::npos) { + CTPDescriptor desc; + desc.name = tokens[0]; + mDescriptors.push_back(desc); + break; + } else { + LOG(warning) << "Unexpected Descriptor:" << line; + break; + } + } + CTPDescriptor desc; + desc.name = tokens[0]; + for (uint32_t i = 1; i < tokens.size(); i++) { + const CTPInput* inp = isInputInConfig(tokens[i]); + if (inp != nullptr) { + desc.inputs.push_back(inp); + } + } + mDescriptors.push_back(desc); + break; + } + case LTG: { + CTPDetector ctpdet; + std::string detname = tokens[1]; + capitaliseString(detname); + o2::detectors::DetID det(detname.c_str()); + if (isDetector(det)) { + ctpdet.detID = det.getID(); + LOG(debug) << "Detector found:" << det.getID() << " " << detname; + } else { + LOG(error) << "Unknown detectors:" << line; + } + mDetectors.push_back(ctpdet); + level = LTGitems; + break; + } + case LTGitems: { + if (ntokens == 1) { + mDetectors.back().mode = tokens[0]; + } + LOG(debug) << "LTGitem:" << line; + break; + } + case CLUSTER: { + CTPCluster cluster; + try { + cluster.hwMask = std::stoull(tokens[0]); + } catch (...) { + LOG(error) << "Cluster syntax error:" << line; + return level; + } + LOG(debug) << "Cluster:" << line; + cluster.name = tokens[2]; + o2::detectors::DetID::mask_t mask; + for (uint32_t item = 3; item < ntokens; item++) { + std::string detname = tokens[item]; + capitaliseString(detname); + // LOG(info) << "Detector:" << detname; + o2::detectors::DetID det(detname.c_str()); + isDetector(det); + mask |= det.getMask(); + } + cluster.maskCluster = mask; + mClusters.push_back(cluster); + level = CLASS; + // LOG(info) << "Cluster done:" << cluster.name << std::endl; + break; + } + case CLASS: { + // add to the last cluster + if (tokens.size() < 6) { + LOG(error) << "CTPClass items < 6" << line; + break; + } + uint64_t index; + try { + index = std::stoull(tokens[1]); + } catch (...) { + LOG(error) << "Class syntax error:" << line; + return level; + } + LOG(debug) << "Class:" << line; + CTPClass cls; + cls.classMask = 1ull << index; + cls.name = tokens[0]; + // Descriptor + std::string descname = tokens[2]; + int dindex; + if (descname.find("DTRUE") != std::string::npos) { + descname = "DTRUE"; + } + const CTPDescriptor* desc = isDescriptorInConfig(descname, dindex); + if (desc != nullptr) { + cls.descriptor = desc; + cls.descriptorIndex = dindex; + } + cls.clusterIndex = mClusters.size() - 1; + // PF not member of class + std::string bcmask = tokens[5]; + bcmask = bcmask.substr(1, bcmask.size() - 2); + if (bcmask.size()) { + const BCMask* bcm = isBCMaskInConfigP(bcmask); + if (bcm != nullptr) { + cls.BCClassMask.push_back(bcm); + } + } + // Down scaling + if (tokens.size() > 6) { + cls.downScale = std::stoul(tokens[6], nullptr, 16); + } + mCTPClasses.push_back(cls); + break; + } + default: { + LOG(warning) << "unknown line:" << line; + } + } + return 0; +} void CTPConfiguration::printStream(std::ostream& stream) const { - stream << "Configuration:" << mName << "\n Version:" << mVersion << std::endl; - stream << "Run:" << mRunNumber << " cfg name:" << mName; + stream << "Configuration:" << mName << " Version:" << mVersion << std::endl; + stream << "Run:" << mRunNumber << " cfg name:" << mName << std::endl; stream << "CTP BC masks:" << std::endl; for (const auto& i : mBCMasks) { i.printStream(stream); } - stream << "CTP inputs:" << std::endl; + stream << "CTP inputs:" << mInputs.size() << std::endl; for (const auto& i : mInputs) { i.printStream(stream); } - stream << "CTP descriptors:" << std::endl; + stream << "CTP generators:" << std::endl; + for (const auto& i : mGenerators) { + i.printStream(stream); + } + stream << "CTP descriptors:" << mDescriptors.size() << std::endl; for (const auto& i : mDescriptors) { i.printStream(stream); } @@ -469,6 +780,36 @@ int CTPConfiguration::getInputIndex(const std::string& name) const LOG(info) << "input:" << name << " index:" << index; return index; } +std::string CTPConfiguration::getClassNameFromIndex(int index) +{ + if (index < (int)mCTPClasses.size()) { + return mCTPClasses[index].name; + } else { + std::string name = "Cls" + std::to_string(index); + return name; + } +}; +std::string CTPConfiguration::getClassNameFromHWIndex(int index) +{ + for (auto& cls : mCTPClasses) { + if (cls.classMask == (1ull << index)) { + return cls.name; + } + } + std::string ret = "not found"; + return ret; +} +const CTPClass* CTPConfiguration::getCTPClassFromHWIndex(int index) const +{ + const CTPClass* clsfound = nullptr; + for (auto const& cls : mCTPClasses) { + if (index == cls.getIndex()) { + clsfound = &cls; + break; + } + } + return clsfound; +} bool CTPConfiguration::isMaskInInputs(const uint64_t& mask) const { for (auto const& inp : mInputs) { @@ -487,18 +828,29 @@ bool CTPConfiguration::isBCMaskInConfig(const std::string maskname) const } return false; } +const BCMask* CTPConfiguration::isBCMaskInConfigP(const std::string maskname) const +{ + for (const auto& bcm : mBCMasks) { + if (bcm.name == maskname) { + LOG(info) << "isBCMaskInConfigP found:" << maskname; + return &bcm; + } + } + LOG(info) << "isBCMaskInConfigP NOT found:" << maskname; + return nullptr; +} const CTPInput* CTPConfiguration::isInputInConfig(const std::string inpname) const { for (const auto& inp : mInputs) { if (inp.name == inpname) { - LOG(info) << "isInpuyInConfig found:" << inpname; + LOG(info) << "isInputInConfig found:" << inpname; return &inp; } } LOG(info) << "isInputInConfig NOT found:" << inpname; return nullptr; } -const CTPInput* CTPConfiguration::isInputInConfig(const int index) const +const CTPInput* CTPConfiguration::isInputInConfig(const uint32_t index) const { for (const auto& inp : mInputs) { // std::cout << "isInputINConfig:" << inp.name << " " << inp.getIndex() << " " << index << std::endl; @@ -509,6 +861,19 @@ const CTPInput* CTPConfiguration::isInputInConfig(const int index) const } return nullptr; } +const CTPDescriptor* CTPConfiguration::isDescriptorInConfig(const std::string descname, int& index) const +{ + index = 0; + for (const auto& desc : mDescriptors) { + if (desc.name == descname) { + LOG(info) << "isDescriptorInConfig found:" << descname; + return &desc; + } + index++; + } + LOG(info) << "isDescriptorInConfig NOT found:" << descname; + return nullptr; +} void CTPConfiguration::createInputsInDecriptorsFromNames() // using run3 conventions for inputs { @@ -549,6 +914,32 @@ uint64_t CTPConfiguration::getTriggerClassMask() const } return clsmask; } +uint64_t CTPConfiguration::getTriggerClassMaskWInputs() const +{ + uint64_t clsmask = 0; + for (auto const& cls : mCTPClasses) { + if (cls.name.find("TRUE") != std::string::npos) { // ignoring internal ctp generators + continue; + } + clsmask |= cls.classMask; + } + return clsmask; +} +uint64_t CTPConfiguration::getTriggerClassMaskWInputsNoTrgDets() const +{ + uint64_t clsmask = 0; + for (auto const& cls : mCTPClasses) { + bool exclude = cls.name.find("TRUE") != std::string::npos; // ignoring internal ctp generators + exclude += cls.name.find("EMC") != std::string::npos; + exclude += cls.name.find("TRD") != std::string::npos; + exclude += cls.name.find("HMP") != std::string::npos; + if (!exclude) { + clsmask |= cls.classMask; + } + } + return clsmask; +} +// Hardware positions of classes std::vector CTPConfiguration::getTriggerClassList() const { uint64_t clsmask = getTriggerClassMask(); @@ -600,393 +991,80 @@ int CTPConfiguration::assignDescriptors() } return 0; } -/// -/// Run Managet to manage Config and Scalers -/// -void CTPRunManager::init() -{ - for (auto r : mActiveRuns) { - r = nullptr; - } - loadScalerNames(); - LOG(info) << "CCDB host:" << mCCDBHost; - LOG(info) << "CTP vNew:" << mNew; - LOG(info) << "CTPRunManager initialised."; -} -int CTPRunManager::loadRun(const std::string& cfg) +int CTPConfiguration::checkConfigConsistency() const { - LOG(info) << "Loading run: " << cfg; - auto now = std::chrono::system_clock::now(); - long timeStamp = std::chrono::duration_cast(now.time_since_epoch()).count(); - LOG(info) << "Timestamp real time:" << timeStamp; - size_t pos = cfg.find(" "); - std::string cfgmod = cfg; - if (pos == std::string::npos) { - LOG(warning) << "Space not found in CTP config"; - } else { - std::string f = cfg.substr(0, pos); - if (f.find("run") == std::string::npos) { - double_t tt = std::stold(f); - timeStamp = (tt * 1000.); - LOG(info) << "Timestamp file:" << timeStamp; - cfgmod = cfg.substr(pos, cfg.size()); - LOG(info) << "ctpcfg: using ctp time"; + LOG(info) << "Checking consistency run:" << mRunNumber; + int ret = 0; + // All inputs used ? + // std::map inputs; + std::map inputs; + for (auto const& inp : mInputs) { + inputs[inp.name] = 0; + } + // Are all descriptors used + // std::map descs; + std::map descs; + for (auto const& desc : mDescriptors) { + descs[desc.name] = 0; + // std::cout << "1 " << &desc << std::endl; + for (auto const inp : desc.inputs) { + inputs[inp->name] += 1; } } - CTPActiveRun* activerun = new CTPActiveRun; - activerun->timeStart = timeStamp; - activerun->cfg.loadConfigurationRun3(cfgmod); - activerun->cfg.printStream(std::cout); - // - uint32_t runnumber = activerun->cfg.getRunNumber(); - activerun->scalers.setRunNumber(runnumber); - activerun->scalers.setClassMask(activerun->cfg.getTriggerClassMask()); - o2::detectors::DetID::mask_t detmask = activerun->cfg.getDetectorMask(); - activerun->scalers.setDetectorMask(detmask); + std::cout << "desc1:" << descs.size() << std::endl; // - mRunsLoaded[runnumber] = activerun; - saveRunConfigToCCDB(&activerun->cfg, timeStamp); - return 0; -} -int CTPRunManager::startRun(const std::string& cfg) -{ - return 0; -} -int CTPRunManager::stopRun(uint32_t irun, long timeStamp) -{ - LOG(info) << "Stopping run index: " << irun; - if (mActiveRuns[irun] == nullptr) { - LOG(error) << "No config for run index:" << irun; - return 1; - } - // const auto now = std::chrono::system_clock::now(); - // const long timeStamp = std::chrono::duration_cast(now.time_since_epoch()).count(); - mActiveRuns[irun]->timeStop = timeStamp * 1000.; - saveRunScalersToCCDB(irun); - delete mActiveRuns[irun]; - mActiveRuns[irun] = nullptr; - return 0; -} -int CTPRunManager::addScalers(uint32_t irun, std::time_t time) -{ - if (mActiveRuns[irun] == nullptr) { - LOG(error) << "No config for run index:" << irun; - return 1; - } - std::string orb = "extorb"; - std::string orbitid = "orbitid"; - CTPScalerRecordRaw scalrec; - scalrec.epochTime = time; - std::vector clslist = mActiveRuns[irun]->cfg.getTriggerClassList(); - for (auto const& cls : clslist) { - std::string cmb = "clamb" + std::to_string(cls + 1); - std::string cma = "clama" + std::to_string(cls + 1); - std::string c0b = "cla0b" + std::to_string(cls + 1); - std::string c0a = "cla0a" + std::to_string(cls + 1); - std::string c1b = "cla1b" + std::to_string(cls + 1); - std::string c1a = "cla1a" + std::to_string(cls + 1); - CTPScalerRaw scalraw; - scalraw.classIndex = (uint32_t)cls; - // std::cout << "cls:" << cls << " " << scalraw.classIndex << std::endl; - scalraw.lmBefore = mCounters[mScalerName2Position[cmb]]; - scalraw.lmAfter = mCounters[mScalerName2Position[cma]]; - scalraw.l0Before = mCounters[mScalerName2Position[c0b]]; - scalraw.l0After = mCounters[mScalerName2Position[c0a]]; - scalraw.l1Before = mCounters[mScalerName2Position[c1b]]; - scalraw.l1After = mCounters[mScalerName2Position[c1a]]; - // std::cout << "positions:" << cmb << " " << mScalerName2Position[cmb] << std::endl; - // std::cout << "positions:" << cma << " " << mScalerName2Position[cma] << std::endl; - scalrec.scalers.push_back(scalraw); - } - // detectors - // std::vector detlist = mActiveRuns[irun]->cfg.getDetectorList(); - o2::detectors::DetID::mask_t detmask = mActiveRuns[irun]->cfg.getDetectorMask(); - for (uint32_t i = 0; i < 32; i++) { - o2::detectors::DetID::mask_t deti = 1ul << i; - bool detin = (detmask & deti).count(); - if (detin) { - std::string detname(o2::detectors::DetID::getName(i)); - std::string countername = "ltg" + CTPConfiguration::detName2LTG.at(detname) + "_PH"; - uint32_t detcount = mCounters[mScalerName2Position[countername]]; - scalrec.scalersDets.push_back(detcount); - // LOG(info) << "Scaler for detector:" << countername << ":" << detcount; + for (const auto& cls : mCTPClasses) { + if (cls.classMask == 0) { + std::cout << "ERROR class:" << cls.name << " NO CLASS MASK" << std::endl; + ret++; } - } - // - if (mNew == 0) { - scalrec.intRecord.orbit = mCounters[mScalerName2Position[orb]]; - } else { - scalrec.intRecord.orbit = mCounters[mScalerName2Position[orbitid]]; - } - scalrec.intRecord.bc = 0; - mActiveRuns[irun]->scalers.addScalerRacordRaw(scalrec); - LOG(info) << "Adding scalers for orbit:" << scalrec.intRecord.orbit; - // scalrec.printStream(std::cout); - // printCounters(); - return 0; -} -int CTPRunManager::processMessage(std::string& topic, const std::string& message) -{ - LOG(info) << "Processing message with topic:" << topic; - std::string firstcounters; - if (topic.find("ctpconfig") != std::string::npos) { - LOG(info) << "ctpconfig received"; - loadRun(message); - return 0; - } - if (topic.find("sox") != std::string::npos) { - // get config - size_t irun = message.find("run"); - if (irun == std::string::npos) { - LOG(warning) << "run keyword not found in SOX"; - irun = message.size(); + if (cls.cluster == nullptr) { + std::cout << "ERROR class:" << cls.name << " NO CLUSTER" << std::endl; + ret++; } - LOG(info) << "SOX received, Run keyword position:" << irun; - std::string cfg = message.substr(irun, message.size() - irun); - startRun(cfg); - firstcounters = message.substr(0, irun); - } - if (topic.find("eox") != std::string::npos) { - LOG(info) << "EOX received"; - mEOX = 1; - } - // - std::vector tokens; - if (firstcounters.size() > 0) { - tokens = o2::utils::Str::tokenize(firstcounters, ' '); - } else { - tokens = o2::utils::Str::tokenize(message, ' '); - } - if (tokens.size() != (CTPRunScalers::NCOUNTERS + 1)) { - if (tokens.size() == (CTPRunScalers::NCOUNTERS)) { - mNew = 0; - LOG(warning) << "v1 scaler size, using external orbit"; - } else { - LOG(error) << "Scalers size wrong:" << tokens.size() << " expected:" << CTPRunScalers::NCOUNTERS + 1; - return 1; + if (cls.clusterIndex == 0xff) { + std::cout << "ERROR class:" << cls.name << " NO CLUSTER INDEX" << std::endl; + ret++; } - } - double timeStamp = std::stold(tokens.at(0)); - std::time_t tt = timeStamp; - LOG(info) << "Processing scalers, all good, time:" << tokens.at(0) << " " << std::asctime(std::localtime(&tt)); - for (int i = 1; i < tokens.size(); i++) { - mCounters[i - 1] = std::stoull(tokens.at(i)); - if (i < (NRUNS + 1)) { - std::cout << mCounters[i - 1] << " "; + if (cls.descriptor == nullptr) { + std::cout << "ERROR class:" << cls.name << " NO DESCRIPTOR" << std::endl; + ret++; + } else { + descs[cls.descriptor->name] += 1; + // std::cout << "2 " << cls.descriptor << std::endl; } - } - std::cout << std::endl; - LOG(info) << "Counter size:" << tokens.size(); - // - for (uint32_t i = 0; i < NRUNS; i++) { - if ((mCounters[i] == 0) && (mActiveRunNumbers[i] == 0)) { - // not active - } else if ((mCounters[i] != 0) && (mActiveRunNumbers[i] == mCounters[i])) { - // active , do scalers - LOG(info) << "Run continue:" << mCounters[i]; - addScalers(i, tt); - } else if ((mCounters[i] != 0) && (mActiveRunNumbers[i] == 0)) { - LOG(info) << "Run started:" << mCounters[i]; - auto run = mRunsLoaded.find(mCounters[i]); - if (run != mRunsLoaded.end()) { - mActiveRunNumbers[i] = mCounters[i]; - mActiveRuns[i] = run->second; - mRunsLoaded.erase(run); - addScalers(i, tt); - } else { - LOG(error) << "Trying to start run which is not loaded:" << mCounters[i]; - } - } else if ((mCounters[i] == 0) && (mActiveRunNumbers[i] != 0)) { - if (mEOX != 1) { - LOG(error) << "Internal error in processMessage: mEOX != 1 expected 0: mEOX:" << mEOX; - } - LOG(info) << "Run stopped:" << mActiveRunNumbers[i]; - addScalers(i, tt); - mActiveRunNumbers[i] = 0; - mEOX = 0; - stopRun(i, tt); + if (cls.descriptorIndex == 0xff) { + std::cout << "ERROR class:" << cls.name << " NO DESCRIPTOR INDEX" << std::endl; + ret++; + } else { + // std::cout << "3 " << &mDescriptors[cls.descriptorIndex] << std::endl; } } - mEOX = 0; - printActiveRuns(); - return 0; -} -void CTPRunManager::printActiveRuns() const -{ - std::cout << "Active runs:"; - for (auto const& arun : mActiveRunNumbers) { - std::cout << arun << " "; - } - std::cout << " #loaded runs:" << mRunsLoaded.size(); - for (auto const& lrun : mRunsLoaded) { - std::cout << " " << lrun.second->cfg.getRunNumber(); - } - std::cout << std::endl; -} -int CTPRunManager::saveRunScalersToCCDB(int i) -{ - // data base - CTPActiveRun* run = mActiveRuns[i]; - using namespace std::chrono_literals; - std::chrono::seconds days3 = 259200s; - std::chrono::seconds min10 = 600s; - long time3days = std::chrono::duration_cast(days3).count(); - long time10min = std::chrono::duration_cast(min10).count(); - long tmin = run->timeStart - time10min; - long tmax = run->timeStop + time3days; - o2::ccdb::CcdbApi api; - map metadata; // can be empty - metadata["runNumber"] = std::to_string(run->cfg.getRunNumber()); - api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation - // store abitrary user object in strongly typed manner - api.storeAsTFileAny(&(run->scalers), mCCDBPathCTPScalers, metadata, tmin, tmax); - LOG(info) << "CTP scalers saved in ccdb:" << mCCDBHost << " run:" << run->cfg.getRunNumber() << " tmin:" << tmin << " tmax:" << tmax; - return 0; -} -int CTPRunManager::saveRunConfigToCCDB(CTPConfiguration* cfg, long timeStart) -{ - // data base - using namespace std::chrono_literals; - std::chrono::seconds days3 = 259200s; - std::chrono::seconds min10 = 600s; - long time3days = std::chrono::duration_cast(days3).count(); - long time10min = std::chrono::duration_cast(min10).count(); - long tmin = timeStart - time10min; - long tmax = timeStart + time3days; - o2::ccdb::CcdbApi api; - map metadata; // can be empty - metadata["runNumber"] = std::to_string(cfg->getRunNumber()); - api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation - // store abitrary user object in strongly typed manner - api.storeAsTFileAny(cfg, CCDBPathCTPConfig, metadata, tmin, tmax); - LOG(info) << "CTP config saved in ccdb:" << mCCDBHost << " run:" << cfg->getRunNumber() << " tmin:" << tmin << " tmax:" << tmax; - return 0; -} -CTPConfiguration CTPRunManager::getConfigFromCCDB(long timestamp, std::string run) -{ - auto& mgr = o2::ccdb::BasicCCDBManager::instance(); - mgr.setURL(mCCDBHost); - map metadata; // can be empty - metadata["runNumber"] = run; - auto ctpconfigdb = mgr.getSpecific(CCDBPathCTPConfig, timestamp, metadata); - if (ctpconfigdb == nullptr) { - LOG(info) << "CTP config not in database, timestamp:" << timestamp; - } else { - // ctpconfigdb->printStream(std::cout); - LOG(info) << "CTP config found. Run:" << run; - } - return *ctpconfigdb; -} -CTPRunScalers CTPRunManager::getScalersFromCCDB(long timestamp, std::string run, bool& ok) -{ - auto& mgr = o2::ccdb::BasicCCDBManager::instance(); - mgr.setURL(mCCDBHost); - map metadata; // can be empty - metadata["runNumber"] = run; - auto ctpscalers = mgr.getSpecific(mCCDBPathCTPScalers, timestamp, metadata); - if (ctpscalers == nullptr) { - LOG(info) << "CTPRunScalers not in database, timestamp:" << timestamp; - ok = 0; - } else { - // ctpscalers->printStream(std::cout); - ok = 1; - } - return *ctpscalers; -} -int CTPRunManager::loadScalerNames() -{ - if (CTPRunScalers::NCOUNTERS != CTPRunScalers::scalerNames.size()) { - LOG(fatal) << "NCOUNTERS:" << CTPRunScalers::NCOUNTERS << " different from names vector:" << CTPRunScalers::scalerNames.size(); - return 1; - } - // try to open files of no success use default - for (uint32_t i = 0; i < CTPRunScalers::scalerNames.size(); i++) { - mScalerName2Position[CTPRunScalers::scalerNames[i]] = i; - } - return 0; -} -void CTPRunManager::printCounters() -{ - int NDET = 18; - int NINPS = 48; - int NCLKFP = 7; - int NLTG_start = NRUNS; - int NCLKFP_start = NLTG_start + NDET * 32; - int NINPS_start = NCLKFP_start + 7; - int NCLS_start = NINPS_start + NINPS; - std::cout << "====> CTP counters:" << std::endl; - std::cout << "RUNS:" << std::endl; - int ipos = 0; - for (int i = 0; i < NRUNS; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - for (int i = 0; i < NDET; i++) { - std::cout << "LTG" << i + 1 << std::endl; - for (int j = NLTG_start + i * 32; j < NLTG_start + (i + 1) * 32; j++) { - std::cout << ipos << ":" << mCounters[j] << " "; - ipos++; + int iw = 0; + for (auto const& inp : inputs) { + if (inp.second == 0) { + iw++; + std::cout << "WARNING inputs:"; } - std::cout << std::endl; - } - std::cout << "BC40,BC240,Orbit,pulser, fastlm, busy,spare" << std::endl; - for (int i = NCLKFP_start; i < NCLKFP_start + 7; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "INPUTS:" << std::endl; - for (int i = NINPS_start; i < NINPS_start + NINPS; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "CLASS M Before" << std::endl; - for (int i = NCLS_start; i < NCLS_start + 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "CLASS M After" << std::endl; - for (int i = NCLS_start + 64; i < NCLS_start + 2 * 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "CLASS 0 Before" << std::endl; - for (int i = NCLS_start + 2 * 64; i < NCLS_start + 3 * 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; + std::cout << inp.first << " " << inp.second << std::endl; } - std::cout << std::endl; - std::cout << "CLASS 0 After" << std::endl; - for (int i = NCLS_start + 3 * 64; i < NCLS_start + 4 * 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "CLASS 1 Before" << std::endl; - for (int i = NCLS_start + 4 * 64; i < NCLS_start + 5 * 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "CLASS 1 After" << std::endl; - for (int i = NCLS_start + 5 * 64; i < NCLS_start + 6 * 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << " REST:" << std::endl; - for (int i = NCLS_start + 6 * 64; i < mCounters.size(); i++) { - if ((ipos % 10) == 0) { - std::cout << std::endl; - std::cout << ipos << ":"; + std::cout << "Descriptors check:" << descs.size() << std::endl; + for (auto const& desc : descs) { + if (desc.second == 0) { + iw++; + std::cout << "WARNING descriptors:"; } - std::cout << mCounters[i] << " "; + // std::cout << (desc.first)->name << " " << desc.second << std::endl; + std::cout << (desc.first) << " " << desc.second << std::endl; } + std::cout << "CTP Config consistency checked. WARNINGS:" << iw << " ERRORS:" << ret << std::endl; + return ret; } +void CTPConfiguration::printConfigString() +{ + std::cout << mConfigString << std::endl; +}; +// int CTPInputsConfiguration::createInputsConfigFromFile(std::string& filename) { int ret = 0; @@ -1027,7 +1105,6 @@ int CTPInputsConfiguration::createInputsConfigFromFile(std::string& filename) ret++; } return ret; - ; } /// /// CTP inputs config @@ -1040,49 +1117,27 @@ void CTPInputsConfiguration::printStream(std::ostream& stream) const input.printStream(stream); } } +// +// EMBA - software generated input for EMC - Min Bias Accepted +// +const std::vector CTPInputsConfiguration::CTPInputsDefault = + { + CTPInput("MT0A", "FT0", 1), CTPInput("MT0C", "FT0", 2), CTPInput("MTVX", "FT0", 3), CTPInput("MTSC", "FT0", 4), CTPInput("MTCE", "FT0", 5), + CTPInput("MVBA", "FV0", 6), CTPInput("MVOR", "FV0", 7), CTPInput("MVIR", "FV0", 8), CTPInput("MVNC", "FV0", 9), CTPInput("MVCH", "FV0", 10), + CTPInput("0UCE", "FDD", 13), CTPInput("0USC", "FDD", 15), CTPInput("0UVX", "FDD", 16), CTPInput("0U0C", "FDD", 17), CTPInput("0U0A", "FDD", 18), + CTPInput("0DMC", "EMC", 14), CTPInput("0DJ1", "EMC", 41), CTPInput("0DG1", "EMC", 42), CTPInput("0DJ2", "EMC", 43), CTPInput("0DG2", "EMC", 44), + CTPInput("0EMC", "EMC", 21), CTPInput("0EJ1", "EMC", 37), CTPInput("0EG1", "EMC", 38), CTPInput("0EJ2", "EMC", 39), CTPInput("0EG2", "EMC", 40), + CTPInput("0PH0", "PHS", 22), CTPInput("1PHL", "PHS", 27), CTPInput("1PHH", "PHS", 28), CTPInput("1PHL", "PHM", 29), + CTPInput("1ZED", "ZDC", 25), CTPInput("1ZNC", "ZDC", 26), CTPInput("EMBA", "EMC", 48)}; void CTPInputsConfiguration::initDefaultInputConfig() { - defaultInputConfig.CTPInputs.push_back(CTPInput("MT0A", "FT0", 1)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MT0C", "FT0", 2)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MTVX", "FT0", 3)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MTSC", "FT0", 4)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MTCE", "FT0", 5)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("MVBA", "FV0", 6)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MVOR", "FV0", 7)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MVIR", "FV0", 8)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MVNC", "FV0", 9)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MVCH", "FV0", 10)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("0UCE", "FT0", 13)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0USC", "FT0", 15)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0UVX", "FT0", 16)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0U0C", "FT0", 17)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0U0A", "FT0", 18)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("0DMC", "EMC", 14)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0DJ1", "EMC", 41)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0DG1", "EMC", 42)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0DJ2", "EMC", 43)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0DG2", "EMC", 44)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("0EMC", "EMC", 21)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0EJ1", "EMC", 37)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0EG1", "EMC", 38)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0EJ2", "EMC", 39)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0EG2", "EMC", 40)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("0PH0", "PHS", 22)); - defaultInputConfig.CTPInputs.push_back(CTPInput("1PHL", "PHS", 27)); - defaultInputConfig.CTPInputs.push_back(CTPInput("1PHH", "PHS", 28)); - defaultInputConfig.CTPInputs.push_back(CTPInput("1PHL", "PHM", 29)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("1ZED", "ZDC", 25)); - defaultInputConfig.CTPInputs.push_back(CTPInput("1ZNC", "ZDC", 26)); + defaultInputConfig.CTPInputs = CTPInputsConfiguration::CTPInputsDefault; } -std::string CTPInputsConfiguration::getInputNameFromIndex(int index) +/// Return input name from default inputs configuration. +/// Take into account convention that L0 inputs has (index+100) in the first version of CTP config file (*.rcfg) +std::string CTPInputsConfiguration::getInputNameFromIndex100(uint32_t index) { - int indexcor = index; + uint32_t indexcor = index; if (index > 100) { indexcor = index - 100; } @@ -1098,17 +1153,98 @@ std::string CTPInputsConfiguration::getInputNameFromIndex(int index) LOG(info) << "Input with index:" << index << " not in deafult input config"; return ""; } +/// Return input name from default inputs configuration. +/// Index has to be in range [1::48] +std::string CTPInputsConfiguration::getInputNameFromIndex(uint32_t index) +{ + if (index > o2::ctp::CTP_NINPUTS) { + LOG(warn) << "getInputNameFRomIndex: index too big:" << index; + return "none"; + } + for (auto& inp : o2::ctp::CTPInputsConfiguration::CTPInputsDefault) { + if (inp.getIndex() == index) { + std::string name = inp.name; + return name; + } + } + LOG(info) << "Input with index:" << index << " not in deafult input config"; + return "none"; +} int CTPInputsConfiguration::getInputIndexFromName(std::string& name) { std::string namecorr = name; - if (name[0] == '0') { - name.substr(1, name.size() - 1); + if ((name[0] == '0') || (name[0] == 'M') || (name[0] == '1')) { + namecorr = namecorr.substr(1, namecorr.size() - 1); + } else { + LOG(warn) << "Input name without level:" << name; } - for (auto& inp : defaultInputConfig.CTPInputs) { + for (auto& inp : o2::ctp::CTPInputsConfiguration::CTPInputsDefault) { if (inp.name.find(namecorr) != std::string::npos) { return inp.getIndex(); } } - LOG(info) << "Input with name:" << name << " not in default input config"; + LOG(warn) << "Input with name:" << name << " not in default input config"; return 0xff; } + +int CtpCfg::readAndSave(std::string& path) +{ + std::string file = path + filename; + std::ifstream ctpcfg(file); + if (ctpcfg.is_open()) { + std::string line; + while (std::getline(ctpcfg, line)) { + o2::utils::Str::trim(line); + if (line.size() == 0) { + continue; + } + if (line[0] == '#') { + continue; + } + std::vector tokens = o2::utils::Str::tokenize(line, ' '); + size_t ntokens = tokens.size(); + if (ntokens < 2) { + LOG(warn) << "Not enough tokens"; + continue; + } + if (tokens[0].find("TForbits") != std::string::npos) { + TFOrbits = std::atol(tokens[1].c_str()); + } else if (tokens[0].find("ccdb") != std::string::npos) { + ccdb = std::atoi(tokens[1].c_str()); + } else if (tokens[0].find("orbitshift") != std::string::npos) { + orbitShift = std::atol(tokens[1].c_str()); + } else if (tokens[0].find("ir_inputs") != std::string::npos) { + irInputs_1_24 = std::stoul(tokens[2].c_str(), nullptr, 16); + irInputs_25_48 = std::stoul(tokens[1].c_str(), nullptr, 16); + } else { + LOG(warn) << " Token not found:" << tokens[0]; + } + } + LOG(warn) << "Open file success:" << file; + } else { + LOG(warn) << "Can not open file:" << file; + return 1; + } + return 0; +} +std::vector CtpCfg::listOfUsedInputs() +{ + std::cout << std::hex << "0x" << irInputs_1_24 << " " << irInputs_25_48 << std::dec << std::endl; + std::vector inputList; + for (int i = 0; i < 24; i++) { + if ((1ul << i) & irInputs_1_24) { + inputList.push_back(i); + } + } + for (int i = 0; i < 24; i++) { + if ((1ul << i) & irInputs_25_48) { + inputList.push_back(i + 24); + } + } + return inputList; +} +std::ostream& o2::ctp::operator<<(std::ostream& in, const o2::ctp::CTPConfiguration& conf) +{ + conf.printStream(in); + return in; +} \ No newline at end of file diff --git a/DataFormats/Detectors/CTP/src/DataFormatsCTPLinkDef.h b/DataFormats/Detectors/CTP/src/DataFormatsCTPLinkDef.h index c3c657659d40f..ac2a83d31edda 100644 --- a/DataFormats/Detectors/CTP/src/DataFormatsCTPLinkDef.h +++ b/DataFormats/Detectors/CTP/src/DataFormatsCTPLinkDef.h @@ -14,6 +14,7 @@ #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class std::bitset < 48> + ; #pragma link C++ class o2::ctp::CTPDigit + ; #pragma link C++ class vector < o2::ctp::CTPDigit> + ; #pragma link C++ class o2::ctp::CTPInputDigit + ; @@ -43,9 +44,9 @@ #pragma link C++ class o2::ctp::CTPScalerRecordO2 + ; #pragma link C++ class vector < o2::ctp::CTPScalerRecordO2> + ; #pragma link C++ class o2::ctp::CTPRunScalers + ; -#pragma link C++ class o2::ctp::CTPRunManager + ; #pragma link C++ class o2::ctp::LumiInfo + ; #pragma link C++ class vector < o2::ctp::LumiInfo> + ; +#pragma link C++ class o2::ctp::CTPRateFetcher + ; #pragma link C++ struct o2::ctp::CTFHeader + ; #pragma link C++ struct o2::ctp::CTF + ; @@ -54,4 +55,6 @@ #pragma link C++ class o2::ctp::TriggerOffsetsParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::ctp::TriggerOffsetsParam> + ; +#pragma link C++ class o2::ctp::CtpCfg + ; + #endif diff --git a/DataFormats/Detectors/CTP/src/Digits.cxx b/DataFormats/Detectors/CTP/src/Digits.cxx index aa44216270721..205225a35500a 100644 --- a/DataFormats/Detectors/CTP/src/Digits.cxx +++ b/DataFormats/Detectors/CTP/src/Digits.cxx @@ -30,13 +30,13 @@ void CTPDigit::printStream(std::ostream& stream) const void CTPDigit::setInputMask(gbtword80_t mask) { - for (int i = 0; i < CTP_NINPUTS; i++) { + for (uint32_t i = 0; i < CTP_NINPUTS; i++) { CTPInputMask[i] = mask[i]; } } void CTPDigit::setClassMask(gbtword80_t mask) { - for (int i = 0; i < CTP_NCLASSES; i++) { + for (uint32_t i = 0; i < CTP_NCLASSES; i++) { CTPClassMask[i] = mask[i]; } } diff --git a/DataFormats/Detectors/CTP/src/LumiInfo.cxx b/DataFormats/Detectors/CTP/src/LumiInfo.cxx new file mode 100644 index 0000000000000..08f8e5e7b0279 --- /dev/null +++ b/DataFormats/Detectors/CTP/src/LumiInfo.cxx @@ -0,0 +1,23 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file LumiInfo.cxx + +#include "DataFormatsCTP/LumiInfo.h" +#include "DataFormatsCTP/Configuration.h" +#include + +using namespace o2::ctp; + +void LumiInfo::printInputs() const +{ + LOG(info) << "Lumi inp1:" << inp1 << ":" << o2::ctp::CTPInputsConfiguration::getInputNameFromIndex(inp1) << " inp2:" << inp2 << ":" << o2::ctp::CTPInputsConfiguration::getInputNameFromIndex(inp2); +} diff --git a/DataFormats/Detectors/CTP/src/Scalers.cxx b/DataFormats/Detectors/CTP/src/Scalers.cxx index deddef75a72a4..256722fc1e5ae 100644 --- a/DataFormats/Detectors/CTP/src/Scalers.cxx +++ b/DataFormats/Detectors/CTP/src/Scalers.cxx @@ -21,15 +21,19 @@ using namespace o2::ctp; void errorCounters::printStream(std::ostream& stream) const { - stream << "Counter errorrs: lmB:" << lmB << " l0B:" << l0B << " l1B:" << l1B << " lmA:" << lmA << " l0A:" << l0A << " l1A:" << l1A; - stream << " lmBlmA:" << lmBlmA << " lmAl0B:" << lmAl0B << " l0Bl0A:" << l0Bl0A << " l0Al1B: " << l0Al1B << " l1Bl1A:" << l1Bl1A; - stream << std::endl; + // stream << "Counter warnings diff 1 lmBlmA:" << lmBlmAd1 << " lmAl0B:" << lmAl0Bd1 << " l0Bl0A:" << l0Bl0Ad1 << " l0Al1B: " << l0Al1Bd1 << " l1Bl1A:" << l1Bl1Ad1; + // stream << std::endl; + if (lmB + l0B + l1B + lmA + l0A + l1A + lmBlmA + lmAl0B + l0Bl0A + l0Al1B + l1Bl1A) { + stream << "Counter errorrs: lmB:" << lmB << " l0B:" << l0B << " l1B:" << l1B << " lmA:" << lmA << " l0A:" << l0A << " l1A:" << l1A; + stream << " lmBlmA:" << lmBlmA << " lmAl0B:" << lmAl0B << " l0Bl0A:" << l0Bl0A << " l0Al1B: " << l0Al1B << " l1Bl1A:" << l1Bl1A; + stream << std::endl; + } } void CTPScalerRaw::printStream(std::ostream& stream) const { stream << "Class:" << std::setw(2) << classIndex << " RAW"; stream << " LMB:" << std::setw(10) << lmBefore << " LMA:" << std::setw(10) << lmAfter; - stream << " LOB:" << std::setw(10) << l0Before << " L0A:" << std::setw(10) << l0After; + stream << " L0B:" << std::setw(10) << l0Before << " L0A:" << std::setw(10) << l0After; stream << " L1B:" << std::setw(10) << l1Before << " L1A:" << std::setw(10) << l1After << std::endl; } // @@ -48,7 +52,7 @@ void CTPScalerO2::printStream(std::ostream& stream) const { stream << "Class:" << std::setw(2) << classIndex << " O2"; stream << " LMB:" << std::setw(10) << lmBefore << " LMA:" << std::setw(10) << lmAfter; - stream << " LOB:" << std::setw(10) << l0Before << " L0A:" << std::setw(10) << l0After; + stream << " L0B:" << std::setw(10) << l0Before << " L0A:" << std::setw(10) << l0After; stream << " L1B:" << std::setw(10) << l1Before << " L1A:" << std::setw(10) << l1After << std::endl; } void CTPScalerO2::printFromZero(std::ostream& stream, CTPScalerO2& scaler0) const @@ -65,7 +69,8 @@ void CTPScalerRecordRaw::printStream(std::ostream& stream) const for (auto const& cnts : scalers) { cnts.printStream(stream); } - for (auto const& dets : scalersDets) { + std::cout << "Inputs:" << scalersInps.size() << std::endl; + for (auto const& dets : scalersInps) { stream << dets << " "; } stream << std::endl; @@ -77,7 +82,8 @@ void CTPScalerRecordO2::printStream(std::ostream& stream) const for (auto const& cnts : scalers) { cnts.printStream(stream); } - for (auto const& dets : scalersDets) { + std::cout << "Inputs:" << scalersInps.size() << std::endl; + for (auto const& dets : scalersInps) { stream << dets << " "; } stream << std::endl; @@ -87,7 +93,7 @@ void CTPScalerRecordO2::printFromZero(std::ostream& stream, CTPScalerRecordO2& r stream << "printFromZero Orbit:" << intRecord.orbit - record0.intRecord.orbit << " BC:" << intRecord.bc; stream << " miliSeconds:" << std::setprecision(20) << epochTime - record0.epochTime << std::endl; // this-record0 - for (int i = 0; i < scalers.size(); i++) { + for (uint32_t i = 0; i < scalers.size(); i++) { scalers[i].printFromZero(stream, record0.scalers[i]); } stream << std::endl; @@ -128,7 +134,7 @@ void CTPRunScalers::printFromZero(std::ostream& stream) const void CTPRunScalers::printClasses(std::ostream& stream) const { stream << "CTP classes:"; - for (int i = 0; i < mClassMask.size(); i++) { + for (uint32_t i = 0; i < mClassMask.size(); i++) { if (mClassMask[i]) { stream << " " << i; } @@ -145,6 +151,24 @@ std::vector CTPRunScalers::getClassIndexes() const } return indexes; } +// cls counted from 0 +int CTPRunScalers::getScalerIndexForClass(uint32_t cls) const +{ + if (cls >= 64) { + LOG(error) << "Class index out of range:" << cls; + return 255; + } + std::vector clslist = getClassIndexes(); + int i = 0; + for (auto const& clsl : clslist) { + if (cls == clsl) { + return i; + } + i++; + } + LOG(error) << " Class not found:" << cls; + return 255; +} int CTPRunScalers::readScalers(const std::string& rawscalers) { LOG(info) << "Loading CTP scalers."; @@ -152,7 +176,7 @@ int CTPRunScalers::readScalers(const std::string& rawscalers) int ret = 0; int level = 0; std::string line; - int nclasses = 0; + uint32_t nclasses = 0; int nlines = 0; while (std::getline(iss, line)) { o2::utils::Str::trim(line); @@ -172,7 +196,7 @@ int CTPRunScalers::readScalers(const std::string& rawscalers) } return 0; } -int CTPRunScalers::processScalerLine(const std::string& line, int& level, int& nclasses) +int CTPRunScalers::processScalerLine(const std::string& line, int& level, uint32_t& nclasses) { //std::cout << "Processing line" << std::endl; if (line.size() == 0) { @@ -205,13 +229,13 @@ int CTPRunScalers::processScalerLine(const std::string& line, int& level, int& n return 2; } else { mRunNumber = std::stol(tokens[0]); - int numofclasses = std::stoi(tokens[1]); + uint32_t numofclasses = std::stoi(tokens[1]); if ((numofclasses + 2) != ntokens) { LOG(error) << "Wrong syntax of second line in CTP scalers"; return 3; } mClassMask.reset(); - for (int i = 0; i < numofclasses; i++) { + for (uint32_t i = 0; i < numofclasses; i++) { int index = std::stoi(tokens[i + 2]); mClassMask[index] = 1; } @@ -273,19 +297,27 @@ int CTPRunScalers::convertRawToO2() overflows[i] = {0, 0, 0, 0, 0, 0}; } } + // Input overflows + std::array overflowsInputs = {48 * 0}; errorCounters eCnts; // 1st o2 rec is just copy CTPScalerRecordO2 o2rec; - copyRawToO2ScalerRecord(mScalerRecordRaw[0], o2rec, overflows); + copyRawToO2ScalerRecord(mScalerRecordRaw[0], o2rec, overflows, overflowsInputs); mScalerRecordO2.push_back(o2rec); int j = 1; for (uint32_t i = 1; i < mScalerRecordRaw.size(); i++) { //update overflows int ret = updateOverflows(mScalerRecordRaw[i - 1], mScalerRecordRaw[i], overflows); + // for(int k = 0; k < mClassMask.size(); k++) { + // if(mClassMask[k]) { + // LOG(info) << i << " " << k << " " << overflows[k][0] << " " << overflows[k][1] << " " << overflows[k][2] << " " << overflows[k][3] << " " << overflows[k][4] << " " << overflows[k][5]; + // } + // } // if (ret == 0) { CTPScalerRecordO2 o2rec; - copyRawToO2ScalerRecord(mScalerRecordRaw[i], o2rec, overflows); + ret = updateOverflowsInps(mScalerRecordRaw[i - 1], mScalerRecordRaw[i], overflowsInputs); + copyRawToO2ScalerRecord(mScalerRecordRaw[i], o2rec, overflows, overflowsInputs); mScalerRecordO2.push_back(o2rec); // Check consistency checkConsistency(mScalerRecordO2[j - 1], mScalerRecordO2[j], eCnts); @@ -295,7 +327,7 @@ int CTPRunScalers::convertRawToO2() eCnts.printStream(std::cout); return 0; } -int CTPRunScalers::copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows) +int CTPRunScalers::copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows, std::array& overflows) { if (rawrec.scalers.size() != (mClassMask.count())) { LOG(error) << "Inconsistent scaler record size:" << rawrec.scalers.size() << " Expected:" << mClassMask.count(); @@ -313,6 +345,10 @@ int CTPRunScalers::copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTP o2scal.createCTPScalerO2FromRaw(rawscal, classesoverflows[k]); o2rec.scalers.push_back(o2scal); } + for (uint32_t i = 0; i < rawrec.scalersInps.size(); i++) { + uint64_t inpo2 = (uint64_t)(rawrec.scalersInps[i]) + 0xffffffffull * (uint64_t)(overflows[i]); + o2rec.scalersInps.push_back(inpo2); + } return 0; } int CTPRunScalers::checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& scal1, errorCounters& eCnts) const @@ -365,24 +401,34 @@ int CTPRunScalers::checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& // LMB >= LMA >= L0B >= L0A >= L1B >= L1A: 5 relations // broken for classes started at L0 // - if ((scal1.lmAfter - scal0.lmAfter) > (scal1.lmBefore - scal0.lmBefore)) { + int64_t difThres = 6; + int64_t dif = (scal1.lmAfter - scal0.lmAfter) - (scal1.lmBefore - scal0.lmBefore); + if (dif <= difThres) { + eCnts.lmBlmAd1++; + } else if (dif > difThres) { eCnts.lmBlmA++; if (eCnts.lmBlmA < eCnts.MAXPRINT) { - LOG(error) << "LMA > LMB error:" << ((scal1.lmAfter - scal0.lmAfter) - (scal1.lmBefore - scal0.lmBefore)); + LOG(error) << "LMA > LMB error:" << dif; } ret++; } - if ((scal1.l0After - scal0.l0After) > (scal1.l0Before - scal0.l0Before)) { + dif = (scal1.l0After - scal0.l0After) - (scal1.l0Before - scal0.l0Before); + if (dif <= difThres) { + eCnts.l0Bl0Ad1++; + } else if (dif > difThres) { eCnts.l0Bl0A++; if (eCnts.l0Bl0A < eCnts.MAXPRINT) { - LOG(error) << "L0A > L0B error:" << ((scal1.l0After - scal0.l0After) - (scal1.l0Before - scal0.l0Before)); + LOG(error) << "L0A > L0B error:" << dif; } ret++; } - if ((scal1.l1After - scal0.l1After) > (scal1.l1Before - scal0.l1Before)) { + dif = (scal1.l0After - scal0.l0After) - (scal1.l0Before - scal0.l0Before); + if (dif <= difThres) { + eCnts.l1Bl1Ad1++; + } else if (dif > difThres) { eCnts.l1Bl1A++; if (eCnts.l1Bl1A < eCnts.MAXPRINT) { - LOG(error) << "L1A > L1B error:" << ((scal1.l0After - scal0.l0After) - (scal1.l0Before - scal0.l0Before)); + LOG(error) << "L1A > L1B error:" << dif; } ret++; } @@ -390,10 +436,15 @@ int CTPRunScalers::checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& // LOG(warning) << "L0B > LMA ok if L0 class."; // ret++; } - if ((scal1.l1Before - scal0.l1Before) > (scal1.l0After - scal0.l0After)) { + dif = (scal1.l1Before - scal0.l1Before) - (scal1.l0After - scal0.l0After); + // LOG(info) << "L1B L0A " << dif << " " << scal1.l1Before << " " << scal1.l0After << " " << scal0.l1Before << " " << scal0.l0After; + if (dif <= difThres) { + eCnts.l0Al1Bd1++; + } else if (dif > difThres) { eCnts.l0Al1B++; if (eCnts.l0Al1B < eCnts.MAXPRINT) { - LOG(error) << "L1B > L0A Before error:" << ((scal1.l1Before - scal0.l1Before) - (scal1.l0After - scal0.l0After)); + // LOG(error) << "L1B > L0A Before error:" << dif << " " << scal1.l1Before << " " << scal1.l0After << " " << scal0.l1Before << " " << scal0.l0After; + LOG(warning) << "L1B > L0A Before error:" << dif; } ret++; } @@ -414,7 +465,7 @@ int CTPRunScalers::updateOverflows(const CTPScalerRecordRaw& rec0, const CTPScal LOG(warning) << "rec0 orbit:" << rec0.intRecord.orbit << "> rec1 orbit:" << rec1.intRecord.orbit << " skipping this record"; return 1; } - for (int i = 0; i < rec0.scalers.size(); i++) { + for (uint32_t i = 0; i < rec0.scalers.size(); i++) { int k = (getClassIndexes())[i]; updateOverflows(rec0.scalers[i], rec1.scalers[i], classesoverflows[k]); } @@ -422,10 +473,11 @@ int CTPRunScalers::updateOverflows(const CTPScalerRecordRaw& rec0, const CTPScal } int CTPRunScalers::checkConsistency(const CTPScalerRecordO2& rec0, const CTPScalerRecordO2& rec1, errorCounters& eCnts) const { - for (int i = 0; i < rec0.scalers.size(); i++) { - checkConsistency(rec0.scalers[i], rec1.scalers[i], eCnts); + int ret = 0; + for (uint32_t i = 0; i < rec0.scalers.size(); i++) { + ret += checkConsistency(rec0.scalers[i], rec1.scalers[i], eCnts); } - return 0; + return ret; } int CTPRunScalers::updateOverflows(const CTPScalerRaw& scal0, const CTPScalerRaw& scal1, std::array& overflow) const { @@ -452,6 +504,33 @@ int CTPRunScalers::updateOverflows(const CTPScalerRaw& scal0, const CTPScalerRaw //std::cout << std::endl; return 0; } +// +int CTPRunScalers::updateOverflowsInps(const CTPScalerRecordRaw& rec0, const CTPScalerRecordRaw& rec1, std::array& overflow) const +{ + static int iPrint = 0; + uint32_t NINPS = 48; + if (rec0.scalersInps.size() < NINPS) { + if (iPrint < 1) { + LOG(warning) << "Input scalers not available. Size:" << rec0.scalersInps.size(); + iPrint++; + } + return 1; + } + if (rec1.scalersInps.size() < NINPS) { + if (iPrint < 1) { + LOG(warning) << "Input scalers not available. Size:" << rec0.scalersInps.size(); + iPrint++; + } + return 2; + } + for (uint32_t i = 0; i < NINPS; i++) { + if (rec0.scalersInps[i] > rec1.scalersInps[i]) { + overflow[i] += 1; + } + } + return 0; +} +// int CTPRunScalers::printRates() { if (mScalerRecordO2.size() == 0) { @@ -461,14 +540,14 @@ int CTPRunScalers::printRates() LOG(info) << "Scaler rates for run:" << mRunNumber; CTPScalerRecordO2* scalrec0 = &mScalerRecordO2[0]; uint32_t orbit0 = scalrec0->intRecord.orbit; - for (int i = 1; i < mScalerRecordO2.size(); i++) { + for (uint32_t i = 1; i < mScalerRecordO2.size(); i++) { CTPScalerRecordO2* scalrec1 = &mScalerRecordO2[i]; double_t tt = (double_t)(scalrec1->intRecord.orbit - scalrec0->intRecord.orbit); double_t tinrun = (double_t)(scalrec1->intRecord.orbit - orbit0); tt = tt * 88e-6; tinrun = tinrun * 88e-6; std::cout << "==> Time wrt to SOR [s]:" << tinrun << " time intervale[s]:" << tt << std::endl; - for (int j = 0; j < scalrec1->scalers.size(); j++) { + for (uint32_t j = 0; j < scalrec1->scalers.size(); j++) { CTPScalerO2* s0 = &(scalrec0->scalers[j]); CTPScalerO2* s1 = &(scalrec1->scalers[j]); double_t rMB = (s1->lmBefore - s0->lmBefore) / tt; @@ -497,7 +576,7 @@ int CTPRunScalers::printIntegrals() double_t timeL = mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime; LOG(info) << "Scaler Integrals for run:" << mRunNumber << " duration:" << timeL - time0; - for (int i = 0; i < mScalerRecordO2[0].scalers.size(); i++) { + for (uint32_t i = 0; i < mScalerRecordO2[0].scalers.size(); i++) { std::cout << i << " LMB " << mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].lmBefore - mScalerRecordO2[0].scalers[i].lmBefore << std::endl; std::cout << i << " LMA " << mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].lmAfter - mScalerRecordO2[0].scalers[i].lmAfter << std::endl; std::cout << i << " L0B " << mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].l0Before - mScalerRecordO2[0].scalers[i].l0Before << std::endl; @@ -507,8 +586,322 @@ int CTPRunScalers::printIntegrals() } return 0; } +// +// Input counting 1..48 +int CTPRunScalers::printInputRateAndIntegral(int inp) +{ + if (mScalerRecordO2.size() == 0) { + LOG(info) << "ScalerRecord is empty, doing nothing"; + return 1; + } + double_t time0 = mScalerRecordO2[0].epochTime; + double_t timeL = mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime; + int integral = mScalerRecordO2[mScalerRecordO2.size() - 1].scalersInps[inp - 1] - mScalerRecordO2[0].scalersInps[inp - 1]; + std::cout << "Scaler Integrals for run:" << mRunNumber << " duration:" << timeL - time0; + std::cout << " Input " << inp << " integral:" << integral << " rate:" << integral / (timeL - time0) << std::endl; + return 0; +} +// Prints class before counters for lumi +// Class counting 1..64 +int CTPRunScalers::printClassBRateAndIntegralII(int iclsindex) +{ + if (mScalerRecordO2.size() == 0) { + LOG(info) << "ScalerRecord is empty, doing nothing"; + return 1; + } + double_t time0 = mScalerRecordO2[0].epochTime; + double_t timeL = mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime; + int iscalerindex = getScalerIndexForClass(iclsindex); + if (iscalerindex != 255) { + int integral = mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[iscalerindex].lmBefore - mScalerRecordO2[0].scalers[iscalerindex].lmBefore; + std::cout << "Scaler Integrals for run:" << mRunNumber << " duration:" << timeL - time0; + std::cout << " Class index" << iclsindex << " integral:" << integral << " rate:" << integral / (timeL - time0) << std::endl; + return 0; + } + return 1; +} +// Prints class before counters for lumi +// Scaler Index Class counting 1..64 +// getScalerIndexForClass(int cls) shpild be called before to convert class index to scaler class index +int CTPRunScalers::printClassBRateAndIntegral(int iclsscalerindex) +{ + if (mScalerRecordO2.size() == 0) { + LOG(info) << "ScalerRecord is empty, doing nothing"; + return 1; + } + double_t time0 = mScalerRecordO2[0].epochTime; + double_t timeL = mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime; + { + int integral = mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[iclsscalerindex - 1].lmBefore - mScalerRecordO2[0].scalers[iclsscalerindex - 1].lmBefore; + std::cout << "Scaler Integrals for run:" << mRunNumber << " duration:" << timeL - time0; + std::cout << " Class scaler index:" << iclsscalerindex << " integral:" << integral << " rate:" << integral / (timeL - time0) << std::endl; + } + return 0; +} +// +void CTPRunScalers::printLMBRateVsT() const +{ + for (uint32_t i = 1; i < mScalerRecordO2.size(); i++) { // loop over time + auto prev = &mScalerRecordO2[i - 1]; + auto curr = &mScalerRecordO2[i]; + double_t tt = (double_t)(curr->intRecord.orbit - prev->intRecord.orbit); + tt = tt * 88e-6; + + for (int j = 0; j < 1; j++) { // loop over classes + auto s0 = &(prev->scalers[j]); // type CTPScalerO2* + auto s1 = &(curr->scalers[j]); + double_t rMB = (s1->lmBefore - s0->lmBefore) / tt; // rate + auto delta = curr->epochTime - prev->epochTime; + double rMB2 = (s1->lmBefore - s0->lmBefore) / delta; + std::cout << "Class " << j << " " << (unsigned long long)(prev->epochTime) << " " << (unsigned long long)(curr->epochTime) << " DeltaTime " << delta << " tt " << tt << " interactions / s " << rMB << " in Hz " << rMB2 << "\n"; + } + } +} +// +uint64_t CTPRunScalers::getLumiNoPuCorr(int classindex, int type) const +{ + if (type < 7) { + const auto s0 = mScalerRecordO2[0].scalers[classindex]; + const auto s1 = mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[classindex]; + switch (type) { + case 1: + return (s1.lmBefore - s0.lmBefore); + case 2: + return (s1.lmAfter - s0.lmAfter); + case 3: + return (s1.l0Before - s0.l0Before); + case 4: + return (s1.l0After - s0.l0After); + case 5: + return (s1.l1Before - s0.l1Before); + case 6: + return (s1.l1After - s0.l1After); + default: + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } + } else if (type == 7) { + auto s0 = mScalerRecordO2[0].scalersInps[classindex]; // type CTPScalerO2* + auto s1 = mScalerRecordO2[mScalerRecordO2.size() - 1].scalersInps[classindex]; + return (s1 - s0); + } else { + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } +}; +// +std::vector> CTPRunScalers::getRatesForIndex(int classindex, int type) const +{ + std::vector> scals; + for (int i = 0; i < mScalerRecordO2.size() - 1; i++) { + double_t diff = 0; + // double_t timeDiff = mScalerRecordO2[i + 1].epochTime - mScalerRecordO2[i].epochTime; + double_t timeDiff = (mScalerRecordO2[i + 1].intRecord.orbit - mScalerRecordO2[i].intRecord.orbit) * o2::constants::lhc::LHCOrbitMUS / 1.e6; + if (type < 7) { + const auto s0 = mScalerRecordO2[i].scalers[classindex]; + const auto s1 = mScalerRecordO2[i + 1].scalers[classindex]; + if (type == 1) { + diff = s1.lmBefore - s0.lmBefore; + } else if (type == 2) { + diff = s1.lmAfter - s0.lmAfter; + } else if (type == 3) { + diff = s1.l0Before - s0.l0Before; + } else if (type == 4) { + diff = s1.l0After - s0.l0After; + } else if (type == 5) { + diff = s1.l1Before - s0.l1Before; + } else if (type == 6) { + diff = s1.l1After - s0.l1After; + } else { + LOG(error) << "Wrong type:" << type; + return scals; // wrong type + } + } else if (type == 7) { + auto s0 = mScalerRecordO2[i].scalersInps[classindex]; // type CTPScalerO2* + auto s1 = mScalerRecordO2[i + 1].scalersInps[classindex]; + diff = s1 - s0; + } else { + LOG(error) << "Wrong type:" << type; + return scals; // wrong type + } + scals.emplace_back(std::pair{diff, timeDiff}); + } + return scals; +}; +// returns the pair of global (levelled) interaction rate, as well as instantaneous interpolated +// rate in Hz at a certain orbit number within the run +// type - 7 : inputs +// type - 1..6 : lmb,lma,l0b,l0a,l1b,l1a +std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, int type, bool qc) const +{ + if (mScalerRecordO2.size() <= 1) { + LOG(error) << "not enough data"; + return std::make_pair(-1., -1.); + } + + // assumption: mScalerRecordO2 is arranged in increasing + // orbit numbers + + // then we can use binary search to find the right entries + auto iter = std::lower_bound(mScalerRecordO2.begin(), mScalerRecordO2.end(), orbit, [&](CTPScalerRecordO2 const& a, uint32_t value) { return a.intRecord.orbit <= value; }); + auto nextindex = std::distance(mScalerRecordO2.begin(), iter); // this points to the first index that has orbit greater or equal to given orbit + + auto calcRate = [&](auto index1, auto index2) -> double { + const auto& snext = mScalerRecordO2[index2]; + const auto& sprev = mScalerRecordO2[index1]; + auto timedelta = (snext.intRecord.orbit - sprev.intRecord.orbit) * 88.e-6; // converts orbits into time + if (type < 7) { + const auto& s0 = sprev.scalers[classindex]; // type CTPScalerO2* + const auto& s1 = snext.scalers[classindex]; + switch (type) { + case 1: + return (s1.lmBefore - s0.lmBefore) / timedelta; + case 2: + return (s1.lmAfter - s0.lmAfter) / timedelta; + case 3: + return (s1.l0Before - s0.l0Before) / timedelta; + case 4: + return (s1.l0After - s0.l0After) / timedelta; + case 5: + return (s1.l1Before - s0.l1Before) / timedelta; + case 6: + return (s1.l1After - s0.l1After) / timedelta; + default: + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } + } else if (type == 7) { + auto s0 = sprev.scalersInps[classindex]; // type CTPScalerO2* + auto s1 = snext.scalersInps[classindex]; + return (s1 - s0) / timedelta; + } else { + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } + }; + // qc flag decides what to return if time outside run + if (nextindex == 0) { + // orbit is out of bounds + if (qc == 0) { + LOG(info) << "query orbit " << orbit << " before first record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query orbit " << orbit << " before first record; Returning the first rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* first rate */ calcRate(0, 1)); + } + } else if (nextindex == mScalerRecordO2.size()) { + if (qc == 0) { + LOG(info) << "query orbit " << orbit << " after last record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query orbit " << orbit << " after last record; Returning the last rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* last rate */ calcRate(mScalerRecordO2.size() - 2, mScalerRecordO2.size() - 1)); + } + } else { + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ calcRate(nextindex - 1, nextindex)); + } + return std::make_pair(-1., -1.); +} +// returns the pair of global (levelled) interaction rate, as well as instantaneous interpolated +// rate in Hz at a certain orbit number within the run +// type - 7 : inputs +// type - 1..6 : lmb,lma,l0b,l0a,l1b,l1a +std::pair CTPRunScalers::getRateGivenT(double timestamp, int classindex, int type, bool qc) const +{ + if (mScalerRecordO2.size() <= 1) { + LOG(error) << "not enough data"; + return std::make_pair(-1., -1.); + } + + // assumption: mScalerRecordO2 is arranged in increasing + // orbit numbers + + // then we can use binary search to find the right entries + auto iter = std::lower_bound(mScalerRecordO2.begin(), mScalerRecordO2.end(), timestamp, [&](CTPScalerRecordO2 const& a, double value) { return a.epochTime <= value; }); + // this points to the first index that has orbit greater to given orbit; + // If this is 0, it means that the above condition was false from the beginning, basically saying that the timestamp is below any of the ScalerRecords' orbits. + // If this is mScalerRecordO2.size(), it means mScalerRecordO2.end() was returned, condition was met throughout all ScalerRecords, basically saying the timestamp is above any of the ScalarRecordss orbits. + auto nextindex = std::distance(mScalerRecordO2.begin(), iter); + + auto calcRate = [&](auto index1, auto index2) -> double { + const auto& snext = mScalerRecordO2[index2]; + const auto& sprev = mScalerRecordO2[index1]; + auto timedelta = (snext.intRecord.orbit - sprev.intRecord.orbit) * 88.e-6; // converts orbits into time + // std::cout << "timedelta:" << timedelta << std::endl; + if (type < 7) { + const auto& s0 = sprev.scalers[classindex]; // type CTPScalerO2* + const auto& s1 = snext.scalers[classindex]; + switch (type) { + case 1: + return (s1.lmBefore - s0.lmBefore) / timedelta; + case 2: + return (s1.lmAfter - s0.lmAfter) / timedelta; + case 3: + return (s1.l0Before - s0.l0Before) / timedelta; + case 4: + return (s1.l0After - s0.l0After) / timedelta; + case 5: + return (s1.l1Before - s0.l1Before) / timedelta; + case 6: + return (s1.l1After - s0.l1After) / timedelta; + default: + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } + } else if (type == 7) { + // LOG(info) << "doing input:"; + auto s0 = sprev.scalersInps[classindex]; // type CTPScalerO2* + auto s1 = snext.scalersInps[classindex]; + return (s1 - s0) / timedelta; + } else { + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } + }; + if (nextindex == 0) { + // orbit is out of bounds + if (qc == 0) { + LOG(info) << "query timestamp " << (long)timestamp << " before first record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query timestamp " << (long)timestamp << " before first record; Returning the first rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* first rate */ calcRate(0, 1)); + } + } else if (nextindex == mScalerRecordO2.size()) { + if (qc == 0) { + LOG(info) << "query timestamp " << (long)timestamp << " after last record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query timestamp " << (long)timestamp << " after last record; Returning the last rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* last rate */ calcRate(mScalerRecordO2.size() - 2, mScalerRecordO2.size() - 1)); + } + } else { + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ calcRate(nextindex - 1, nextindex)); + } + return std::make_pair(-1., -1.); +} +// Offset orbit of all records +// +int CTPRunScalers::addOrbitOffset(uint32_t offset) +{ + int over = 0; + LOG(info) << "Subtracting from orbit " << offset; + for (auto& screc : mScalerRecordRaw) { + uint32_t orbit = screc.intRecord.orbit; + uint32_t orbitnew = 0; + orbitnew = orbit - offset; + if (orbit < offset) { + over++; + } + screc.intRecord.orbit = orbitnew; + } + if (over != 0 && over != mScalerRecordRaw.size()) { + LOG(warning) << "Orbit overflow inside run. Run:" << mRunNumber; + } + return 0; +} std::vector CTPRunScalers::scalerNames = { "runn0", "runn1", "runn2", "runn3", "runn4", "runn5", "runn6", "runn7", "runn8", "runn9", "runn10", "runn11", "runn12", "runn13", "runn14", "runn15", "ltg1_ORB", "ltg1_HB", "ltg1_HBr", "ltg1_HC", "ltg1_PH", "ltg1_PP", "ltg1_CAL", "ltg1_SOT", "ltg1_EOT", "ltg1_SOC", "ltg1_EOC", "ltg1_TF", "ltg1_FERST", "ltg1_RT", "ltg1_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg1_GAP1", "ltg1_GAP2", "ltg1_TPC_sync", "ltg1_TPC_rst", "ltg1_TOF", "ltg2_ORB", "ltg2_HB", "ltg2_HBr", "ltg2_HC", "ltg2_PH", "ltg2_PP", "ltg2_CAL", "ltg2_SOT", "ltg2_EOT", "ltg2_SOC", "ltg2_EOC", "ltg2_TF", "ltg2_FERST", "ltg2_RT", "ltg2_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg2_GAP1", "ltg2_GAP2", "ltg2_TPC_sync", "ltg2_TPC_rst", "ltg2_TOF", "ltg3_ORB", "ltg3_HB", "ltg3_HBr", "ltg3_HC", "ltg3_PH", "ltg3_PP", "ltg3_CAL", "ltg3_SOT", "ltg3_EOT", "ltg3_SOC", "ltg3_EOC", "ltg3_TF", "ltg3_FERST", "ltg3_RT", "ltg3_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg3_GAP1", "ltg3_GAP2", "ltg3_TPC_sync", "ltg3_TPC_rst", "ltg3_TOF", "ltg4_ORB", "ltg4_HB", "ltg4_HBr", "ltg4_HC", "ltg4_PH", "ltg4_PP", "ltg4_CAL", "ltg4_SOT", "ltg4_EOT", "ltg4_SOC", "ltg4_EOC", "ltg4_TF", "ltg4_FERST", "ltg4_RT", "ltg4_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg4_GAP1", "ltg4_GAP2", "ltg4_TPC_sync", "ltg4_TPC_rst", "ltg4_TOF", "ltg5_ORB", "ltg5_HB", "ltg5_HBr", "ltg5_HC", "ltg5_PH", "ltg5_PP", "ltg5_CAL", "ltg5_SOT", "ltg5_EOT", "ltg5_SOC", "ltg5_EOC", "ltg5_TF", "ltg5_FERST", "ltg5_RT", "ltg5_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg5_GAP1", "ltg5_GAP2", "ltg5_TPC_sync", "ltg5_TPC_rst", "ltg5_TOF", "ltg6_ORB", "ltg6_HB", "ltg6_HBr", "ltg6_HC", "ltg6_PH", "ltg6_PP", "ltg6_CAL", "ltg6_SOT", "ltg6_EOT", "ltg6_SOC", "ltg6_EOC", "ltg6_TF", "ltg6_FERST", "ltg6_RT", "ltg6_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg6_GAP1", "ltg6_GAP2", "ltg6_TPC_sync", "ltg6_TPC_rst", "ltg6_TOF", "ltg7_ORB", "ltg7_HB", "ltg7_HBr", "ltg7_HC", "ltg7_PH", "ltg7_PP", "ltg7_CAL", "ltg7_SOT", "ltg7_EOT", "ltg7_SOC", "ltg7_EOC", "ltg7_TF", "ltg7_FERST", "ltg7_RT", "ltg7_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg7_GAP1", "ltg7_GAP2", "ltg7_TPC_sync", "ltg7_TPC_rst", "ltg7_TOF", "ltg8_ORB", "ltg8_HB", "ltg8_HBr", "ltg8_HC", "ltg8_PH", "ltg8_PP", "ltg8_CAL", "ltg8_SOT", "ltg8_EOT", "ltg8_SOC", "ltg8_EOC", "ltg8_TF", "ltg8_FERST", "ltg8_RT", "ltg8_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg8_GAP1", "ltg8_GAP2", "ltg8_TPC_sync", "ltg8_TPC_rst", "ltg8_TOF", "ltg9_ORB", "ltg9_HB", "ltg9_HBr", "ltg9_HC", "ltg9_PH", "ltg9_PP", "ltg9_CAL", "ltg9_SOT", "ltg9_EOT", "ltg9_SOC", "ltg9_EOC", "ltg9_TF", "ltg9_FERST", "ltg9_RT", "ltg9_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg9_GAP1", "ltg9_GAP2", "ltg9_TPC_sync", "ltg9_TPC_rst", "ltg9_TOF", "ltg10_ORB", "ltg10_HB", "ltg10_HBr", "ltg10_HC", "ltg10_PH", "ltg10_PP", "ltg10_CAL", "ltg10_SOT", "ltg10_EOT", "ltg10_SOC", "ltg10_EOC", "ltg10_TF", "ltg10_FERST", "ltg10_RT", "ltg10_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg10_GAP1", "ltg10_GAP2", "ltg10_TPC_sync", "ltg10_TPC_rst", "ltg10_TOF", "ltg11_ORB", "ltg11_HB", "ltg11_HBr", "ltg11_HC", "ltg11_PH", "ltg11_PP", "ltg11_CAL", "ltg11_SOT", "ltg11_EOT", "ltg11_SOC", "ltg11_EOC", "ltg11_TF", "ltg11_FERST", "ltg11_RT", "ltg11_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg11_GAP1", "ltg11_GAP2", "ltg11_TPC_sync", "ltg11_TPC_rst", "ltg11_TOF", "ltg12_ORB", "ltg12_HB", "ltg12_HBr", "ltg12_HC", "ltg12_PH", "ltg12_PP", "ltg12_CAL", "ltg12_SOT", "ltg12_EOT", "ltg12_SOC", "ltg12_EOC", "ltg12_TF", "ltg12_FERST", "ltg12_RT", "ltg12_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg12_GAP1", "ltg12_GAP2", "ltg12_TPC_sync", "ltg12_TPC_rst", "ltg12_TOF", "ltg13_ORB", "ltg13_HB", "ltg13_HBr", "ltg13_HC", "ltg13_PH", "ltg13_PP", "ltg13_CAL", "ltg13_SOT", "ltg13_EOT", "ltg13_SOC", "ltg13_EOC", "ltg13_TF", "ltg13_FERST", "ltg13_RT", "ltg13_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg13_GAP1", "ltg13_GAP2", "ltg13_TPC_sync", "ltg13_TPC_rst", "ltg13_TOF", "ltg14_ORB", "ltg14_HB", "ltg14_HBr", "ltg14_HC", "ltg14_PH", "ltg14_PP", "ltg14_CAL", "ltg14_SOT", "ltg14_EOT", "ltg14_SOC", "ltg14_EOC", "ltg14_TF", "ltg14_FERST", "ltg14_RT", "ltg14_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg14_GAP1", "ltg14_GAP2", "ltg14_TPC_sync", "ltg14_TPC_rst", "ltg14_TOF", "ltg15_ORB", "ltg15_HB", "ltg15_HBr", "ltg15_HC", "ltg15_PH", "ltg15_PP", "ltg15_CAL", "ltg15_SOT", "ltg15_EOT", "ltg15_SOC", "ltg15_EOC", "ltg15_TF", "ltg15_FERST", "ltg15_RT", "ltg15_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg15_GAP1", "ltg15_GAP2", "ltg15_TPC_sync", "ltg15_TPC_rst", "ltg15_TOF", "ltg16_ORB", "ltg16_HB", "ltg16_HBr", "ltg16_HC", "ltg16_PH", "ltg16_PP", "ltg16_CAL", "ltg16_SOT", "ltg16_EOT", "ltg16_SOC", "ltg16_EOC", "ltg16_TF", "ltg16_FERST", "ltg16_RT", "ltg16_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg16_GAP1", "ltg16_GAP2", "ltg16_TPC_sync", "ltg16_TPC_rst", "ltg16_TOF", "ltg17_ORB", "ltg17_HB", "ltg17_HBr", "ltg17_HC", "ltg17_PH", "ltg17_PP", "ltg17_CAL", "ltg17_SOT", "ltg17_EOT", "ltg17_SOC", "ltg17_EOC", "ltg17_TF", "ltg17_FERST", "ltg17_RT", "ltg17_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg17_GAP1", "ltg17_GAP2", "ltg17_TPC_sync", "ltg17_TPC_rst", "ltg17_TOF", "ltg18_ORB", "ltg18_HB", "ltg18_HBr", "ltg18_HC", "ltg18_PH", "ltg18_PP", "ltg18_CAL", "ltg18_SOT", "ltg18_EOT", "ltg18_SOC", "ltg18_EOC", "ltg18_TF", "ltg18_FERST", "ltg18_RT", "ltg18_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg18_GAP1", "ltg18_GAP2", "ltg18_TPC_sync", "ltg18_TPC_rst", "ltg18_TOF", "bc40", "clk240", "extorb", "PLSRin", "FastLMin", "BUSYin", "SPAREin", "inp1", "inp2", "inp3", "inp4", "inp5", "inp6", "inp7", "inp8", "inp9", "inp10", "inp11", "inp12", "inp13", "inp14", "inp15", "inp16", "inp17", "inp18", "inp19", "inp20", "inp21", "inp22", "inp23", "inp24", "inp25", "inp26", "inp27", "inp28", "inp29", "inp30", "inp31", "inp32", "inp33", "inp34", "inp35", "inp36", "inp37", "inp38", "inp39", "inp40", "inp41", "inp42", "inp43", "inp44", "inp45", "inp46", "inp47", "inp48", "clamb1", "clamb2", "clamb3", "clamb4", "clamb5", "clamb6", "clamb7", "clamb8", "clamb9", "clamb10", "clamb11", "clamb12", "clamb13", "clamb14", "clamb15", "clamb16", "clamb17", "clamb18", "clamb19", "clamb20", "clamb21", "clamb22", "clamb23", "clamb24", "clamb25", "clamb26", "clamb27", "clamb28", "clamb29", "clamb30", "clamb31", "clamb32", "clamb33", "clamb34", "clamb35", "clamb36", "clamb37", "clamb38", "clamb39", "clamb40", "clamb41", "clamb42", "clamb43", "clamb44", "clamb45", "clamb46", "clamb47", "clamb48", "clamb49", "clamb50", "clamb51", "clamb52", "clamb53", "clamb54", "clamb55", "clamb56", "clamb57", "clamb58", "clamb59", "clamb60", "clamb61", "clamb62", "clamb63", "clamb64", "clama1", "clama2", "clama3", "clama4", "clama5", "clama6", "clama7", "clama8", "clama9", "clama10", "clama11", "clama12", "clama13", "clama14", "clama15", "clama16", "clama17", "clama18", "clama19", "clama20", "clama21", "clama22", "clama23", "clama24", "clama25", "clama26", "clama27", "clama28", "clama29", "clama30", "clama31", "clama32", "clama33", "clama34", "clama35", "clama36", "clama37", "clama38", "clama39", "clama40", "clama41", "clama42", "clama43", "clama44", "clama45", "clama46", "clama47", "clama48", "clama49", "clama50", "clama51", "clama52", "clama53", "clama54", "clama55", "clama56", "clama57", "clama58", "clama59", "clama60", "clama61", "clama62", "clama63", "clama64", "cla0b1", "cla0b2", "cla0b3", "cla0b4", "cla0b5", "cla0b6", "cla0b7", "cla0b8", "cla0b9", "cla0b10", "cla0b11", "cla0b12", "cla0b13", "cla0b14", "cla0b15", "cla0b16", "cla0b17", "cla0b18", "cla0b19", "cla0b20", "cla0b21", "cla0b22", "cla0b23", "cla0b24", "cla0b25", "cla0b26", "cla0b27", "cla0b28", "cla0b29", "cla0b30", "cla0b31", "cla0b32", "cla0b33", "cla0b34", "cla0b35", "cla0b36", "cla0b37", "cla0b38", "cla0b39", "cla0b40", "cla0b41", "cla0b42", "cla0b43", "cla0b44", "cla0b45", "cla0b46", "cla0b47", "cla0b48", "cla0b49", "cla0b50", "cla0b51", "cla0b52", "cla0b53", "cla0b54", "cla0b55", "cla0b56", "cla0b57", "cla0b58", "cla0b59", "cla0b60", "cla0b61", "cla0b62", "cla0b63", "cla0b64", "cla0a1", "cla0a2", "cla0a3", "cla0a4", "cla0a5", "cla0a6", "cla0a7", "cla0a8", "cla0a9", "cla0a10", "cla0a11", "cla0a12", "cla0a13", "cla0a14", "cla0a15", "cla0a16", "cla0a17", "cla0a18", "cla0a19", "cla0a20", "cla0a21", "cla0a22", "cla0a23", "cla0a24", "cla0a25", "cla0a26", "cla0a27", "cla0a28", "cla0a29", "cla0a30", "cla0a31", "cla0a32", "cla0a33", "cla0a34", "cla0a35", "cla0a36", "cla0a37", "cla0a38", "cla0a39", "cla0a40", "cla0a41", "cla0a42", "cla0a43", "cla0a44", "cla0a45", "cla0a46", "cla0a47", "cla0a48", "cla0a49", "cla0a50", "cla0a51", "cla0a52", "cla0a53", "cla0a54", "cla0a55", "cla0a56", "cla0a57", "cla0a58", "cla0a59", "cla0a60", "cla0a61", "cla0a62", "cla0a63", "cla0a64", "cla1b1", "cla1b2", "cla1b3", "cla1b4", "cla1b5", "cla1b6", "cla1b7", "cla1b8", "cla1b9", "cla1b10", "cla1b11", "cla1b12", "cla1b13", "cla1b14", "cla1b15", "cla1b16", "cla1b17", "cla1b18", "cla1b19", "cla1b20", "cla1b21", "cla1b22", "cla1b23", "cla1b24", "cla1b25", "cla1b26", "cla1b27", "cla1b28", "cla1b29", "cla1b30", "cla1b31", "cla1b32", "cla1b33", "cla1b34", "cla1b35", "cla1b36", "cla1b37", "cla1b38", "cla1b39", "cla1b40", "cla1b41", "cla1b42", "cla1b43", "cla1b44", "cla1b45", "cla1b46", "cla1b47", "cla1b48", "cla1b49", "cla1b50", "cla1b51", "cla1b52", "cla1b53", "cla1b54", "cla1b55", "cla1b56", "cla1b57", "cla1b58", "cla1b59", "cla1b60", "cla1b61", "cla1b62", "cla1b63", "cla1b64", "cla1a1", "cla1a2", "cla1a3", "cla1a4", "cla1a5", "cla1a6", "cla1a7", "cla1a8", "cla1a9", "cla1a10", "cla1a11", "cla1a12", "cla1a13", "cla1a14", "cla1a15", "cla1a16", "cla1a17", "cla1a18", "cla1a19", "cla1a20", "cla1a21", "cla1a22", "cla1a23", "cla1a24", "cla1a25", "cla1a26", "cla1a27", "cla1a28", "cla1a29", "cla1a30", "cla1a31", "cla1a32", "cla1a33", "cla1a34", "cla1a35", "cla1a36", "cla1a37", "cla1a38", "cla1a39", "cla1a40", "cla1a41", "cla1a42", "cla1a43", "cla1a44", "cla1a45", "cla1a46", "cla1a47", "cla1a48", "cla1a49", "cla1a50", "cla1a51", "cla1a52", "cla1a53", "cla1a54", "cla1a55", "cla1a56", "cla1a57", "cla1a58", "cla1a59", "cla1a60", "cla1a61", "cla1a62", "cla1a63", "cla1a64", "l0_trigger", "l1_trigger", "l2_trigger", "clum1", "clum2", "clum3", "clum4", "clum5", "clum6", "clu01", "clu02", "clu03", "clu04", "clu05", "clu06", "clu11", "clu12", "clu13", "clu14", "clu15", "clu16", "ltg1_busy", "ltg2_busy", "ltg3_busy", "ltg4_busy", "ltg5_busy", "ltg6_busy", "ltg7_busy", "ltg8_busy", "ltg9_busy", - "ltg10_busy", "ltg11_busy", "ltg12_busy", "ltg13_busy", "ltg14_busy", "ltg15_busy", "ltg16_busy", "ltg17_busy", "ltg18_busy", "orbitid"}; + "ltg10_busy", "ltg11_busy", "ltg12_busy", "ltg13_busy", "ltg14_busy", "ltg15_busy", "ltg16_busy", "ltg17_busy", "ltg18_busy", "orbitid", "clum7", "clum8", "clu07", "clu08", "clu17", "clu18", "clu_busy1", "clu_busy2", "clu_busy3", "clu_busy4", "clu_busy5", "clu_busy6", "clu_busy7", "clu_busy8"}; diff --git a/DataFormats/Detectors/Common/CMakeLists.txt b/DataFormats/Detectors/Common/CMakeLists.txt index 6b090771f292a..bbc0aaf9e8320 100644 --- a/DataFormats/Detectors/Common/CMakeLists.txt +++ b/DataFormats/Detectors/Common/CMakeLists.txt @@ -52,3 +52,14 @@ o2_add_test(DetID PUBLIC_LINK_LIBRARIES O2::DetectorsCommonDataFormats COMPONENT_NAME DetectorsCommonDataFormats LABELS dataformats) + +o2_add_test(CTFEntropyCoder + NAME CTFEntropyCoder + SOURCES test/testCTFEntropyCoder.cxx + PUBLIC_LINK_LIBRARIES O2::DetectorsCommonDataFormats O2::rANS + COMPONENT_NAME DetectorsCommonDataFormats + TARGETVARNAME TEST_CTF_ENTROPY_CODER + LABELS detectorsbase) +if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") + target_compile_options(${TEST_CTF_ENTROPY_CODER} PRIVATE -march=native) +endif() diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/ANSHeader.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/ANSHeader.h new file mode 100644 index 0000000000000..110af41cddd49 --- /dev/null +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/ANSHeader.h @@ -0,0 +1,70 @@ +// Copyright 2019-2023 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ANSHeader.h +/// \author michael.lettrich@cern.ch +/// \brief representation of ANS Version number in a comparable way + +#ifndef _ALICEO2_ANSHEADER_H_ +#define _ALICEO2_ANSHEADER_H_ + +#include +#include +#include +#include + +namespace o2::ctf +{ + +struct ANSHeader { + uint8_t majorVersion; + uint8_t minorVersion; + + void clear() { majorVersion = minorVersion = 0; } + inline constexpr operator uint16_t() const noexcept + { + uint16_t major = majorVersion; + uint16_t minor = minorVersion; + return (major << 8) | minor; + }; + + inline operator std::string() const + { + return fmt::format("{}.{}", majorVersion, minorVersion); + }; + inline constexpr uint32_t version() const noexcept { return static_cast(*this); }; + inline constexpr bool operator==(const ANSHeader& other) const noexcept { return static_cast(*this) == static_cast(other); }; + inline constexpr bool operator!=(const ANSHeader& other) const noexcept { return static_cast(*this) != static_cast(other); }; + inline constexpr bool operator<(const ANSHeader& other) const noexcept { return static_cast(*this) < static_cast(other); }; + inline constexpr bool operator>(const ANSHeader& other) const noexcept { return static_cast(*this) > static_cast(other); }; + inline constexpr bool operator>=(const ANSHeader& other) const noexcept { return static_cast(*this) >= static_cast(other); }; + inline constexpr bool operator<=(const ANSHeader& other) const noexcept { return static_cast(*this) <= static_cast(other); }; + ClassDefNV(ANSHeader, 2); +}; + +inline constexpr ANSHeader ANSVersionUnspecified{0, 0}; +inline constexpr ANSHeader ANSVersionCompat{0, 1}; +inline constexpr ANSHeader ANSVersion1{1, 0}; + +inline ANSHeader ansVersionFromString(const std::string& ansVersionString) +{ + if (ansVersionString == "0.1" || ansVersionString == "compat") { + return ctf::ANSVersionCompat; + } else if (ansVersionString == "1.0") { + return ctf::ANSVersion1; + } else { + return ctf::ANSVersionUnspecified; + } +} + +} // namespace o2::ctf + +#endif /* _ALICEO2_ANSHEADER_H_ */ diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h index 74610264336be..2eff4aeb78b53 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h @@ -37,9 +37,12 @@ class AlignParam AlignParam(const char* symname, int algID, // volume symbolic name and its alignable ID double x, double y, double z, // delta translation double psi, double theta, double phi, // delta rotation - bool global = true); // global (preferable) or local delta definition + bool global = true, // global (preferable) or local delta definition + bool convertLocalToGlobal = true); // if local is provided, convert it to global - AlignParam(const char* symname, int algID, TGeoMatrix& m, bool global = true); + AlignParam(const char* symname, int algID, TGeoMatrix& m, + bool global = true, // global (preferable) or local delta definition + bool convertLocalToGlobal = true); // if local is provided, convert it to global /// return symbolic name of the volume const std::string& getSymName() const { return mSymName; } @@ -52,7 +55,7 @@ class AlignParam double getZ() const { return mZ; } /// apply object to geoemetry - bool applyToGeometry() const; + bool applyToGeometry(int printLevel = -1) const; /// extract global delta matrix TGeoHMatrix createMatrix() const; @@ -70,6 +73,9 @@ class AlignParam void setAlignableID(int id) { mAlignableID = id; } /// ================ methods for direct setting of delta params + /// set parameters + void setParams(double x, double y, double z, double psi, double theta, double phi); + /// set parameters of global delta void setGlobalParams(double x, double y, double z, double psi, double theta, double phi); @@ -110,10 +116,13 @@ class AlignParam int getLevel() const; - void print() const; + void print(bool printLocal = false) const; int rectify(double zero = 1e-13); + bool isGlobal() const { return mIsGlobal; } + void setIsGlobal(bool v) { mIsGlobal = v; } + protected: bool matrixToAngles(const double* rot, double& psi, double& theta, double& phi) const; void anglesToMatrix(double psi, double theta, double phi, double* rot) const; @@ -121,22 +130,22 @@ class AlignParam void setMatrixTranslation(double x, double y, double z, TGeoHMatrix& dest) const; private: - std::string mSymName{}; + std::string mSymName; + bool mIsGlobal = true; /// is this global delta? int mAlignableID = -1; /// alignable ID (set for sensors only) - - double mX = 0.; ///< X translation of global delta - double mY = 0.; ///< Y translation of global delta - double mZ = 0.; ///< Z translation of global delta + double mX = 0.; ///< X translation of global delta + double mY = 0.; ///< Y translation of global delta + double mZ = 0.; ///< Z translation of global delta double mPsi = 0.; ///< "pitch" : Euler angle of rotation around final X axis (radians) double mTheta = 0.; ///< "roll" : Euler angle of rotation around Y axis after 1st rotation (radians) double mPhi = 0.; ///< "yaw" : Euler angle of rotation around Z axis (radians) - ClassDefNV(AlignParam, 1); + ClassDefNV(AlignParam, 2); }; -} -} +} // namespace detectors +} // namespace o2 #endif diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h index d8dd485fa0852..2d2383783cfc3 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h @@ -83,7 +83,12 @@ class DetID static constexpr ID TRK = 19; static constexpr ID FT3 = 20; static constexpr ID FCT = 21; - static constexpr ID Last = FCT; + static constexpr ID TF3 = 22; + static constexpr ID RCH = 23; + static constexpr ID MI3 = 24; + static constexpr ID ECL = 25; + static constexpr ID FD3 = 26; + static constexpr ID Last = FD3; #else static constexpr ID Last = FOC; ///< if extra detectors added, update this !!! #endif @@ -177,7 +182,7 @@ class DetID // detector names, will be defined in DataSources static constexpr const char* sDetNames[nDetectors + 1] = ///< defined detector names #ifdef ENABLE_UPGRADES - {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "TST", "CTP", "FOC", "IT3", "TRK", "FT3", "FCT", nullptr}; + {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "TST", "CTP", "FOC", "IT3", "TRK", "FT3", "FCT", "TF3", "RCH", "MI3", "ECL", "FD3", nullptr}; #else {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "TST", "CTP", "FOC", nullptr}; #endif @@ -190,7 +195,8 @@ class DetID o2h::gDataOriginTST, o2h::gDataOriginCTP, o2h::gDataOriginFOC #ifdef ENABLE_UPGRADES , - o2h::gDataOriginIT3, o2h::gDataOriginTRK, o2h::gDataOriginFT3, o2h::gDataOriginFCT + o2h::gDataOriginIT3, o2h::gDataOriginTRK, o2h::gDataOriginFT3, o2h::gDataOriginFCT, o2h::gDataOriginTF3, + o2h::gDataOriginRCH, o2h::gDataOriginMI3, o2h::gDataOriginECL, o2h::gDataOriginFD3 #endif }; #endif // GPUCA_GPUCODE_DEVICE @@ -206,9 +212,11 @@ GPUconstexpr() DetID::mask_t sMasks[DetID::nDetectors] = ///< detectot masks DetID::mask_t(math_utils::bit2Mask(DetID::CPV)), DetID::mask_t(math_utils::bit2Mask(DetID::EMC)), DetID::mask_t(math_utils::bit2Mask(DetID::HMP)), DetID::mask_t(math_utils::bit2Mask(DetID::MFT)), DetID::mask_t(math_utils::bit2Mask(DetID::MCH)), DetID::mask_t(math_utils::bit2Mask(DetID::MID)), DetID::mask_t(math_utils::bit2Mask(DetID::ZDC)), DetID::mask_t(math_utils::bit2Mask(DetID::FT0)), DetID::mask_t(math_utils::bit2Mask(DetID::FV0)), DetID::mask_t(math_utils::bit2Mask(DetID::FDD)), DetID::mask_t(math_utils::bit2Mask(DetID::TST)), DetID::mask_t(math_utils::bit2Mask(DetID::CTP)), DetID::mask_t(math_utils::bit2Mask(DetID::FOC)) + #ifdef ENABLE_UPGRADES , - DetID::mask_t(math_utils::bit2Mask(DetID::IT3)), DetID::mask_t(math_utils::bit2Mask(DetID::TRK)), DetID::mask_t(math_utils::bit2Mask(DetID::FT3)), DetID::mask_t(math_utils::bit2Mask(DetID::FCT)) + DetID::mask_t(math_utils::bit2Mask(DetID::IT3)), DetID::mask_t(math_utils::bit2Mask(DetID::TRK)), DetID::mask_t(math_utils::bit2Mask(DetID::FT3)), DetID::mask_t(math_utils::bit2Mask(DetID::FCT)), DetID::mask_t(math_utils::bit2Mask(DetID::TF3)), + DetID::mask_t(math_utils::bit2Mask(DetID::RCH)), DetID::mask_t(math_utils::bit2Mask(DetID::MI3)), DetID::mask_t(math_utils::bit2Mask(DetID::ECL)), DetID::mask_t(math_utils::bit2Mask(DetID::FD3)) #endif }; } // namespace detid_internal diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h index e29067e6d6030..ba6b853f7fb23 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h @@ -16,18 +16,31 @@ #ifndef ALICEO2_ENCODED_BLOCKS_H #define ALICEO2_ENCODED_BLOCKS_H -#undef NDEBUG -#include +// #undef NDEBUG +// #include #include #include #include -#include "rANS/rans.h" -#include "rANS/utils.h" +#include + #include "TTree.h" #include "CommonUtils/StringUtils.h" #include "Framework/Logger.h" #include "DetectorsCommonDataFormats/CTFDictHeader.h" #include "DetectorsCommonDataFormats/CTFIOSize.h" +#include "DetectorsCommonDataFormats/ANSHeader.h" +#include "DetectorsCommonDataFormats/internal/Packer.h" +#include "DetectorsCommonDataFormats/Metadata.h" +#ifndef __CLING__ +#include "DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h" +#include "DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h" +#include "rANS/compat.h" +#include "rANS/histogram.h" +#include "rANS/serialize.h" +#include "rANS/factory.h" +#include "rANS/metrics.h" +#include "rANS/utils.h" +#endif namespace o2 { @@ -50,9 +63,20 @@ struct is_iterator inline constexpr bool is_iterator_v = is_iterator::value; + +inline constexpr bool mayEEncode(Metadata::OptStore opt) noexcept +{ + return (opt == Metadata::OptStore::EENCODE) || (opt == Metadata::OptStore::EENCODE_OR_PACK); +} + +inline constexpr bool mayPack(Metadata::OptStore opt) noexcept +{ + return (opt == Metadata::OptStore::PACK) || (opt == Metadata::OptStore::EENCODE_OR_PACK); +} + } // namespace detail +constexpr size_t PackingThreshold = 512; -using namespace o2::rans; constexpr size_t Alignment = 16; constexpr int WrappersSplitLevel = 99; @@ -76,10 +100,10 @@ inline T* relocatePointer(const char* oldBase, char* newBase, const T* ptr) } template = sizeof(source_T)), bool> = true> -inline size_t calculateNDestTElements(size_t sourceElems) noexcept +inline constexpr size_t calculateNDestTElements(size_t nElems) noexcept { - const size_t sizeOfSourceArray = sourceElems * sizeof(source_T); - return sizeOfSourceArray / sizeof(dest_T) + (sizeOfSourceArray % sizeof(dest_T) != 0); + const size_t srcBufferSize = nElems * sizeof(source_T); + return srcBufferSize / sizeof(dest_T) + (srcBufferSize % sizeof(dest_T) != 0); }; template = sizeof(source_T)), bool> = true> @@ -94,52 +118,6 @@ inline size_t calculatePaddedSize(size_t nElems) noexcept ///>>======================== Auxiliary classes =======================>> -struct ANSHeader { - uint8_t majorVersion; - uint8_t minorVersion; - - void clear() { majorVersion = minorVersion = 0; } - ClassDefNV(ANSHeader, 1); -}; - -struct Metadata { - enum class OptStore : uint8_t { // describe how the store the data described by this metadata - EENCODE, // entropy encoding applied - ROOTCompression, // original data repacked to array with slot-size = streamSize and saved with root compression - NONE, // original data repacked to array with slot-size = streamSize and saved w/o compression - NODATA // no data was provided - }; - size_t messageLength = 0; - size_t nLiterals = 0; - uint8_t messageWordSize = 0; - uint8_t coderType = 0; - uint8_t streamSize = 0; - uint8_t probabilityBits = 0; - OptStore opt = OptStore::EENCODE; - int32_t min = 0; - int32_t max = 0; - int nDictWords = 0; - int nDataWords = 0; - int nLiteralWords = 0; - - size_t getUncompressedSize() const { return messageLength * messageWordSize; } - size_t getCompressedSize() const { return (nDictWords + nDataWords + nLiteralWords) * streamSize; } - void clear() - { - min = max = 0; - messageLength = 0; - messageWordSize = 0; - nLiterals = 0; - coderType = 0; - streamSize = 0; - probabilityBits = 0; - nDictWords = 0; - nDataWords = 0; - nLiteralWords = 0; - } - ClassDefNV(Metadata, 2); -}; - /// registry struct for the buffer start and offsets of writable space struct Registry { char* head = nullptr; //! pointer on the head of the CTF @@ -160,6 +138,12 @@ struct Registry { return size - offsFreeStart; } + char* getFreeBlockEnd() const + { + assert(offsFreeStart <= size); + return getFreeBlockStart() + getFreeSize(); + } + ClassDefNV(Registry, 1); }; @@ -178,11 +162,21 @@ struct Block { inline const W* getData() const { return nData ? (payload + nDict) : nullptr; } inline const W* getDataPointer() const { return payload ? (payload + nDict) : nullptr; } // needed when nData is not set yet inline const W* getLiterals() const { return nLiterals ? (payload + nDict + nData) : nullptr; } + inline const W* getEndOfBlock() const + { + if (!registry) { + return nullptr; + } + // get last legal W*, since unaligned data is undefined behavior! + const size_t delta = reinterpret_cast(registry->getFreeBlockEnd()) % sizeof(W); + return reinterpret_cast(registry->getFreeBlockEnd() - delta); + } inline W* getCreatePayload() { return payload ? payload : (registry ? (payload = reinterpret_cast(registry->getFreeBlockStart())) : nullptr); } inline W* getCreateDict() { return payload ? payload : getCreatePayload(); } inline W* getCreateData() { return payload ? (payload + nDict) : getCreatePayload(); } inline W* getCreateLiterals() { return payload ? payload + (nDict + nData) : getCreatePayload(); } + inline W* getEndOfBlock() { return const_cast(static_cast(*this).getEndOfBlock()); }; inline auto getOffsDict() { return reinterpret_cast(getCreateDict()) - reinterpret_cast(registry->head); } inline auto getOffsData() { return reinterpret_cast(getCreateData()) - reinterpret_cast(registry->head); } @@ -290,8 +284,10 @@ struct Block { // resize block and free up unused buffer space. void realignBlock() { - size_t sz = estimateSize(getNStored()); - registry->offsFreeStart = (reinterpret_cast(payload) - registry->head) + sz; + if (payload) { + size_t sz = estimateSize(getNStored()); + registry->offsFreeStart = (reinterpret_cast(payload) - registry->head) + sz; + } } /// store binary blob data (buffer filled from head to tail) @@ -340,7 +336,15 @@ class EncodedBlocks public: typedef EncodedBlocks base; - void setHeader(const H& h) { mHeader = h; } +#ifndef __CLING__ + template + using dictionaryType = std::variant, rans::RenormedDenseHistogram>; +#endif + + void setHeader(const H& h) + { + mHeader = h; + } const H& getHeader() const { return mHeader; } H& getHeader() { return mHeader; } std::shared_ptr cloneHeader() const { return std::shared_ptr(new H(mHeader)); } // for dictionary creation @@ -361,15 +365,70 @@ class EncodedBlocks return mBlocks[i]; } - o2::rans::RenormedFrequencyTable getFrequencyTable(int i) const +#ifndef __CLING__ + template + dictionaryType getDictionary(int i, ANSHeader ansVersion = ANSVersionUnspecified) const { const auto& block = getBlock(i); const auto& metadata = getMetadata(i); - rans::FrequencyTable frequencyTable{block.getDict(), block.getDict() + block.getNDict(), metadata.min}; - return rans::renorm(std::move(frequencyTable), metadata.probabilityBits); - } + ansVersion = checkANSVersion(ansVersion); + + assert(static_cast(std::numeric_limits::min()) <= static_cast(metadata.max)); + assert(static_cast(std::numeric_limits::max()) >= static_cast(metadata.min)); + + // check consistency of metadata and type + [&]() { + const int64_t sourceMin = std::numeric_limits::min(); + const int64_t sourceMax = std::numeric_limits::max(); + + auto view = rans::trim(rans::HistogramView{block.getDict(), block.getDict() + block.getNDict(), metadata.min}); + const int64_t dictMin = view.getMin(); + const int64_t dictMax = view.getMax(); + assert(dictMin >= metadata.min); + assert(dictMax <= metadata.max); + + if ((dictMin < sourceMin) || (dictMax > sourceMax)) { + if (ansVersion == ANSVersionCompat && mHeader.majorVersion == 1 && mHeader.minorVersion == 0 && mHeader.dictTimeStamp < 1653192000000) { + LOGP(warn, "value range of dictionary and target datatype are incompatible: target type [{},{}] vs dictionary [{},{}], tolerate in compat mode for old dictionaries", sourceMin, sourceMax, dictMin, dictMax); + } else { + throw std::runtime_error(fmt::format("value range of dictionary and target datatype are incompatible: target type [{},{}] vs dictionary [{},{}]", sourceMin, sourceMax, dictMin, dictMax)); + } + } + }(); - void setANSHeader(const ANSHeader& h) { mANSHeader = h; } + if (ansVersion == ANSVersionCompat) { + rans::DenseHistogram histogram{block.getDict(), block.getDict() + block.getNDict(), metadata.min}; + return rans::compat::renorm(std::move(histogram), metadata.probabilityBits); + } else if (ansVersion == ANSVersion1) { + // dictionary is loaded from an explicit dict file and is stored densly + if (getANSHeader() == ANSVersionUnspecified) { + rans::DenseHistogram histogram{block.getDict(), block.getDict() + block.getNDict(), metadata.min}; + size_t renormingBits = rans::utils::sanitizeRenormingBitRange(metadata.probabilityBits); + LOG_IF(debug, renormingBits != metadata.probabilityBits) + << fmt::format("While reading metadata from external dictionary, rANSV1 is rounding renorming precision from {} to {}", metadata.probabilityBits, renormingBits); + return rans::renorm(std::move(histogram), renormingBits, rans::RenormingPolicy::ForceIncompressible); + } else { + // dictionary is elias-delta coded inside the block + if constexpr (sizeof(source_T) > 2) { + return rans::readRenormedSetDictionary(block.getDict(), block.getDict() + block.getNDict(), + static_cast(metadata.min), static_cast(metadata.max), + metadata.probabilityBits); + } else { + return rans::readRenormedDictionary(block.getDict(), block.getDict() + block.getNDict(), + static_cast(metadata.min), static_cast(metadata.max), + metadata.probabilityBits); + } + } + } else { + throw std::runtime_error(fmt::format("Failed to load serialized Dictionary. Unsupported ANS Version: {}", static_cast(ansVersion))); + } + }; +#endif + + void setANSHeader(const ANSHeader& h) + { + mANSHeader = h; + } const ANSHeader& getANSHeader() const { return mANSHeader; } ANSHeader& getANSHeader() { return mANSHeader; } @@ -409,6 +468,9 @@ class EncodedBlocks /// total allocated size in bytes size_t size() const { return mRegistry.size; } + /// used part of total allocated size in bytes (output size) + size_t outputsize() const { return mRegistry.offsFreeStart; } + /// size remaining for additional data size_t getFreeSize() const { return mRegistry.getFreeSize(); } @@ -435,25 +497,27 @@ class EncodedBlocks /// encode vector src to bloc at provided slot template - inline o2::ctf::CTFIOSize encode(const VE& src, int slot, uint8_t symbolTablePrecision, Metadata::OptStore opt, buffer_T* buffer = nullptr, const void* encoderExt = nullptr, float memfc = 1.f) + inline o2::ctf::CTFIOSize encode(const VE& src, int slot, uint8_t symbolTablePrecision, Metadata::OptStore opt, buffer_T* buffer = nullptr, const std::any& encoderExt = {}, float memfc = 1.f) { return encode(std::begin(src), std::end(src), slot, symbolTablePrecision, opt, buffer, encoderExt, memfc); } /// encode vector src to bloc at provided slot template - o2::ctf::CTFIOSize encode(const input_IT srcBegin, const input_IT srcEnd, int slot, uint8_t symbolTablePrecision, Metadata::OptStore opt, buffer_T* buffer = nullptr, const void* encoderExt = nullptr, float memfc = 1.f); + o2::ctf::CTFIOSize encode(const input_IT srcBegin, const input_IT srcEnd, int slot, uint8_t symbolTablePrecision, Metadata::OptStore opt, buffer_T* buffer = nullptr, const std::any& encoderExt = {}, float memfc = 1.f); /// decode block at provided slot to destination vector (will be resized as needed) template - o2::ctf::CTFIOSize decode(container_T& dest, int slot, const void* decoderExt = nullptr) const; + o2::ctf::CTFIOSize decode(container_T& dest, int slot, const std::any& decoderExt = {}) const; /// decode block at provided slot to destination pointer, the needed space assumed to be available template , bool> = true> - o2::ctf::CTFIOSize decode(D_IT dest, int slot, const void* decoderExt = nullptr) const; + o2::ctf::CTFIOSize decode(D_IT dest, int slot, const std::any& decoderExt = {}) const; +#ifndef __CLING__ /// create a special EncodedBlocks containing only dictionaries made from provided vector of frequency tables - static std::vector createDictionaryBlocks(const std::vector& vfreq, const std::vector& prbits); + static std::vector createDictionaryBlocks(const std::vector>& vfreq, const std::vector& prbits); +#endif /// print itself void print(const std::string& prefix = "", int verbosity = 1) const; @@ -468,6 +532,8 @@ class EncodedBlocks std::array mMetadata; // compressed block's details std::array, N> mBlocks; //! this is in fact stored, but to overcome TBuffer limits we have to define the branches per block!!! + inline static constexpr Metadata::OptStore FallbackStorageType{Metadata::OptStore::NONE}; + /// setup internal structure and registry for given buffer size (in bytes!!!) void init(size_t sz); @@ -495,8 +561,93 @@ class EncodedBlocks template static bool readTreeBranch(TTree& tree, const std::string& brname, D& dt, int ev = 0); - ClassDefNV(EncodedBlocks, 2); -}; + template + auto expandStorage(size_t slot, size_t nElemets, T* buffer = nullptr) -> decltype(auto); + + inline ANSHeader checkANSVersion(ANSHeader ansVersion) const + { + auto ctfANSHeader = getANSHeader(); + ANSHeader ret{ANSVersionUnspecified}; + + const bool isEqual{ansVersion == ctfANSHeader}; + const bool isHeaderUnspecified{ctfANSHeader == ANSVersionUnspecified}; + + if (isEqual) { + if (isHeaderUnspecified) { + throw std::runtime_error{fmt::format("Missmatch of ANSVersions, trying to encode/decode CTF with ANS Version Header {} with ANS Version {}", + static_cast(ctfANSHeader), + static_cast(ansVersion))}; + } else { + ret = ctfANSHeader; + } + } else { + if (isHeaderUnspecified) { + ret = ansVersion; + } else { + ret = ctfANSHeader; + } + } + + return ret; + }; + + template + o2::ctf::CTFIOSize entropyCodeRANSCompat(const input_IT srcBegin, const input_IT srcEnd, int slot, uint8_t symbolTablePrecision, buffer_T* buffer = nullptr, const std::any& encoderExt = {}, float memfc = 1.f); + + template + o2::ctf::CTFIOSize entropyCodeRANSV1(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer = nullptr, const std::any& encoderExt = {}, float memfc = 1.f); + + template + o2::ctf::CTFIOSize encodeRANSV1External(const input_IT srcBegin, const input_IT srcEnd, int slot, const std::any& encoderExt, buffer_T* buffer = nullptr, double_t sizeEstimateSafetyFactor = 1); + + template + o2::ctf::CTFIOSize encodeRANSV1Inplace(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer = nullptr, double_t sizeEstimateSafetyFactor = 1); + +#ifndef __CLING__ + template + o2::ctf::CTFIOSize pack(const input_IT srcBegin, const input_IT srcEnd, int slot, rans::Metrics::value_type> metrics, buffer_T* buffer = nullptr); + + template + inline o2::ctf::CTFIOSize pack(const input_IT srcBegin, const input_IT srcEnd, int slot, buffer_T* buffer = nullptr) + { + using source_type = typename std::iterator_traits::value_type; + + rans::Metrics metrics{}; + metrics.getDatasetProperties().numSamples = std::distance(srcBegin, srcEnd); + + if (metrics.getDatasetProperties().numSamples != 0) { + const auto [minIter, maxIter] = std::minmax_element(srcBegin, srcEnd); + metrics.getDatasetProperties().min = *minIter; + metrics.getDatasetProperties().max = *maxIter; + + // special case: if min === max, the range is 0 and the data can be reconstructed just via the metadata. + metrics.getDatasetProperties().alphabetRangeBits = + rans::utils::getRangeBits(metrics.getDatasetProperties().min, + metrics.getDatasetProperties().max); + } + + return pack(srcBegin, srcEnd, slot, metrics, buffer); + } +#endif + + template + o2::ctf::CTFIOSize store(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer = nullptr); + + // decode + template + CTFIOSize decodeCompatImpl(dst_IT dest, int slot, const std::any& decoderExt) const; + + template + CTFIOSize decodeRansV1Impl(dst_IT dest, int slot, const std::any& decoderExt) const; + + template + CTFIOSize decodeUnpackImpl(dst_IT dest, int slot) const; + + template + CTFIOSize decodeCopyImpl(dst_IT dest, int slot) const; + + ClassDefNV(EncodedBlocks, 3); +}; // namespace ctf ///_____________________________________________________________________________ /// read from tree to non-flat object @@ -701,7 +852,7 @@ auto EncodedBlocks::getImage(const void* newHead) // we don't modify newHead, but still need to remove constness for relocation interface relocate(image.mRegistry.head, const_cast(reinterpret_cast(newHead)), reinterpret_cast(&image)); - return std::move(image); + return image; } ///_____________________________________________________________________________ @@ -751,7 +902,7 @@ void EncodedBlocks::print(const std::string& prefix, int verbosity) con ndata += mBlocks[i].getNData(); nlit += mBlocks[i].getNLiterals(); } - LOG(info) << prefix << N << " blocks, input size: " << inpSize << ", output size: " << size() + LOG(info) << prefix << N << " blocks, input size: " << inpSize << ", output size: " << outputsize() << " NDictWords: " << ndict << " NDataWords: " << ndata << " NLiteralWords: " << nlit; } } @@ -759,9 +910,9 @@ void EncodedBlocks::print(const std::string& prefix, int verbosity) con ///_____________________________________________________________________________ template template -inline o2::ctf::CTFIOSize EncodedBlocks::decode(container_T& dest, // destination container - int slot, // slot of the block to decode - const void* decoderExt) const // optional externally provided decoder +inline o2::ctf::CTFIOSize EncodedBlocks::decode(container_T& dest, // destination container + int slot, // slot of the block to decode + const std::any& decoderExt) const // optional externally provided decoder { dest.resize(mMetadata[slot].messageLength); // allocate output buffer return decode(std::begin(dest), slot, decoderExt); @@ -770,54 +921,190 @@ inline o2::ctf::CTFIOSize EncodedBlocks::decode(container_T& dest, ///_____________________________________________________________________________ template template , bool>> -o2::ctf::CTFIOSize EncodedBlocks::decode(D_IT dest, // iterator to destination - int slot, // slot of the block to decode - const void* decoderExt) const // optional externally provided decoder +CTFIOSize EncodedBlocks::decode(D_IT dest, // iterator to destination + int slot, // slot of the block to decode + const std::any& decoderExt) const // optional externally provided decoder { + // get references to the right data + const auto& ansVersion = getANSHeader(); const auto& block = mBlocks[slot]; const auto& md = mMetadata[slot]; + LOGP(debug, "Slot{} | NStored={} Ndict={} nData={}, MD: messageLength:{} opt:{} min:{} max:{} offs:{} width:{} ", slot, block.getNStored(), block.getNDict(), block.getNData(), md.messageLength, (int)md.opt, md.min, md.max, md.literalsPackingOffset, md.literalsPackingWidth); - using dest_t = typename std::iterator_traits::value_type; + constexpr size_t word_size = sizeof(W); - // decode - if (block.getNStored()) { + if (ansVersion == ANSVersionCompat) { + if (!block.getNStored()) { + return {0, md.getUncompressedSize(), md.getCompressedSize() * word_size}; + } if (md.opt == Metadata::OptStore::EENCODE) { - if (!decoderExt && !block.getNDict()) { - LOG(error) << "Dictionaty is not saved for slot " << slot << " and no external decoder is provided"; - throw std::runtime_error("Dictionary is not saved and no external decoder provided"); - } - const o2::rans::LiteralDecoder64* decoder = reinterpret_cast*>(decoderExt); - std::unique_ptr> decoderLoc; - if (block.getNDict()) { // if dictionaty is saved, prefer it - decoderLoc = std::make_unique>(this->getFrequencyTable(slot)); - decoder = decoderLoc.get(); - } else { // verify that decoded corresponds to stored metadata - if (md.min != decoder->getMinSymbol()) { - LOG(error) << "Mismatch between min=" << md.min << " symbol in metadata and those in external decoder " - << decoder->getMinSymbol() << " for slot " << slot; - throw std::runtime_error("Mismatch between min symbol in metadata and the one in external decoder"); - } - } - // load incompressible symbols if they existed - std::vector literals; - if (block.getNLiterals()) { - // note: here we have to use md.nLiterals (original number of literal words) rather than md.nLiteralWords == block.getNLiterals() - // (number of W-words in the EncodedBlock occupied by literals) as we cast literals stored in W-word array - // to D-word array - literals = std::vector{reinterpret_cast(block.getLiterals()), reinterpret_cast(block.getLiterals()) + md.nLiterals}; + return decodeCompatImpl(dest, slot, decoderExt); + } else { + return decodeCopyImpl(dest, slot); + } + } else if (ansVersion == ANSVersion1) { + if (md.opt == Metadata::OptStore::PACK) { + return decodeUnpackImpl(dest, slot); + } + if (!block.getNStored()) { + return {0, md.getUncompressedSize(), md.getCompressedSize() * word_size}; + } + if (md.opt == Metadata::OptStore::EENCODE) { + return decodeRansV1Impl(dest, slot, decoderExt); + } else { + return decodeCopyImpl(dest, slot); + } + } else { + throw std::runtime_error("unsupported ANS Version"); + } +}; + +#ifndef __CLING__ +template +template +CTFIOSize EncodedBlocks::decodeCompatImpl(dst_IT dstBegin, int slot, const std::any& decoderExt) const +{ + + // get references to the right data + const auto& block = mBlocks[slot]; + const auto& md = mMetadata[slot]; + + using dst_type = typename std::iterator_traits::value_type; + using decoder_type = typename rans::compat::decoder_type; + + std::optional inplaceDecoder{}; + if (md.nDictWords > 0) { + inplaceDecoder = decoder_type{std::get>(this->getDictionary(slot))}; + } else if (!decoderExt.has_value()) { + throw std::runtime_error("neither dictionary nor external decoder provided"); + } + + auto getDecoder = [&]() -> const decoder_type& { + if (inplaceDecoder.has_value()) { + return inplaceDecoder.value(); + } else { + return std::any_cast(decoderExt); + } + }; + + const size_t NDecoderStreams = rans::compat::defaults::CoderPreset::nStreams; + + if (block.getNLiterals()) { + auto* literalsEnd = reinterpret_cast(block.getLiterals()) + md.nLiterals; + getDecoder().process(block.getData() + block.getNData(), dstBegin, md.messageLength, NDecoderStreams, literalsEnd); + } else { + getDecoder().process(block.getData() + block.getNData(), dstBegin, md.messageLength, NDecoderStreams); + } + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; +}; + +template +template +CTFIOSize EncodedBlocks::decodeRansV1Impl(dst_IT dstBegin, int slot, const std::any& decoderExt) const +{ + + // get references to the right data + const auto& block = mBlocks[slot]; + const auto& md = mMetadata[slot]; + + using dst_type = typename std::iterator_traits::value_type; + using decoder_type = typename rans::defaultDecoder_type; + + std::optional inplaceDecoder{}; + if (md.nDictWords > 0) { + std::visit([&](auto&& arg) { inplaceDecoder = decoder_type{arg}; }, this->getDictionary(slot)); + } else if (!decoderExt.has_value()) { + throw std::runtime_error("no dictionary nor external decoder provided"); + } + + auto getDecoder = [&]() -> const decoder_type& { + if (inplaceDecoder.has_value()) { + return inplaceDecoder.value(); + } else { + return std::any_cast(decoderExt); + } + }; + + // verify decoders + [&]() { + const decoder_type& decoder = getDecoder(); + const size_t decoderSymbolTablePrecision = decoder.getSymbolTablePrecision(); + + if (md.probabilityBits != decoderSymbolTablePrecision) { + throw std::runtime_error(fmt::format( + "Missmatch in decoder renorming precision vs metadata:{} Bits vs {} Bits.", + md.probabilityBits, decoderSymbolTablePrecision)); + } + + if (md.streamSize != rans::utils::getStreamingLowerBound_v) { + throw std::runtime_error("Streaming lower bound of dataset and decoder do not match"); + } + }(); + + // do the actual decoding + if (block.getNLiterals()) { + std::vector literals(md.nLiterals); + rans::unpack(block.getLiterals(), md.nLiterals, literals.data(), md.literalsPackingWidth, md.literalsPackingOffset); + getDecoder().process(block.getData() + block.getNData(), dstBegin, md.messageLength, md.nStreams, literals.end()); + } else { + getDecoder().process(block.getData() + block.getNData(), dstBegin, md.messageLength, md.nStreams); + } + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; +}; + +template +template +CTFIOSize EncodedBlocks::decodeUnpackImpl(dst_IT dest, int slot) const +{ + using dest_t = typename std::iterator_traits::value_type; + + const auto& block = mBlocks[slot]; + const auto& md = mMetadata[slot]; + + const size_t messageLength = md.messageLength; + const size_t packingWidth = md.probabilityBits; + const dest_t offset = md.min; + const auto* srcIt = block.getData(); + // we have a vector of one and the same value. All information is in the metadata + if (packingWidth == 0) { + const dest_t value = [&]() -> dest_t { + // Bugfix: We tried packing values with a width of 0 Bits; + if (md.nDataWords > 0) { + LOGP(debug, "packing bug recovery: MD nStreams:{} messageLength:{} nLiterals:{} messageWordSize:{} coderType:{} streamSize:{} probabilityBits:{} (int)opt:{} min:{} max:{} literalsPackingOffset:{} literalsPackingWidth:{} nDictWords:{} nDataWords:{} nLiteralWords:{}", + value, md.nStreams, md.messageLength, md.nLiterals, md.messageWordSize, md.coderType, md.streamSize, md.probabilityBits, (int)md.opt, md.min, md.max, md.literalsPackingOffset, md.literalsPackingWidth, md.nDictWords, md.nDataWords, md.nLiteralWords); + return offset + static_cast(*srcIt); } - decoder->process(block.getData() + block.getNData(), dest, md.messageLength, literals); - } else { // data was stored as is - using destPtr_t = typename std::iterator_traits::pointer; - destPtr_t srcBegin = reinterpret_cast(block.payload); - destPtr_t srcEnd = srcBegin + md.messageLength * sizeof(dest_t); - std::copy(srcBegin, srcEnd, dest); - // std::memcpy(dest, block.payload, md.messageLength * sizeof(dest_t)); + // normal case: + return offset; + }(); + for (size_t i = 0; i < messageLength; ++i) { + *dest++ = value; } + } else { + rans::unpack(srcIt, messageLength, dest, packingWidth, offset); } - return {0, md.getUncompressedSize(), md.getCompressedSize()}; -} + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; +}; + +template +template +CTFIOSize EncodedBlocks::decodeCopyImpl(dst_IT dest, int slot) const +{ + // get references to the right data + const auto& block = mBlocks[slot]; + const auto& md = mMetadata[slot]; + + using dest_t = typename std::iterator_traits::value_type; + using decoder_t = typename rans::compat::decoder_type; + using destPtr_t = typename std::iterator_traits::pointer; + + destPtr_t srcBegin = reinterpret_cast(block.payload); + destPtr_t srcEnd = srcBegin + md.messageLength * sizeof(dest_t); + std::copy(srcBegin, srcEnd, dest); + + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; +}; ///_____________________________________________________________________________ template @@ -828,147 +1115,417 @@ o2::ctf::CTFIOSize EncodedBlocks::encode(const input_IT srcBegin, uint8_t symbolTablePrecision, // encoding into Metadata::OptStore opt, // option for data compression buffer_T* buffer, // optional buffer (vector) providing memory for encoded blocks - const void* encoderExt, // optional external encoder + const std::any& encoderExt, // optional external encoder float memfc) // memory allocation margin factor { - - using storageBuffer_t = W; - using input_t = typename std::iterator_traits::value_type; - using ransEncoder_t = typename rans::LiteralEncoder64; - using ransState_t = typename ransEncoder_t::coder_t; - using ransStream_t = typename ransEncoder_t::stream_t; - - // assert at compile time that output types align so that padding is not necessary. - static_assert(std::is_same_v); - static_assert(std::is_same_v); - // fill a new block assert(slot == mRegistry.nFilledBlocks); mRegistry.nFilledBlocks++; const size_t messageLength = std::distance(srcBegin, srcEnd); // cover three cases: - // * empty source message: no entropy coding + // * empty source message: no co // * source message to pass through without any entropy coding // * source message where entropy coding should be applied // case 1: empty source message if (messageLength == 0) { - mMetadata[slot] = Metadata{0, 0, sizeof(input_t), sizeof(ransState_t), sizeof(ransStream_t), symbolTablePrecision, Metadata::OptStore::NODATA, 0, 0, 0, 0, 0}; + mMetadata[slot] = Metadata{}; + mMetadata[slot].opt = Metadata::OptStore::NODATA; return {}; } + if (detail::mayEEncode(opt)) { + const ANSHeader& ansVersion = getANSHeader(); + if (ansVersion == ANSVersionCompat) { + return entropyCodeRANSCompat(srcBegin, srcEnd, slot, symbolTablePrecision, buffer, encoderExt, memfc); + } else if (ansVersion == ANSVersion1) { + return entropyCodeRANSV1(srcBegin, srcEnd, slot, opt, buffer, encoderExt, memfc); + } else { + throw std::runtime_error(fmt::format("Unsupported ANS Coder Version: {}.{}", ansVersion.majorVersion, ansVersion.minorVersion)); + } + } else if (detail::mayPack(opt)) { + return pack(srcBegin, srcEnd, slot, buffer); + } else { + return store(srcBegin, srcEnd, slot, opt, buffer); + } +}; - auto* thisBlock = &mBlocks[slot]; - auto* thisMetadata = &mMetadata[slot]; +template +template +[[nodiscard]] auto EncodedBlocks::expandStorage(size_t slot, size_t nElements, T* buffer) -> decltype(auto) +{ + // after previous relocation this (hence its data members) are not guaranteed to be valid + auto* old = get(buffer->data()); + auto* thisBlock = &(old->mBlocks[slot]); + auto* thisMetadata = &(old->mMetadata[slot]); // resize underlying buffer of block if necessary and update all pointers. - auto expandStorage = [&](int additionalElements) { - auto* const blockHead = get(thisBlock->registry->head); // extract pointer from the block, as "this" might be invalid - const size_t additionalSize = blockHead->estimateBlockSize(additionalElements); // size in bytes!!! - if (additionalSize >= thisBlock->registry->getFreeSize()) { - LOG(debug) << "Slot " << slot << ": free size: " << thisBlock->registry->getFreeSize() << ", need " << additionalSize << " for " << additionalElements << " words"; - if (buffer) { - blockHead->expand(*buffer, blockHead->size() + (additionalSize - blockHead->getFreeSize())); - thisMetadata = &(get(buffer->data())->mMetadata[slot]); - thisBlock = &(get(buffer->data())->mBlocks[slot]); // in case of resizing this and any this.xxx becomes invalid - } else { - throw std::runtime_error("no room for encoded block in provided container"); - } + auto* const blockHead = get(thisBlock->registry->head); // extract pointer from the block, as "this" might be invalid + const size_t additionalSize = blockHead->estimateBlockSize(nElements); // additionalSize is in bytes!!! + if (additionalSize >= thisBlock->registry->getFreeSize()) { + LOGP(debug, "Slot {} with {} available words needs to allocate {} bytes for a total of {} words.", slot, thisBlock->registry->getFreeSize(), additionalSize, nElements); + if (buffer) { + blockHead->expand(*buffer, blockHead->size() + (additionalSize - blockHead->getFreeSize())); + thisMetadata = &(get(buffer->data())->mMetadata[slot]); + thisBlock = &(get(buffer->data())->mBlocks[slot]); // in case of resizing this and any this.xxx becomes invalid + } else { + throw std::runtime_error("failed to allocate additional space in provided external buffer"); } - }; + } + return std::make_pair(thisBlock, thisMetadata); +}; - // case 3: message where entropy coding should be applied - if (opt == Metadata::OptStore::EENCODE) { - // build symbol statistics - constexpr size_t SizeEstMarginAbs = 10 * 1024; - const float SizeEstMarginRel = 1.5 * memfc; +template +template +o2::ctf::CTFIOSize EncodedBlocks::entropyCodeRANSCompat(const input_IT srcBegin, const input_IT srcEnd, int slot, uint8_t symbolTablePrecision, buffer_T* buffer, const std::any& encoderExt, float memfc) +{ + using storageBuffer_t = W; + using input_t = typename std::iterator_traits::value_type; + using ransEncoder_t = typename rans::compat::encoder_type; + using ransState_t = typename ransEncoder_t::coder_type::state_type; + using ransStream_t = typename ransEncoder_t::stream_type; - const auto [inplaceEncoder, frequencyTable] = [&]() { - if (encoderExt) { - return std::make_tuple(ransEncoder_t{}, rans::FrequencyTable{}); + // assert at compile time that output types align so that padding is not necessary. + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + auto* thisBlock = &mBlocks[slot]; + auto* thisMetadata = &mMetadata[slot]; + + // build symbol statistics + constexpr size_t SizeEstMarginAbs = 10 * 1024; + const float SizeEstMarginRel = 1.5 * memfc; + + const size_t messageLength = std::distance(srcBegin, srcEnd); + rans::DenseHistogram frequencyTable{}; + rans::compat::encoder_type inplaceEncoder{}; + + try { + std::tie(inplaceEncoder, frequencyTable) = [&]() { + if (encoderExt.has_value()) { + return std::make_tuple(ransEncoder_t{}, rans::DenseHistogram{}); } else { - rans::FrequencyTable frequencyTable = rans::makeFrequencyTableFromSamples(srcBegin, srcEnd); - RenormedFrequencyTable renormedFrequencyTable = rans::renorm(frequencyTable, symbolTablePrecision); - return std::make_tuple(ransEncoder_t{renormedFrequencyTable}, frequencyTable); + auto histogram = rans::makeDenseHistogram::fromSamples(srcBegin, srcEnd); + auto encoder = rans::compat::makeEncoder::fromHistogram(histogram, symbolTablePrecision); + return std::make_tuple(std::move(encoder), std::move(histogram)); } }(); - ransEncoder_t const* const encoder = encoderExt ? reinterpret_cast(encoderExt) : &inplaceEncoder; - - // estimate size of encode buffer - int dataSize = rans::calculateMaxBufferSize(messageLength, encoder->getAlphabetRangeBits(), sizeof(input_t)); // size in bytes - // preliminary expansion of storage based on dict size + estimated size of encode buffer - dataSize = SizeEstMarginAbs + int(SizeEstMarginRel * (dataSize / sizeof(storageBuffer_t))) + (sizeof(input_t) < sizeof(storageBuffer_t)); // size in words of output stream - expandStorage(frequencyTable.size() + dataSize); - // store dictionary first - if (!frequencyTable.empty()) { - thisBlock->storeDict(frequencyTable.size(), frequencyTable.data()); - LOGP(debug, "StoreDict {} bytes, offs: {}:{}", frequencyTable.size() * sizeof(W), thisBlock->getOffsDict(), thisBlock->getOffsDict() + frequencyTable.size() * sizeof(W)); + } catch (const rans::HistogramError& error) { + LOGP(warning, "Failed to build Dictionary for rANS encoding, using fallback option"); + return store(srcBegin, srcEnd, slot, this->FallbackStorageType, buffer); + } + const ransEncoder_t& encoder = encoderExt.has_value() ? std::any_cast(encoderExt) : inplaceEncoder; + + // estimate size of encode buffer + int dataSize = rans::compat::calculateMaxBufferSizeB(messageLength, rans::compat::getAlphabetRangeBits(encoder.getSymbolTable())); // size in bytes + // preliminary expansion of storage based on dict size + estimated size of encode buffer + dataSize = SizeEstMarginAbs + int(SizeEstMarginRel * (dataSize / sizeof(storageBuffer_t))) + (sizeof(input_t) < sizeof(storageBuffer_t)); // size in words of output stream + + const auto view = rans::trim(rans::makeHistogramView(frequencyTable)); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, view.size() + dataSize, buffer); + + // store dictionary first + + if (!view.empty()) { + thisBlock->storeDict(view.size(), view.data()); + LOGP(debug, "StoreDict {} bytes, offs: {}:{}", view.size() * sizeof(W), thisBlock->getOffsDict(), thisBlock->getOffsDict() + view.size() * sizeof(W)); + } + // vector of incompressible literal symbols + std::vector literals; + // directly encode source message into block buffer. + storageBuffer_t* const blockBufferBegin = thisBlock->getCreateData(); + const size_t maxBufferSize = thisBlock->registry->getFreeSize(); // note: "this" might be not valid after expandStorage call!!! + const auto [encodedMessageEnd, literalsEnd] = encoder.process(srcBegin, srcEnd, blockBufferBegin, std::back_inserter(literals)); + rans::utils::checkBounds(encodedMessageEnd, blockBufferBegin + maxBufferSize / sizeof(W)); + dataSize = encodedMessageEnd - thisBlock->getDataPointer(); + thisBlock->setNData(dataSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreData {} bytes, offs: {}:{}", dataSize * sizeof(W), thisBlock->getOffsData(), thisBlock->getOffsData() + dataSize * sizeof(W)); + // update the size claimed by encode message directly inside the block + + // store incompressible symbols if any + const size_t nLiteralSymbols = literals.size(); + const size_t nLiteralWords = [&]() { + if (!literals.empty()) { + const size_t nSymbols = literals.size(); + // introduce padding in case literals don't align; + const size_t nLiteralSymbolsPadded = calculatePaddedSize(nSymbols); + literals.resize(nLiteralSymbolsPadded, {}); + + const size_t nLiteralStorageElems = calculateNDestTElements(nSymbols); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, nLiteralStorageElems, buffer); + thisBlock->storeLiterals(nLiteralStorageElems, reinterpret_cast(literals.data())); + LOGP(debug, "StoreLiterals {} bytes, offs: {}:{}", nLiteralStorageElems * sizeof(W), thisBlock->getOffsLiterals(), thisBlock->getOffsLiterals() + nLiteralStorageElems * sizeof(W)); + return nLiteralStorageElems; } - // vector of incompressible literal symbols - std::vector literals; - // directly encode source message into block buffer. - storageBuffer_t* const blockBufferBegin = thisBlock->getCreateData(); - const size_t maxBufferSize = thisBlock->registry->getFreeSize(); // note: "this" might be not valid after expandStorage call!!! - const auto encodedMessageEnd = encoder->process(srcBegin, srcEnd, blockBufferBegin, literals); - rans::utils::checkBounds(encodedMessageEnd, blockBufferBegin + maxBufferSize / sizeof(W)); - dataSize = encodedMessageEnd - thisBlock->getDataPointer(); - thisBlock->setNData(dataSize); - thisBlock->realignBlock(); - LOGP(debug, "StoreData {} bytes, offs: {}:{}", dataSize * sizeof(W), thisBlock->getOffsData(), thisBlock->getOffsData() + dataSize * sizeof(W)); - // update the size claimed by encode message directly inside the block - - // store incompressible symbols if any - const size_t nLiteralSymbols = literals.size(); - const size_t nLiteralWords = [&]() { - if (!literals.empty()) { - const size_t nSymbols = literals.size(); - // introduce padding in case literals don't align; - const size_t nLiteralSymbolsPadded = calculatePaddedSize(nSymbols); - literals.resize(nLiteralSymbolsPadded, {}); - - const size_t nLiteralStorageElems = calculateNDestTElements(nSymbols); - expandStorage(nLiteralStorageElems); - thisBlock->storeLiterals(nLiteralStorageElems, reinterpret_cast(literals.data())); - LOGP(debug, "StoreLiterals {} bytes, offs: {}:{}", nLiteralStorageElems * sizeof(W), thisBlock->getOffsLiterals(), thisBlock->getOffsLiterals() + nLiteralStorageElems * sizeof(W)); - return nLiteralStorageElems; - } - return size_t(0); - }(); + return size_t(0); + }(); + + LOGP(debug, "Min, {} Max, {}, size, {}, nSamples {}", view.getMin(), view.getMax(), view.size(), frequencyTable.getNumSamples()); + + *thisMetadata = detail::makeMetadataRansCompat(encoder.getNStreams(), + messageLength, + nLiteralSymbols, + encoder.getSymbolTable().getPrecision(), + view.getMin(), + view.getMax(), + view.size(), + dataSize, + nLiteralWords); + + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; +} + +template +template +o2::ctf::CTFIOSize EncodedBlocks::entropyCodeRANSV1(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer, const std::any& encoderExt, float memfc) +{ + CTFIOSize encoderStatistics{}; + + const size_t nSamples = std::distance(srcBegin, srcEnd); + if (detail::mayPack(opt) && nSamples < PackingThreshold) { + encoderStatistics = pack(srcBegin, srcEnd, slot, buffer); + } else { - *thisMetadata = Metadata{messageLength, - nLiteralSymbols, - sizeof(input_t), - sizeof(ransState_t), - sizeof(ransStream_t), - static_cast(encoder->getSymbolTablePrecision()), - opt, - encoder->getMinSymbol(), - encoder->getMaxSymbol(), - static_cast(frequencyTable.size()), - dataSize, - static_cast(nLiteralWords)}; - } else { // store original data w/o EEncoding - // FIXME(milettri): we should be able to do without an intermediate vector; - // provided iterator is not necessarily pointer, need to use intermediate vector!!! - - // introduce padding in case literals don't align; - const size_t nSourceElemsPadded = calculatePaddedSize(messageLength); - std::vector tmp(nSourceElemsPadded, {}); - std::copy(srcBegin, srcEnd, std::begin(tmp)); - - const size_t nBufferElems = calculateNDestTElements(messageLength); - expandStorage(nBufferElems); - thisBlock->storeData(nBufferElems, reinterpret_cast(tmp.data())); - - *thisMetadata = Metadata{messageLength, 0, sizeof(input_t), sizeof(ransState_t), sizeof(storageBuffer_t), symbolTablePrecision, opt, 0, 0, 0, static_cast(nBufferElems), 0}; + if (encoderExt.has_value()) { + encoderStatistics = encodeRANSV1External(srcBegin, srcEnd, slot, encoderExt, buffer, memfc); + } else { + encoderStatistics = encodeRANSV1Inplace(srcBegin, srcEnd, slot, opt, buffer, memfc); + } } - return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize()}; + return encoderStatistics; } +template +template +CTFIOSize EncodedBlocks::encodeRANSV1External(const input_IT srcBegin, const input_IT srcEnd, int slot, const std::any& encoderExt, buffer_T* buffer, double_t sizeEstimateSafetyFactor) +{ + using storageBuffer_t = W; + using input_t = typename std::iterator_traits::value_type; + using ransEncoder_t = typename internal::ExternalEntropyCoder::encoder_type; + using ransState_t = typename ransEncoder_t::coder_type::state_type; + using ransStream_t = typename ransEncoder_t::stream_type; + + // assert at compile time that output types align so that padding is not necessary. + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + auto* thisBlock = &mBlocks[slot]; + auto* thisMetadata = &mMetadata[slot]; + + const size_t messageLength = std::distance(srcBegin, srcEnd); + internal::ExternalEntropyCoder encoder{std::any_cast(encoderExt)}; + + const size_t payloadSizeWords = encoder.template computePayloadSizeEstimate(messageLength); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, payloadSizeWords, buffer); + + // encode payload + auto encodedMessageEnd = encoder.encode(srcBegin, srcEnd, thisBlock->getCreateData(), thisBlock->getEndOfBlock()); + const size_t dataSize = std::distance(thisBlock->getCreateData(), encodedMessageEnd); + thisBlock->setNData(dataSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreData {} bytes, offs: {}:{}", dataSize * sizeof(storageBuffer_t), thisBlock->getOffsData(), thisBlock->getOffsData() + dataSize * sizeof(storageBuffer_t)); + // update the size claimed by encoded message directly inside the block + + // encode literals + size_t literalsSize = 0; + if (encoder.getNIncompressibleSamples() > 0) { + const size_t literalsBufferSizeWords = encoder.template computePackedIncompressibleSize(); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, literalsBufferSizeWords, buffer); + auto literalsEnd = encoder.writeIncompressible(thisBlock->getCreateLiterals(), thisBlock->getEndOfBlock()); + literalsSize = std::distance(thisBlock->getCreateLiterals(), literalsEnd); + thisBlock->setNLiterals(literalsSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreLiterals {} bytes, offs: {}:{}", literalsSize * sizeof(storageBuffer_t), thisBlock->getOffsLiterals(), thisBlock->getOffsLiterals() + literalsSize * sizeof(storageBuffer_t)); + } + + // write metadata + const auto& symbolTable = encoder.getEncoder().getSymbolTable(); + *thisMetadata = detail::makeMetadataRansV1(encoder.getEncoder().getNStreams(), + rans::utils::getStreamingLowerBound_v, + messageLength, + encoder.getNIncompressibleSamples(), + symbolTable.getPrecision(), + symbolTable.getOffset(), + symbolTable.getOffset() + symbolTable.size(), + encoder.getIncompressibleSymbolOffset(), + encoder.getIncompressibleSymbolPackingBits(), + 0, + dataSize, + literalsSize); + + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; +}; + +template +template +CTFIOSize EncodedBlocks::encodeRANSV1Inplace(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer, double_t sizeEstimateSafetyFactor) +{ + using storageBuffer_t = W; + using input_t = typename std::iterator_traits::value_type; + using ransEncoder_t = typename rans::denseEncoder_type; + using ransState_t = typename ransEncoder_t::coder_type::state_type; + using ransStream_t = typename ransEncoder_t::stream_type; + + // assert at compile time that output types align so that padding is not necessary. + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + auto* thisBlock = &mBlocks[slot]; + auto* thisMetadata = &mMetadata[slot]; + + internal::InplaceEntropyCoder encoder{}; + rans::SourceProxy proxy{srcBegin, srcEnd, [](input_IT begin, input_IT end) { + const size_t nSamples = std::distance(begin, end); + return (!std::is_pointer_v && (nSamples < rans::utils::pow2(23))); + }}; + + try { + if (proxy.isCached()) { + encoder = internal::InplaceEntropyCoder{proxy.beginCache(), proxy.endCache()}; + } else { + encoder = internal::InplaceEntropyCoder{proxy.beginIter(), proxy.endIter()}; + } + } catch (const rans::HistogramError& error) { + LOGP(warning, "Failed to build Dictionary for rANS encoding, using fallback option"); + if (proxy.isCached()) { + return store(proxy.beginCache(), proxy.endCache(), slot, this->FallbackStorageType, buffer); + } else { + return store(proxy.beginIter(), proxy.endIter(), slot, this->FallbackStorageType, buffer); + } + } + + const rans::Metrics& metrics = encoder.getMetrics(); + /* + if constexpr (sizeof(input_t) > 2) { + const auto& dp = metrics.getDatasetProperties(); + LOGP(info, "Metrics:{{slot: {}, numSamples: {}, min: {}, max: {}, alphabetRangeBits: {}, nUsedAlphabetSymbols: {}, preferPacking: {}}}", slot, dp.numSamples, dp.min, dp.max, dp.alphabetRangeBits, dp.nUsedAlphabetSymbols, metrics.getSizeEstimate().preferPacking()); + } + */ + if (detail::mayPack(opt) && metrics.getSizeEstimate().preferPacking()) { + if (proxy.isCached()) { + return pack(proxy.beginCache(), proxy.endCache(), slot, metrics, buffer); + } else { + return pack(proxy.beginIter(), proxy.endIter(), slot, metrics, buffer); + }; + } + + encoder.makeEncoder(); + + const rans::SizeEstimate sizeEstimate = metrics.getSizeEstimate(); + const size_t bufferSizeWords = rans::utils::nBytesTo((sizeEstimate.getCompressedDictionarySize() + + sizeEstimate.getCompressedDatasetSize() + + sizeEstimate.getIncompressibleSize()) * + sizeEstimateSafetyFactor); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, bufferSizeWords, buffer); + + // encode dict + auto encodedDictEnd = encoder.writeDictionary(thisBlock->getCreateDict(), thisBlock->getEndOfBlock()); + const size_t dictSize = std::distance(thisBlock->getCreateDict(), encodedDictEnd); + thisBlock->setNDict(dictSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreDict {} bytes, offs: {}:{}", dictSize * sizeof(storageBuffer_t), thisBlock->getOffsDict(), thisBlock->getOffsDict() + dictSize * sizeof(storageBuffer_t)); + + // encode payload + auto encodedMessageEnd = thisBlock->getCreateData(); + if (proxy.isCached()) { + encodedMessageEnd = encoder.encode(proxy.beginCache(), proxy.endCache(), thisBlock->getCreateData(), thisBlock->getEndOfBlock()); + } else { + encodedMessageEnd = encoder.encode(proxy.beginIter(), proxy.endIter(), thisBlock->getCreateData(), thisBlock->getEndOfBlock()); + } + const size_t dataSize = std::distance(thisBlock->getCreateData(), encodedMessageEnd); + thisBlock->setNData(dataSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreData {} bytes, offs: {}:{}", dataSize * sizeof(storageBuffer_t), thisBlock->getOffsData(), thisBlock->getOffsData() + dataSize * sizeof(storageBuffer_t)); + // update the size claimed by encoded message directly inside the block + + // encode literals + size_t literalsSize{}; + if (encoder.getNIncompressibleSamples() > 0) { + auto literalsEnd = encoder.writeIncompressible(thisBlock->getCreateLiterals(), thisBlock->getEndOfBlock()); + literalsSize = std::distance(thisBlock->getCreateLiterals(), literalsEnd); + thisBlock->setNLiterals(literalsSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreLiterals {} bytes, offs: {}:{}", literalsSize * sizeof(storageBuffer_t), thisBlock->getOffsLiterals(), thisBlock->getOffsLiterals() + literalsSize * sizeof(storageBuffer_t)); + } + + // write metadata + *thisMetadata = detail::makeMetadataRansV1(encoder.getNStreams(), + rans::utils::getStreamingLowerBound_v, + std::distance(srcBegin, srcEnd), + encoder.getNIncompressibleSamples(), + encoder.getSymbolTablePrecision(), + *metrics.getCoderProperties().min, + *metrics.getCoderProperties().max, + metrics.getDatasetProperties().min, + metrics.getDatasetProperties().alphabetRangeBits, + dictSize, + dataSize, + literalsSize); + + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; +}; // namespace ctf + +template +template +o2::ctf::CTFIOSize EncodedBlocks::pack(const input_IT srcBegin, const input_IT srcEnd, int slot, rans::Metrics::value_type> metrics, buffer_T* buffer) +{ + using storageBuffer_t = W; + using input_t = typename std::iterator_traits::value_type; + + const size_t messageLength = metrics.getDatasetProperties().numSamples; + const auto alphabetRangeBits = metrics.getDatasetProperties().alphabetRangeBits; + + auto* thisBlock = &mBlocks[slot]; + auto* thisMetadata = &mMetadata[slot]; + size_t packedSize = 0; + + if (messageLength == 0) { + *thisMetadata = detail::makeMetadataPack(0, 0, 0, 0); + } else if (metrics.getDatasetProperties().alphabetRangeBits == 0) { + *thisMetadata = detail::makeMetadataPack(messageLength, 0, *srcBegin, 0); + } else { + internal::Packer packer{metrics}; + size_t packingBufferWords = packer.template getPackingBufferSize(messageLength); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, packingBufferWords, buffer); + auto packedMessageEnd = packer.pack(srcBegin, srcEnd, thisBlock->getCreateData(), thisBlock->getEndOfBlock()); + packedSize = std::distance(thisBlock->getCreateData(), packedMessageEnd); + *thisMetadata = detail::makeMetadataPack(messageLength, packer.getPackingWidth(), packer.getOffset(), packedSize); + thisBlock->setNData(packedSize); + thisBlock->realignBlock(); + } + + LOGP(debug, "StoreData {} bytes, offs: {}:{}", packedSize * sizeof(storageBuffer_t), thisBlock->getOffsData(), thisBlock->getOffsData() + packedSize * sizeof(storageBuffer_t)); + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; +}; + +template +template +o2::ctf::CTFIOSize EncodedBlocks::store(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer) +{ + using storageBuffer_t = W; + using input_t = typename std::iterator_traits::value_type; + + const size_t messageLength = std::distance(srcBegin, srcEnd); + // introduce padding in case literals don't align; + const size_t nSourceElemsPadded = calculatePaddedSize(messageLength); + std::vector tmp(nSourceElemsPadded, {}); + std::copy(srcBegin, srcEnd, std::begin(tmp)); + + const size_t nBufferElems = calculateNDestTElements(messageLength); + auto [thisBlock, thisMetadata] = expandStorage(slot, nBufferElems, buffer); + thisBlock->storeData(nBufferElems, reinterpret_cast(tmp.data())); + + *thisMetadata = detail::makeMetadataStore(messageLength, opt, nBufferElems); + + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; +}; + /// create a special EncodedBlocks containing only dictionaries made from provided vector of frequency tables template -std::vector EncodedBlocks::createDictionaryBlocks(const std::vector& vfreq, const std::vector& vmd) +std::vector EncodedBlocks::createDictionaryBlocks(const std::vector>& vfreq, const std::vector& vmd) { + if (vfreq.size() != N) { throw std::runtime_error(fmt::format("mismatch between the size of frequencies vector {} and number of blocks {}", vfreq.size(), N)); } @@ -979,9 +1536,12 @@ std::vector EncodedBlocks::createDictionaryBlocks(const std::vect std::vector vdict(sz); // memory space for dictionary auto dictBlocks = create(vdict.data(), sz); for (int ib = 0; ib < N; ib++) { - if (vfreq[ib].size()) { - LOG(info) << "adding dictionary of " << vfreq[ib].size() << " words for block " << ib << ", min/max= " << vfreq[ib].getMinSymbol() << "/" << vfreq[ib].getMaxSymbol(); - dictBlocks->mBlocks[ib].storeDict(vfreq[ib].size(), vfreq[ib].data()); + const auto& thisHistogram = vfreq[ib]; + const auto view = rans::trim(rans::makeHistogramView(thisHistogram)); + + if (!view.empty()) { + LOG(info) << "adding dictionary of " << view.size() << " words for block " << ib << ", min/max= " << view.getMin() << "/" << view.getMax(); + dictBlocks->mBlocks[ib].storeDict(view.size(), view.data()); dictBlocks = get(vdict.data()); // !!! rellocation might have invalidated dictBlocks pointer dictBlocks->mMetadata[ib] = vmd[ib]; dictBlocks->mMetadata[ib].opt = Metadata::OptStore::ROOTCompression; // we will compress the dictionary with root! @@ -991,8 +1551,9 @@ std::vector EncodedBlocks::createDictionaryBlocks(const std::vect } dictBlocks->mRegistry.nFilledBlocks++; } - return std::move(vdict); + return vdict; } +#endif template void EncodedBlocks::dump(const std::string& prefix, int ncol) const diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/FileMetaData.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/FileMetaData.h index e696e3798c47d..1408e0e11714a 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/FileMetaData.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/FileMetaData.h @@ -16,6 +16,7 @@ #include #include +#include namespace o2 { @@ -26,25 +27,25 @@ class DataTakingContext; namespace dataformats { -struct FileMetaData { // https://docs.google.com/document/d/1nH9EZEFBSpuZwOWs3RBcfy-6aRChgAqClBv6G06MjH4/edit - std::string LHCPeriod{}; // 1, LHC data taking period + detector name, in case of individual detector data stream, required - std::string lurl{}; // 3, the local EPN path to the CTF or calibration file, required - std::string type{}; // 4, raw or calib or other; default is other, optional - std::string guid{}; // 7, default is auto-generated, optional - std::string surl{}; // 8, the remote storage path where we store the data file, optional - std::string curl{}; // 9, the Grid catalogue path, optional - std::string md5{}; //10, default the checksum of the lurl file; only filled after a successful transfer, if needed, optional - std::string xxhash{}; //11, default calculated from the lurl file, only filled after a successful transfer, if needed, optional - std::string seName{}; // 12, default is taken from the configuration file, optional - std::string seioDaemons{}; // 13, default is taken from the configuration file, optional - std::string priority{}; // 14, low or high; default is low, optional - std::string detComposition{}; // 17, contains a list of detectors, optional - std::string run{}; // 2, run number, required - long ctime{}; // 5, default the timestamp of the lurl file, optional - size_t size{}; // 6, default the size of the lurl file, optional - int persistent{}; // 16, default is forever, optional +struct FileMetaData { // https://docs.google.com/document/d/1nH9EZEFBSpuZwOWs3RBcfy-6aRChgAqClBv6G06MjH4/edit + std::string LHCPeriod{}; // 1, LHC data taking period + detector name, in case of individual detector data stream, required + std::string lurl{}; // 3, the local EPN path to the CTF or calibration file, required + std::string type{}; // 4, raw or calib or other; default is other, optional + std::string guid{}; // 7, default is auto-generated, optional + std::string surl{}; // 8, the remote storage path where we store the data file, optional + std::string curl{}; // 9, the Grid catalogue path, optional + std::string md5{}; // 10, default the checksum of the lurl file; only filled after a successful transfer, if needed, optional + std::string xxhash{}; // 11, default calculated from the lurl file, only filled after a successful transfer, if needed, optional + std::string seName{}; // 12, default is taken from the configuration file, optional + std::string seioDaemons{}; // 13, default is taken from the configuration file, optional + std::string priority{}; // 14, low or high; default is low, optional + std::string detComposition{}; // 17, contains a list of detectors, optional + std::string run{}; // 2, run number, required + long ctime{}; // 5, default the timestamp of the lurl file, optional + size_t size{}; // 6, default the size of the lurl file, optional + int persistent{}; // 16, default is forever, optional std::vector tfOrbits; // 15, comma-sep. list of 1st orbits of TFs, optional - bool fillFileData(const std::string& fname); + bool fillFileData(const std::string& fname, bool fillmd5 = false, const std::string& tmpEnding = ""); void setDataTakingContext(const o2::framework::DataTakingContext& dtc); std::string asString() const; }; diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/Metadata.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/Metadata.h new file mode 100644 index 0000000000000..975522767dce1 --- /dev/null +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/Metadata.h @@ -0,0 +1,206 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Metadata.h +/// \brief Metadata required to decode a Block + +/// The Metadata required to decoded the CTF of particular detector. + +#ifndef ALICEO2_METADATA_H +#define ALICEO2_METADATA_H + +#include +#include + +namespace o2::ctf +{ + +struct Metadata { + enum class OptStore : uint8_t { // describe how the store the data described by this metadata + EENCODE, // entropy encoding applied + ROOTCompression, // original data repacked to array with slot-size = streamSize and saved with root compression + NONE, // original data repacked to array with slot-size = streamSize and saved w/o compression + NODATA, // no data was provided + PACK, // use Bitpacking + EENCODE_OR_PACK // decide at runtime if to encode or pack + }; + uint8_t nStreams = 0; // Amount of concurrent Streams used by the encoder. only used by rANS version >=1. + size_t messageLength = 0; // Message length (multiply with messageWordSize to get size in Bytes). + size_t nLiterals = 0; // Number of samples that were stored as literals. + uint8_t messageWordSize = 0; // size in Bytes of a symbol in the encoded message. + uint8_t coderType = 0; // what type of CTF Coder is used? (32 vs 64 bit coders). + uint8_t streamSize = 0; // number of Bytes emmitted during rANS stream out (ransCompat) or lower renorming bound (ransV1). + uint8_t probabilityBits = 0; // The encoder renormed the distribution of source symbols to sum up to 2^probabilityBits. + OptStore opt = OptStore::EENCODE; // The type of storage operation that was conducted. + int32_t min = 0; // min symbol of the source dataset. + int32_t max = 0; // max symbol of the source dataset. + int32_t literalsPackingOffset = 0; // Offset from 0 used for bit packing of literal (incompressible) symbols. only used by rANS version >=1. + uint8_t literalsPackingWidth = 0; // Amount of bits used to pack literal (incompressible) symbols. only used by rANS version >=1. + int nDictWords = 0; // Amount of words used to store the encoding dictionary. + int nDataWords = 0; // Amount of words used to store the actual data. + int nLiteralWords = 0; // Amount of words used to store literal (incompressible) samples. + + /** + * @brief Uncompressed size of stored data in bytes + * + * @return size_t Uncompressed size in bytes + */ + size_t getUncompressedSize() const { return messageLength * messageWordSize; } + + /** + * @brief Size of the stored, compressed data in multiples of the underlying buffer word size + * + * @return size_t The size in multiples of the underlying buffer word size + * @warning This size is in number of words of the underlying storage buffer. + * Multiply with the size of the storage buffer type to get the correct size in bytes. + */ + size_t getCompressedSize() const { return nDictWords + nDataWords + nLiteralWords; } + void clear() + { + nStreams = 0; + messageLength = 0; + nLiterals = 0; + messageWordSize = 0; + coderType = 0; + streamSize = 0; + probabilityBits = 0; + min = 0; + max = 0; + literalsPackingOffset = 0; + literalsPackingWidth = 0; + nDictWords = 0; + nDataWords = 0; + nLiteralWords = 0; + } + ClassDefNV(Metadata, 3); +}; + +namespace detail +{ + +template +[[nodiscard]] inline constexpr Metadata makeMetadataRansCompat(size_t nStreams, size_t messageLength, + size_t nLiterals, size_t symbolTablePrecision, + source_T min, source_T max, size_t dictWords, + size_t dataWords, size_t literalWords) noexcept +{ + return Metadata{ + static_cast(nStreams), + messageLength, + nLiterals, + static_cast(sizeof(source_T)), + static_cast(sizeof(state_T)), + static_cast(sizeof(stream_T)), + static_cast(symbolTablePrecision), + Metadata::OptStore::EENCODE, + static_cast(min), + static_cast(max), + static_cast(0), + static_cast(sizeof(source_T)), + static_cast(dictWords), + static_cast(dataWords), + static_cast(literalWords)}; +}; + +template +[[nodiscard]] inline constexpr Metadata makeMetadataRansDict(size_t symbolTablePrecision, source_T min, + source_T max, size_t dictWords, ctf::Metadata::OptStore optStore) noexcept +{ + return Metadata{ + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(sizeof(source_T)), + static_cast(0), + static_cast(0), + static_cast(symbolTablePrecision), + optStore, + static_cast(min), + static_cast(max), + static_cast(0), + static_cast(0), + static_cast(dictWords), + static_cast(0), + static_cast(0)}; +}; + +template +[[nodiscard]] inline constexpr Metadata makeMetadataRansV1(size_t nStreams, size_t streamingLowerBound, size_t messageLength, + size_t nLiterals, size_t symbolTablePrecision, + source_T dictMin, source_T dictMax, + source_T literalsOffset, size_t literalsPackingWidth, size_t dictWords, + size_t dataWords, size_t literalWords) noexcept +{ + return Metadata{ + static_cast(nStreams), + messageLength, + nLiterals, + static_cast(sizeof(source_T)), + static_cast(sizeof(state_T)), + static_cast(streamingLowerBound), + static_cast(symbolTablePrecision), + Metadata::OptStore::EENCODE, + static_cast(dictMin), + static_cast(dictMax), + static_cast(literalsOffset), + static_cast(literalsPackingWidth), + static_cast(dictWords), + static_cast(dataWords), + static_cast(literalWords)}; +}; + +template +[[nodiscard]] inline constexpr Metadata makeMetadataPack(size_t messageLength, size_t packingWidth, + source_T packingOffset, size_t dataWords) noexcept +{ + return Metadata{ + static_cast(1), + messageLength, + static_cast(0), + static_cast(sizeof(source_T)), + static_cast(0), + static_cast(0), + static_cast(packingWidth), + Metadata::OptStore::PACK, + static_cast(packingOffset), + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(dataWords), + static_cast(0)}; +}; + +template +[[nodiscard]] inline constexpr Metadata makeMetadataStore(size_t messageLength, Metadata::OptStore opStore, size_t dataWords) noexcept +{ + return Metadata{ + static_cast(0), + messageLength, + static_cast(0), + static_cast(sizeof(source_T)), + static_cast(0), + static_cast(sizeof(buffer_T)), + static_cast(0), + opStore, + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(dataWords), + static_cast(0)}; +}; + +} // namespace detail +} // namespace o2::ctf + +#endif \ No newline at end of file diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h index 92fc2bfa46a6d..72782d4ed7bdf 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h @@ -95,7 +95,12 @@ class SimTraits /*IT3*/ VS{ "IT3Hit" }, /*TRK*/ VS{ "TRKHit" }, /*FT3*/ VS{ "FT3Hit" }, - /*FCT*/ VS{ "FCTHit" } + /*FCT*/ VS{ "FCTHit" }, + /*TF3*/ VS{ "TF3Hit" }, + /*RCH*/ VS{ "RCHHit" }, + /*MI3*/ VS{ "MI3Hit" }, + /*ECL*/ VS{ "ECLHit" }, + /*FD */ VS{ "FDHit" } #endif }; // clang-format on @@ -119,6 +124,10 @@ namespace itsmft { class Hit; } +namespace trkft3 +{ +class Hit; +} namespace tof { class HitType; @@ -163,7 +172,10 @@ namespace tpc { class HitGroup; } - +namespace focal +{ +class Hit; +} namespace detectors { @@ -227,6 +239,10 @@ template <> struct DetIDToHitTypes { using HitType = o2::tpc::HitGroup; }; +template <> +struct DetIDToHitTypes { + using HitType = o2::focal::Hit; +}; #ifdef ENABLE_UPGRADES template <> struct DetIDToHitTypes { @@ -234,11 +250,11 @@ struct DetIDToHitTypes { }; template <> struct DetIDToHitTypes { - using HitType = o2::itsmft::Hit; + using HitType = o2::trkft3::Hit; }; template <> struct DetIDToHitTypes { - using HitType = o2::itsmft::Hit; + using HitType = o2::trkft3::Hit; }; template <> struct DetIDToHitTypes { diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h new file mode 100644 index 0000000000000..5335514278e26 --- /dev/null +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h @@ -0,0 +1,119 @@ +// Copyright 2019-2023 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CTFEntropyCoder.h +/// \author michael.lettrich@cern.ch +/// \brief Interface for externally provided rANS entropy coders + +#ifndef ALICEO2_EXTERNALENTROPYCODER_H_ +#define ALICEO2_EXTERNALENTROPYCODER_H_ + +#include + +#include "DetectorsCommonDataFormats/internal/Packer.h" + +#include "rANS/encode.h" +#include "rANS/factory.h" +#include "rANS/histogram.h" +#include "rANS/metrics.h" +#include "rANS/serialize.h" + +namespace o2::ctf::internal +{ + +template +class ExternalEntropyCoder +{ + public: + using source_type = source_T; + using encoder_type = typename rans::denseEncoder_type; + + ExternalEntropyCoder(const encoder_type& encoder); + + [[nodiscard]] inline const encoder_type& getEncoder() const noexcept { return *mEncoder; }; + + template + [[nodiscard]] inline size_t computePayloadSizeEstimate(size_t nElements, double_t safetyFactor = 1); + + template + [[nodiscard]] dst_IT encode(src_IT srcBegin, src_IT srcEnd, dst_IT dstBegin, dst_IT dstEnd); + + [[nodiscard]] inline size_t getNIncompressibleSamples() const noexcept { return mIncompressibleBuffer.size(); }; + + [[nodiscard]] inline source_type getIncompressibleSymbolOffset() const noexcept { return mIncompressiblePacker.getOffset(); }; + + [[nodiscard]] inline size_t getIncompressibleSymbolPackingBits() const noexcept { return mIncompressiblePacker.getPackingWidth(); }; + + template + [[nodiscard]] size_t computePackedIncompressibleSize() const noexcept; + + template + [[nodiscard]] dst_T* writeIncompressible(dst_T* dstBegin, dst_T* dstEnd) const; + + private: + const encoder_type* mEncoder{}; + std::vector mIncompressibleBuffer{}; + Packer mIncompressiblePacker{}; +}; + +template +ExternalEntropyCoder::ExternalEntropyCoder(const encoder_type& encoder) : mEncoder{&encoder} +{ + if (!getEncoder().getSymbolTable().hasEscapeSymbol()) { + throw std::runtime_error("External entropy encoder must be able to handle incompressible symbols."); + } +}; + +template +template +[[nodiscard]] inline size_t ExternalEntropyCoder::computePayloadSizeEstimate(size_t nElements, double_t safetyFactor) +{ + constexpr size_t Overhead = 10 * rans::utils::pow2(10); // 10KB overhead safety margin + const double_t RelativeSafetyFactor = 2.0 * safetyFactor; + const size_t messageSizeB = nElements * sizeof(source_type); + return rans::utils::nBytesTo(std::ceil(RelativeSafetyFactor * messageSizeB) + Overhead); +} + +template +template +[[nodiscard]] dst_IT ExternalEntropyCoder::encode(src_IT srcBegin, src_IT srcEnd, dst_IT dstBegin, dst_IT dstEnd) +{ + const size_t incompressibleSymbolFrequency = [&]() { + const auto& symbolTable = mEncoder->getSymbolTable(); + const double_t incompressibleSymbolProbability = static_cast(symbolTable.getEscapeSymbol().getFrequency()) / rans::utils::pow2(symbolTable.getPrecision()); + return std::ceil(std::distance(srcBegin, srcEnd) * incompressibleSymbolProbability); + }(); + + mIncompressibleBuffer.reserve(incompressibleSymbolFrequency); + auto [encodedMessageEnd, literalsEnd] = mEncoder->process(srcBegin, srcEnd, dstBegin, std::back_inserter(mIncompressibleBuffer)); + rans::utils::checkBounds(encodedMessageEnd, dstEnd); + mIncompressiblePacker = Packer{mIncompressibleBuffer.data(), mIncompressibleBuffer.data() + mIncompressibleBuffer.size()}; + + return encodedMessageEnd; +}; + +template +template +[[nodiscard]] inline size_t ExternalEntropyCoder::computePackedIncompressibleSize() const noexcept +{ + return mIncompressiblePacker.template getPackingBufferSize(mIncompressibleBuffer.size()); +}; + +template +template +[[nodiscard]] inline dst_T* ExternalEntropyCoder::writeIncompressible(dst_T* dstBegin, dst_T* dstEnd) const +{ + return mIncompressiblePacker.pack(mIncompressibleBuffer.data(), mIncompressibleBuffer.size(), dstBegin, dstEnd); +}; + +} // namespace o2::ctf::internal + +#endif /* ALICEO2_EXTERNALENTROPYCODER_H_ */ \ No newline at end of file diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h new file mode 100644 index 0000000000000..8601469b34142 --- /dev/null +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h @@ -0,0 +1,312 @@ +// Copyright 2019-2023 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CTFEntropyCoder.h +/// \author michael.lettrich@cern.ch +/// \brief ANS Entropy Coding and packing specialization for CTF Coders + +#ifndef ALICEO2_INPLACEENTROPYCODER_H_ +#define ALICEO2_INPLACEENTROPYCODER_H_ + +#include +#include +#include + +#include "DetectorsCommonDataFormats/internal/Packer.h" + +#include "rANS/encode.h" +#include "rANS/factory.h" +#include "rANS/histogram.h" +#include "rANS/metrics.h" +#include "rANS/serialize.h" + +namespace o2::ctf::internal +{ + +template +class InplaceEntropyCoder +{ + using dense_histogram_type = rans::DenseHistogram; + using adaptive_histogram_type = rans::AdaptiveHistogram; + using sparse_histogram_type = rans::SparseHistogram; + + using dense_encoder_type = rans::denseEncoder_type; + using adaptive_encoder_type = rans::adaptiveEncoder_type; + using sparse_encoder_type = rans::sparseEncoder_type; + + using dict_buffer_type = std::vector; + + public: + using source_type = source_T; + using metrics_type = rans::Metrics; + using packer_type = Packer; + using histogram_type = std::variant; + using encoder_type = std::variant; + using incompressible_buffer_type = std::vector; + + InplaceEntropyCoder() = default; + + template + InplaceEntropyCoder(source_IT srcBegin, source_IT srcEnd); + + template + InplaceEntropyCoder(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max); + + void makeEncoder(); + + // getters + + [[nodiscard]] inline const metrics_type& getMetrics() const noexcept { return mMetrics; }; + + [[nodiscard]] inline size_t getNIncompressibleSamples() const noexcept { return mIncompressibleBuffer.size(); }; + + [[nodiscard]] size_t getNStreams() const; + + [[nodiscard]] size_t getSymbolTablePrecision() const; + + template + [[nodiscard]] size_t getPackedIncompressibleSize() const noexcept; + + // operations + template + [[nodiscard]] dst_IT encode(src_IT srcBegin, src_IT srcEnd, dst_IT dstBegin, dst_IT dstEnd); + + template + [[nodiscard]] dst_IT writeDictionary(dst_IT dstBegin, dst_IT dstEnd); + + template + [[nodiscard]] dst_T* writeIncompressible(dst_T* dstBegin, dst_T* dstEnd); + + private: + template ::value_type) < 4), bool> = true> + void init(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max); + + template ::value_type) == 4), bool> = true> + void init(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max); + + template ::value_type) < 4), bool> = true> + void init(source_IT srcBegin, source_IT srcEnd); + + template ::value_type) == 4), bool> = true> + void init(source_IT srcBegin, source_IT srcEnd); + + template + void serializeDictionary(const container_T&); + + std::optional mHistogram{}; + metrics_type mMetrics{}; + std::optional mEncoder{}; + incompressible_buffer_type mIncompressibleBuffer{}; + dict_buffer_type mDictBuffer{}; + packer_type mIncompressiblePacker{}; +}; + +template +template +InplaceEntropyCoder::InplaceEntropyCoder(src_IT srcBegin, src_IT srcEnd) +{ + static_assert(std::is_same_v::value_type>); + + const size_t nSamples = std::distance(srcBegin, srcEnd); + if constexpr (std::is_pointer_v) { + if (sizeof(source_type) > 2 && nSamples > 0) { + const auto [min, max] = rans::internal::minmax(gsl::span(srcBegin, srcEnd)); + init(srcBegin, srcEnd, min, max); + } else { + init(srcBegin, srcEnd); + } + } else { + init(srcBegin, srcEnd); + } + + mIncompressiblePacker = Packer(mMetrics); +}; + +template +template +InplaceEntropyCoder::InplaceEntropyCoder(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max) +{ + static_assert(std::is_same_v::value_type>); + init(srcBegin, srcEnd, min, max); + mIncompressiblePacker = Packer(mMetrics); +}; + +template +[[nodiscard]] inline size_t InplaceEntropyCoder::getNStreams() const +{ + size_t nStreams{}; + std::visit([&, this](auto&& encoder) { nStreams = encoder.getNStreams(); }, *mEncoder); + return nStreams; +} + +template +[[nodiscard]] inline size_t InplaceEntropyCoder::getSymbolTablePrecision() const +{ + size_t precision{}; + std::visit([&, this](auto&& encoder) { precision = encoder.getSymbolTable().getPrecision(); }, *mEncoder); + return precision; +} + +template +void InplaceEntropyCoder::makeEncoder() +{ + std::visit([this](auto&& histogram) { + auto renormed = rans::renorm(std::move(histogram), mMetrics); + + if (std::holds_alternative(*mHistogram)) { + serializeDictionary(renormed); + } + + const size_t rangeBits = rans::utils::getRangeBits(*mMetrics.getCoderProperties().min, *mMetrics.getCoderProperties().max); + const size_t nUsedAlphabetSymbols = mMetrics.getDatasetProperties().nUsedAlphabetSymbols; + + if (rangeBits <= 18) { + // dense symbol tables if they fit into cache, or source data covers the range of the alphabet well + mEncoder = encoder_type{std::in_place_type, renormed}; + } else if (nUsedAlphabetSymbols < rans::utils::pow2(14)) { + // sparse symbol table makes sense if it fits into L3 Cache + mEncoder = encoder_type{std::in_place_type, renormed}; + } else { + // adaptive symbol table otherwise + mEncoder = encoder_type{std::in_place_type, renormed}; + } + }, + *mHistogram); +}; + +template +template +[[nodiscard]] dst_IT InplaceEntropyCoder::encode(src_IT srcBegin, src_IT srcEnd, dst_IT dstBegin, dst_IT dstEnd) +{ + static_assert(std::is_same_v::value_type>); + + dst_IT messageEnd = dstBegin; + + std::visit([&, this](auto&& encoder) { + if (encoder.getSymbolTable().hasEscapeSymbol()) { + mIncompressibleBuffer.reserve(*mMetrics.getCoderProperties().nIncompressibleSamples); + auto [encodedMessageEnd, literalsEnd] = encoder.process(srcBegin, srcEnd, dstBegin, std::back_inserter(mIncompressibleBuffer)); + messageEnd = encodedMessageEnd; + } else { + messageEnd = encoder.process(srcBegin, srcEnd, dstBegin); + } + rans::utils::checkBounds(messageEnd, dstEnd); + }, + *mEncoder); + + return messageEnd; +}; + +template +template +[[nodiscard]] inline dst_IT InplaceEntropyCoder::writeDictionary(dst_IT dstBegin, dst_IT dstEnd) +{ + static_assert(std::is_pointer_v); + + using dst_type = std::remove_pointer_t; + + dst_IT ret{}; + if (mDictBuffer.empty()) { + std::visit([&, this](auto&& encoder) { ret = rans::compressRenormedDictionary(encoder.getSymbolTable(), dstBegin); }, *mEncoder); + } else { + // copy + std::memcpy(dstBegin, mDictBuffer.data(), mDictBuffer.size()); + + // determine location of end + auto end = reinterpret_cast(dstBegin) + mDictBuffer.size(); + // realign pointer + constexpr size_t alignment = std::alignment_of_v; + end += (alignment - reinterpret_cast(end) % alignment) % alignment; + // and convert it back to ret + ret = reinterpret_cast(end); + } + + rans::utils::checkBounds(ret, dstEnd); + return ret; +}; + +template +template +inline dst_T* InplaceEntropyCoder::writeIncompressible(dst_T* dstBegin, dst_T* dstEnd) +{ + return mIncompressiblePacker.pack(mIncompressibleBuffer.data(), mIncompressibleBuffer.size(), dstBegin, dstEnd); +}; + +template +template +[[nodiscard]] inline size_t InplaceEntropyCoder::getPackedIncompressibleSize() const noexcept +{ + return mIncompressiblePacker.template getPackingBufferSize(getNIncompressibleSamples()); +} + +template +template ::value_type) < 4), bool>> +void InplaceEntropyCoder::init(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max) +{ + mHistogram.emplace(histogram_type{rans::makeDenseHistogram::fromSamples(srcBegin, srcEnd)}); + mMetrics = metrics_type{std::get(*mHistogram), min, max}; +}; + +template +template ::value_type) == 4), bool>> +void InplaceEntropyCoder::init(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max) +{ + const size_t nSamples = std::distance(srcBegin, srcEnd); + const size_t rangeBits = rans::utils::getRangeBits(min, max); + + if ((rangeBits <= 18) || ((nSamples / rans::utils::pow2(rangeBits)) >= 0.80)) { + // either the range of source symbols is distrubuted such that it fits into L3 Cache + // Or it is possible for the data to cover a very significant fraction of the total [min,max] range + mHistogram = histogram_type{std::in_place_type, rans::makeDenseHistogram::fromSamples(srcBegin, srcEnd, min, max)}; + mMetrics = metrics_type{std::get(*mHistogram), min, max}; + } else if (nSamples / rans::utils::pow2(rangeBits) <= 0.3) { + // or the range of source symbols is spread very thinly accross a large range + mHistogram = histogram_type{std::in_place_type, rans::makeSparseHistogram::fromSamples(srcBegin, srcEnd)}; + mMetrics = metrics_type{std::get(*mHistogram), min, max}; + } else { + // no strong evidence of either extreme case + mHistogram = histogram_type{std::in_place_type, rans::makeAdaptiveHistogram::fromSamples(srcBegin, srcEnd)}; + mMetrics = metrics_type{std::get(*mHistogram), min, max}; + } +}; + +template +template ::value_type) < 4), bool>> +void InplaceEntropyCoder::init(source_IT srcBegin, source_IT srcEnd) +{ + mHistogram = histogram_type{std::in_place_type, rans::makeDenseHistogram::fromSamples(srcBegin, srcEnd)}; + mMetrics = metrics_type{std::get(*mHistogram)}; +}; + +template +template ::value_type) == 4), bool>> +void InplaceEntropyCoder::init(source_IT srcBegin, source_IT srcEnd) +{ + mHistogram = histogram_type{std::in_place_type, rans::makeSparseHistogram::fromSamples(srcBegin, srcEnd)}; + mMetrics = metrics_type{std::get(*mHistogram)}; +}; + +template +template +void InplaceEntropyCoder::serializeDictionary(const container_T& renormedHistogram) +{ + + mDictBuffer.resize(mMetrics.getSizeEstimate().getCompressedDictionarySize(), 0); + auto end = rans::compressRenormedDictionary(renormedHistogram, mDictBuffer.data()); + rans::utils::checkBounds(end, mDictBuffer.data() + mDictBuffer.size()); + mDictBuffer.resize(std::distance(mDictBuffer.data(), end)); + + assert(mDictBuffer.size() > 0); +}; + +} // namespace o2::ctf::internal + +#endif /* ALICEO2_INPLACEENTROPYCODER_H_ */ \ No newline at end of file diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/Packer.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/Packer.h new file mode 100644 index 0000000000000..fa11e9f6dac6d --- /dev/null +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/Packer.h @@ -0,0 +1,124 @@ +// Copyright 2019-2023 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Packer.h +/// \author michael.lettrich@cern.ch +/// \brief Interfaces for BitPacking using librans + +#ifndef ALICEO2_PACKER_H_ +#define ALICEO2_PACKER_H_ + +#ifndef __CLING__ +#include "rANS/pack.h" +#include "rANS/metrics.h" +#endif + +namespace o2::ctf::internal +{ + +template +class Packer +{ + public: + using source_type = source_T; + + Packer() = default; +#ifndef __CLING__ + explicit Packer(rans::Metrics& metrics) : mOffset{metrics.getDatasetProperties().min}, + mPackingWidth{metrics.getDatasetProperties().alphabetRangeBits} {}; +#endif + + template + Packer(source_IT srcBegin, source_IT srcEnd); + + [[nodiscard]] inline source_type getOffset() const noexcept { return mOffset; }; + + [[nodiscard]] inline size_t getPackingWidth() const noexcept { return mPackingWidth; }; + + template + [[nodiscard]] size_t getPackingBufferSize(size_t messageLength) const noexcept; + + template + [[nodiscard]] dst_T* pack(source_IT srcBegin, source_IT srcEnd, dst_T* dstBegin, dst_T* dstEnd) const; + + template + [[nodiscard]] dst_T* pack(const source_T* __restrict srcBegin, size_t extent, dst_T* dstBegin, dst_T* dstEnd) const; + + private: + source_type mOffset{}; + size_t mPackingWidth{}; +}; + +template +template +Packer::Packer(source_IT srcBegin, source_IT srcEnd) +{ +#ifndef __CLING__ + static_assert(rans::utils::isCompatibleIter_v); + if (srcBegin != srcEnd) { + + const auto [min, max] = [&]() { + if constexpr (std::is_pointer_v) { + return rans::utils::minmax(gsl::span(srcBegin, srcEnd)); + } else { + const auto [minIter, maxIter] = std::minmax_element(srcBegin, srcEnd); + return std::make_pair(*minIter, *maxIter); + } + }(); + + mOffset = min; + mPackingWidth = rans::utils::getRangeBits(min, max); + } +#endif +}; + +template +template +[[nodiscard]] inline size_t Packer::getPackingBufferSize(size_t messageLength) const noexcept +{ +#ifndef __CLING__ + return rans::computePackingBufferSize(messageLength, mPackingWidth); +#else + return 0; +#endif +}; + +template +template +[[nodiscard]] inline dst_T* Packer::pack(const source_T* __restrict srcBegin, size_t extent, dst_T* dstBegin, dst_T* dstEnd) const +{ + return pack(srcBegin, srcBegin + extent, dstBegin, dstEnd); +} + +template +template +[[nodiscard]] dst_T* Packer::pack(source_IT srcBegin, source_IT srcEnd, dst_T* dstBegin, dst_T* dstEnd) const +{ + static_assert(std::is_same_v::value_type>); + size_t extent = std::distance(srcBegin, srcEnd); + + if (extent == 0) { + return dstBegin; + } +#ifndef __CLING__ + rans::BitPtr packEnd = rans::pack(srcBegin, extent, dstBegin, mPackingWidth, mOffset); + auto* end = packEnd.toPtr(); + ++end; // one past end iterator; + rans::utils::checkBounds(end, dstEnd); + return end; +#else + return nullptr; +#endif +}; + +} // namespace o2::ctf::internal + +#endif /* ALICEO2_PACKER_H_ */ \ No newline at end of file diff --git a/DataFormats/Detectors/Common/src/AlignParam.cxx b/DataFormats/Detectors/Common/src/AlignParam.cxx index 90f2a349607a1..35cbbbae7c2f8 100644 --- a/DataFormats/Detectors/Common/src/AlignParam.cxx +++ b/DataFormats/Detectors/Common/src/AlignParam.cxx @@ -12,12 +12,12 @@ /// \file AlignParam.cxx /// \brief Implementation of the base alignment parameters class -#include #include #include #include #include +#include "Framework/Logger.h" #include "DetectorsCommonDataFormats/AlignParam.h" using namespace o2::detectors; @@ -26,8 +26,9 @@ using namespace o2::detectors; AlignParam::AlignParam(const char* symname, int algID, // volume symbolic name and its alignable ID double x, double y, double z, // delta translation double psi, double theta, double phi, // delta rotation - bool global) // global (preferable) or local delta definition - : mSymName(symname), mAlignableID(algID) + bool global, // global (preferable) or local delta definition + bool convertLocalToGlobal) // if local is provided, convert it to global + : mSymName(symname), mIsGlobal(global || convertLocalToGlobal), mAlignableID(algID) { /// standard constructor with 3 translation + 3 rotation parameters /// If the user explicitly sets the global variable to false then the @@ -35,23 +36,22 @@ AlignParam::AlignParam(const char* symname, int algID, // volume symbolic /// This requires to have a gGeoMenager active instance, otherwise the /// constructor will fail (no object created) - if (global) { - setGlobalParams(x, y, z, psi, theta, phi); - } else { + setParams(x, y, z, psi, theta, phi); + if (!global && convertLocalToGlobal) { setLocalParams(x, y, z, psi, theta, phi); } } //___________________________________________________ -AlignParam::AlignParam(const char* symname, int algID, TGeoMatrix& m, bool global) - : mSymName(symname), mAlignableID(algID) +AlignParam::AlignParam(const char* symname, int algID, TGeoMatrix& m, bool global, bool convertLocalToGlobal) + : mSymName(symname), mIsGlobal(global || convertLocalToGlobal), mAlignableID(algID) { setTranslation(m); if (!setRotation(m)) { const double* rot = m.GetRotationMatrix(); throw std::runtime_error(fmt::format("Failed to extract roll-pitch-yall angles from [[{},{},{}], [{},{},{}], [{},{},{}] for {}", rot[0], rot[1], rot[2], rot[3], rot[4], rot[5], rot[6], rot[7], rot[8], symname)); } - if (!global && !setLocalParams(mX, mY, mZ, mPsi, mTheta, mPhi)) { + if (!global && convertLocalToGlobal && !setLocalParams(mX, mY, mZ, mPsi, mTheta, mPhi)) { throw std::runtime_error(fmt::format("Alignment creation for {} failed: geomManager is absent", symname)); } } @@ -223,6 +223,10 @@ bool AlignParam::createLocalMatrix(TGeoHMatrix& m) const // In case that the TGeo was not initialized or not closed, // returns false and the object parameters are not set. // + m = createMatrix(); + if (!mIsGlobal) { + return true; + } if (!gGeoManager || !gGeoManager->IsClosed()) { LOG(error) << "Can't get the local alignment object parameters! gGeoManager doesn't exist or it is still open!"; return false; @@ -247,7 +251,6 @@ bool AlignParam::createLocalMatrix(TGeoHMatrix& m) const LOG(error) << "Volume name or path " << symname << " is not valid!"; return false; } - m = createMatrix(); TGeoHMatrix gprime, gprimeinv; gprime = *node->GetMatrix(); gprimeinv = gprime.Inverse(); @@ -258,7 +261,7 @@ bool AlignParam::createLocalMatrix(TGeoHMatrix& m) const } //_____________________________________________________________________________ -bool AlignParam::applyToGeometry() const +bool AlignParam::applyToGeometry(int printLevel) const { /// Apply the current alignment object to the TGeo geometry /// This method returns FALSE if the symname of the object was not @@ -302,18 +305,26 @@ bool AlignParam::applyToGeometry() const } // double threshold = 0.001; + TGeoHMatrix* align = new TGeoHMatrix(createMatrix()); + if (mIsGlobal) { + align->Multiply(node->GetMatrix()); + align->MultiplyLeft(node->GetMatrix(node->GetLevel() - 1)->Inverse()); + } else { + align->MultiplyLeft(node->GetOriginalMatrix()); + } - TGeoHMatrix gprime = *node->GetMatrix(); - TGeoHMatrix align = createMatrix(); - gprime.MultiplyLeft(&align); - TGeoHMatrix* ginv = new TGeoHMatrix; // TGeoPhysicalNode takes and manages raw pointer, need naked new! - TGeoHMatrix* g = node->GetMatrix(node->GetLevel() - 1); - *ginv = g->Inverse(); - *ginv *= gprime; - - LOG(debug) << "Aligning volume " << symname; - - node->Align(ginv); + node->Align(align); + + if (getLevel() <= printLevel) { + LOGP(info, "{:*^100}", symname); + LOGP(info, " - Alignment parameter:"); + print(); + LOGP(info, " - Alignment matrix:"); + align->Print(); + LOGP(info, " - Node:"); + node->Print(); + LOGP(info, "{:~^100}", symname); + } return true; } @@ -344,11 +355,21 @@ int AlignParam::getLevel() const } //_____________________________________________________________________________ -void AlignParam::print() const +void AlignParam::print(bool printLocal) const { - // print parameters - printf("%s : %6d | X: %+e Y: %+e Z: %+e | pitch: %+e roll: %+e yaw: %e\n", getSymName().c_str(), getAlignableID(), getX(), - getY(), getZ(), getPsi(), getTheta(), getPhi()); + if (!printLocal || !mIsGlobal) { + printf("%s (Lvl:%2d): %6d | %s | tra: X: %+e Y: %+e Z: %+e | pitch: %+e roll: %+e yaw: %e\n", getSymName().c_str(), getLevel(), getAlignableID(), (mIsGlobal) ? "G" : "L", + getX(), getY(), getZ(), getPsi(), getTheta(), getPhi()); + } else { + TGeoHMatrix local; + double psi, theta, phi; + if (!createLocalMatrix(local) || !matrixToAngles(local.GetRotationMatrix(), psi, theta, phi)) { + printf("Failed to create local deltas for %s\n", mSymName.c_str()); + return; + } + const auto* tra = local.GetTranslation(); + printf("%s (Lvl:%2d): %6d | L | tra: X: %+e Y: %+e Z: %+e | pitch: %+e roll: %+e yaw: %e\n", getSymName().c_str(), getLevel(), getAlignableID(), tra[0], tra[1], tra[2], phi, theta, psi); + } } //_____________________________________________________________________________ @@ -359,6 +380,14 @@ void AlignParam::setGlobalParams(double x, double y, double z, double psi, doubl setRotation(psi, theta, phi); } +//_____________________________________________________________________________ +void AlignParam::setParams(double x, double y, double z, double psi, double theta, double phi) +{ + /// set parameters of global delta + setTranslation(x, y, z); + setRotation(psi, theta, phi); +} + //_____________________________________________________________________________ void AlignParam::setRotation(double psi, double theta, double phi) { diff --git a/DataFormats/Detectors/Common/src/CTFHeader.cxx b/DataFormats/Detectors/Common/src/CTFHeader.cxx index e704b46c1e240..4332f8aecd3d6 100644 --- a/DataFormats/Detectors/Common/src/CTFHeader.cxx +++ b/DataFormats/Detectors/Common/src/CTFHeader.cxx @@ -18,7 +18,7 @@ using DetID = o2::detectors::DetID; /// describe itsel as a string std::string CTFHeader::describe() const { - return fmt::format("Run:{:07d} TF:{} Orbit:{:08d} CteationTime:{} Detectors: {}", run, tfCounter, firstTForbit, creationTime, DetID::getNames(detectors)); + return fmt::format("Run:{:07d} TF:{} Orbit:{:08d} CreationTime:{} Detectors: {}", run, tfCounter, firstTForbit, creationTime, DetID::getNames(detectors)); } void CTFHeader::print() const diff --git a/DataFormats/Detectors/Common/src/FileMetaData.cxx b/DataFormats/Detectors/Common/src/FileMetaData.cxx index 7a025648a32d3..f9230fd8ac7a3 100644 --- a/DataFormats/Detectors/Common/src/FileMetaData.cxx +++ b/DataFormats/Detectors/Common/src/FileMetaData.cxx @@ -13,6 +13,7 @@ #include "DetectorsCommonDataFormats/FileMetaData.h" #include "Framework/DataTakingContext.h" +#include "CommonUtils/StringUtils.h" #include #include #include @@ -20,14 +21,22 @@ using namespace o2::dataformats; -bool FileMetaData::fillFileData(const std::string& fname) +bool FileMetaData::fillFileData(const std::string& fname, bool fillmd5, const std::string& tmpEnding) { + // fill metadata for fname, accounting that the fname might be temporary one while the real one is fnameFinal try { lurl = std::filesystem::canonical(fname).string(); size = std::filesystem::file_size(lurl); - std::unique_ptr md5ptr{TMD5::FileChecksum(fname.c_str())}; - md5 = md5ptr->AsString(); + if (fillmd5) { + std::unique_ptr md5ptr{TMD5::FileChecksum(fname.c_str())}; + if (md5ptr) { + md5 = md5ptr->AsString(); + } + } ctime = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + if (tmpEnding.size()) { + o2::utils::Str::rtrim(lurl, tmpEnding); + } } catch (std::exception const& e) { LOG(error) << "Failed to fill metadata for file " << fname << ", reason: " << e.what(); return false; diff --git a/DataFormats/Detectors/Common/test/testCTFEntropyCoder.cxx b/DataFormats/Detectors/Common/test/testCTFEntropyCoder.cxx new file mode 100644 index 0000000000000..6b688fcebc7f7 --- /dev/null +++ b/DataFormats/Detectors/Common/test/testCTFEntropyCoder.cxx @@ -0,0 +1,350 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file testCTFEntropyCoder +/// @author Michael Lettrich +/// @brief Test entropy coding using rANS algorithm + +#define BOOST_TEST_MODULE Test CTFEntropyCoder class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "DetectorsCommonDataFormats/internal/Packer.h" +#include "DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h" +#include "DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h" +#include "rANS/histogram.h" +#include "rANS/metrics.h" +#include "rANS/factory.h" +#include "rANS/iterator.h" + +using namespace o2; + +using buffer_type = uint32_t; +using source_types = boost::mp11::mp_list; + +template +class SourceMessage +{ + public: + SourceMessage(size_t messageSize, source_T max = std::numeric_limits::max(), source_T min = std::numeric_limits::min()) : mMin{min}, mMax{max} + { + if (mSourceMessage.empty()) { + std::mt19937 mt(0); // same seed we want always the same distrubution of random numbers; + assert(max >= min); + const size_t draws = (max - min) + 1; + const double probability = 0.5; + std::binomial_distribution dist(draws, probability); + mSourceMessage.resize(messageSize); + std::generate(mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt, min]() -> source_T { return static_cast(dist(mt)) + min; }); + } + } + + inline constexpr source_T getMin() const noexcept { return mMin; }; + inline constexpr source_T getMax() const noexcept { return mMax; }; + inline constexpr auto& get() const noexcept { return mSourceMessage; }; + + private: + source_T mMin{}; + source_T mMax{}; + std::vector mSourceMessage{}; +}; + +class SourceMessageProxy +{ + public: + SourceMessageProxy() = default; + + template + const auto& getMessage() const noexcept + { + if constexpr (std::is_same_v) { + return sourceMessage8u.get(); + } else if constexpr (std::is_same_v) { + return sourceMessage8.get(); + } else if constexpr (std::is_same_v) { + return sourceMessage16u.get(); + } else if constexpr (std::is_same_v) { + return sourceMessage16.get(); + } else if constexpr (std::is_same_v) { + return sourceMessage32u.get(); + } else if constexpr (std::is_same_v) { + return sourceMessage32.get(); + } else { + throw std::runtime_error{"unsupported source type"}; + } + }; + + private: + inline static constexpr size_t MessageSize = rans::utils::pow2(10); + SourceMessage sourceMessage8u{MessageSize}; + SourceMessage sourceMessage8{MessageSize}; + SourceMessage sourceMessage16u{MessageSize}; + SourceMessage sourceMessage16{MessageSize}; + SourceMessage sourceMessage32u{MessageSize, rans::utils::pow2(27)}; + SourceMessage sourceMessage32{MessageSize, rans::utils::pow2(26), -static_cast(rans::utils::pow2(26))}; +}; + +inline const SourceMessageProxy MessageProxy{}; + +template +void encodeInplace(source_IT begin, source_IT end) +{ + using source_type = typename std::iterator_traits::value_type; + + ctf::internal::InplaceEntropyCoder entropyCoder{begin, end}; + // BOOST_CHECK_THROW(entropyCoder.getEncoder(), std::runtime_error); + entropyCoder.makeEncoder(); + + const rans::Metrics& metrics = entropyCoder.getMetrics(); + const rans::SizeEstimate sizeEstimate = metrics.getSizeEstimate(); + + LOGP(info, "dataset[{},{}], coder[{},{}]", metrics.getDatasetProperties().min, metrics.getDatasetProperties().max, *metrics.getCoderProperties().min, *metrics.getCoderProperties().max); + + std::vector encodeBuffer(sizeEstimate.getCompressedDatasetSize(), 0); + std::vector literalSymbolsBuffer(sizeEstimate.getIncompressibleSize(), 0); + std::vector dictBuffer(sizeEstimate.getCompressedDictionarySize(), 0); + + auto encoderEnd = entropyCoder.encode(begin, end, encodeBuffer.data(), encodeBuffer.data() + encodeBuffer.size()); + [[maybe_unused]] auto literalsEnd = entropyCoder.writeIncompressible(literalSymbolsBuffer.data(), literalSymbolsBuffer.data() + literalSymbolsBuffer.size()); + auto dictEnd = entropyCoder.writeDictionary(dictBuffer.data(), dictBuffer.data() + dictBuffer.size()); + // decode + const auto& coderProperties = metrics.getCoderProperties(); + auto decoder = rans::makeDecoder<>::fromRenormed(rans::readRenormedDictionary(dictBuffer.data(), dictEnd, + *coderProperties.min, *coderProperties.max, + *coderProperties.renormingPrecisionBits)); + std::vector literals(entropyCoder.getNIncompressibleSamples()); + + const auto& datasetPropterties = metrics.getDatasetProperties(); + rans::unpack(literalSymbolsBuffer.data(), literals.size(), literals.data(), + datasetPropterties.alphabetRangeBits, datasetPropterties.min); + + size_t messageLength = std::distance(begin, end); + std::vector sourceBuffer(messageLength, 0); + + decoder.process(encoderEnd, sourceBuffer.data(), messageLength, entropyCoder.getNStreams(), literals.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS(sourceBuffer.begin(), sourceBuffer.end(), begin, end); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testInplaceEncoderEmpty, source_T, source_types) +{ + std::vector testMessage{}; + encodeInplace(testMessage.data(), testMessage.data() + testMessage.size()); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testInplaceEncoderPTR, source_T, source_types) +{ + const auto& testMessage = MessageProxy.getMessage(); + encodeInplace(testMessage.data(), testMessage.data() + testMessage.size()); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testInplaceEncoderIter, source_T, source_types) +{ + const auto& testMessage = MessageProxy.getMessage(); + encodeInplace(testMessage.begin(), testMessage.end()); +}; + +template +class ShiftFunctor + +{ + public: + template + inline value_T operator()(iterA_T iterA, iterB_T iterB) const + { + return *iterB + (static_cast(*iterA) << shift); + }; + + template + inline void operator()(iterA_T iterA, iterB_T iterB, value_T value) const + { + *iterA = value >> shift; + *iterB = value & ((0x1 << shift) - 0x1); + }; +}; + +template +auto makeInputIterators(iterA_T iterA, iterB_T iterB, size_t nElements, F functor) +{ + using namespace o2::rans::utils; + + return std::make_tuple(rans::CombinedInputIterator{iterA, iterB, functor}, + rans::CombinedInputIterator{advanceIter(iterA, nElements), advanceIter(iterB, nElements), functor}); +}; + +BOOST_AUTO_TEST_CASE(testInplaceEncoderCombinedIterator) +{ + + const auto& testMessage1 = MessageProxy.getMessage(); + const auto& testMessage2 = MessageProxy.getMessage(); + + auto [begin, end] = makeInputIterators(testMessage1.data(), testMessage2.data(), testMessage1.size(), ShiftFunctor()>{}); + + encodeInplace(begin, end); +}; + +class ExternalEncoderDecoderProxy +{ + public: + ExternalEncoderDecoderProxy() + { + SourceMessageProxy proxy{}; + + auto renormed8u = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + auto renormed8 = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + auto renormed16u = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + auto renormed16 = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + auto renormed32u = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + auto renormed32 = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + + encoder8u = rans::makeDenseEncoder<>::fromRenormed(renormed8u); + encoder8 = rans::makeDenseEncoder<>::fromRenormed(renormed8); + encoder16u = rans::makeDenseEncoder<>::fromRenormed(renormed16u); + encoder16 = rans::makeDenseEncoder<>::fromRenormed(renormed16); + encoder32u = rans::makeDenseEncoder<>::fromRenormed(renormed32u); + encoder32 = rans::makeDenseEncoder<>::fromRenormed(renormed32); + + decoder8u = rans::makeDecoder<>::fromRenormed(renormed8u); + decoder8 = rans::makeDecoder<>::fromRenormed(renormed8); + decoder16u = rans::makeDecoder<>::fromRenormed(renormed16u); + decoder16 = rans::makeDecoder<>::fromRenormed(renormed16); + decoder32u = rans::makeDecoder<>::fromRenormed(renormed32u); + decoder32 = rans::makeDecoder<>::fromRenormed(renormed32); + } + + template + const auto& getEncoder() const noexcept + { + if constexpr (std::is_same_v) { + return encoder8u; + } else if constexpr (std::is_same_v) { + return encoder8; + } else if constexpr (std::is_same_v) { + return encoder16u; + } else if constexpr (std::is_same_v) { + return encoder16; + } else if constexpr (std::is_same_v) { + return encoder32u; + } else if constexpr (std::is_same_v) { + return encoder32; + } else { + throw std::runtime_error{"unsupported encoder type"}; + } + }; + + template + const auto& getDecoder() const noexcept + { + if constexpr (std::is_same_v) { + return decoder8u; + } else if constexpr (std::is_same_v) { + return decoder8; + } else if constexpr (std::is_same_v) { + return decoder16u; + } else if constexpr (std::is_same_v) { + return decoder16; + } else if constexpr (std::is_same_v) { + return decoder32u; + } else if constexpr (std::is_same_v) { + return decoder32; + } else { + throw std::runtime_error{"unsupported encoder type"}; + } + }; + + private: + rans::denseEncoder_type encoder8u{}; + rans::denseEncoder_type encoder8{}; + rans::denseEncoder_type encoder16u{}; + rans::denseEncoder_type encoder16{}; + rans::denseEncoder_type encoder32u{}; + rans::denseEncoder_type encoder32{}; + + rans::defaultDecoder_type decoder8u{}; + rans::defaultDecoder_type decoder8{}; + rans::defaultDecoder_type decoder16u{}; + rans::defaultDecoder_type decoder16{}; + rans::defaultDecoder_type decoder32u{}; + rans::defaultDecoder_type decoder32{}; +}; + +ExternalEncoderDecoderProxy ExternalEncoders{}; + +template +void encodeExternal(source_IT begin, source_IT end) +{ + using source_type = typename std::iterator_traits::value_type; + + ctf::internal::ExternalEntropyCoder entropyCoder{ExternalEncoders.getEncoder()}; + + const size_t sourceExtent = std::distance(begin, end); + std::vector encodeBuffer(entropyCoder.template computePayloadSizeEstimate(sourceExtent), 0); + auto encoderEnd = entropyCoder.encode(begin, end, encodeBuffer.data(), encodeBuffer.data() + encodeBuffer.size()); + + std::vector literalSymbolsBuffer(entropyCoder.template computePackedIncompressibleSize(), 0); + [[maybe_unused]] auto literalsEnd = entropyCoder.writeIncompressible(literalSymbolsBuffer.data(), literalSymbolsBuffer.data() + literalSymbolsBuffer.size()); + + // decode + auto decoder = ExternalEncoders.getDecoder(); + std::vector literals((entropyCoder.getNIncompressibleSamples())); + + rans::unpack(literalSymbolsBuffer.data(), literals.size(), literals.data(), + entropyCoder.getIncompressibleSymbolPackingBits(), entropyCoder.getIncompressibleSymbolOffset()); + + size_t messageLength = std::distance(begin, end); + std::vector sourceBuffer(messageLength, 0); + + decoder.process(encoderEnd, sourceBuffer.data(), messageLength, entropyCoder.getEncoder().getNStreams(), literals.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS(sourceBuffer.begin(), sourceBuffer.end(), begin, end); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testExternalEncoderEmpty, source_T, source_types) +{ + std::vector testMessage{}; + encodeExternal(testMessage.data(), testMessage.data() + testMessage.size()); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testExternalEncoderPTR, source_T, source_types) +{ + const auto& testMessage = MessageProxy.getMessage(); + encodeExternal(testMessage.data(), testMessage.data() + testMessage.size()); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testExternalEncoderIter, source_T, source_types) +{ + const auto& testMessage = MessageProxy.getMessage(); + encodeExternal(testMessage.begin(), testMessage.end()); +}; + +BOOST_AUTO_TEST_CASE(testExternalEncoderCombinedIterator) +{ + + const auto& testMessage1 = MessageProxy.getMessage(); + const auto& testMessage2 = MessageProxy.getMessage(); + + auto [begin, end] = makeInputIterators(testMessage1.data(), testMessage2.data(), testMessage1.size(), ShiftFunctor()>{}); + + encodeExternal(begin, end); +}; \ No newline at end of file diff --git a/DataFormats/Detectors/EMCAL/CMakeLists.txt b/DataFormats/Detectors/EMCAL/CMakeLists.txt index 1e7c4f4739a3b..9c93bae30ddf6 100644 --- a/DataFormats/Detectors/EMCAL/CMakeLists.txt +++ b/DataFormats/Detectors/EMCAL/CMakeLists.txt @@ -20,6 +20,9 @@ o2_add_library(DataFormatsEMCAL src/EventHandler.cxx src/CTF.cxx src/ErrorTypeFEE.cxx + src/CellLabel.cxx + src/ClusterLabel.cxx + src/CompressedTriggerData.cxx PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers O2::MathUtils @@ -37,7 +40,9 @@ o2_target_root_dictionary(DataFormatsEMCAL include/DataFormatsEMCAL/EventHandler.h include/DataFormatsEMCAL/MCLabel.h include/DataFormatsEMCAL/CTF.h - include/DataFormatsEMCAL/ErrorTypeFEE.h) + include/DataFormatsEMCAL/ErrorTypeFEE.h + include/DataFormatsEMCAL/CellLabel.h + include/DataFormatsEMCAL/ClusterLabel.h) o2_add_test(Cell SOURCES test/testCell.cxx diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/AnalysisCluster.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/AnalysisCluster.h index 758e0a1fa0b47..e19fd17dea2ce 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/AnalysisCluster.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/AnalysisCluster.h @@ -182,6 +182,9 @@ class AnalysisCluster float getCoreEnergy() const { return mCoreEnergy; } void setCoreEnergy(float energy) { mCoreEnergy = energy; } + float getFCross() const { return mFCross; } + void setFCross(float fCross) { mFCross = fCross; } + /// /// Returns TLorentzVector with momentum of the cluster. Only valid for clusters /// identified as photons or pi0 (overlapped gamma) produced on the vertex @@ -223,12 +226,13 @@ class AnalysisCluster float mTime = 0.; ///< Time of the digit/cell with maximal energy deposition bool mIsExotic = false; //! +#include +#include #include "DataFormatsEMCAL/Constants.h" namespace o2 @@ -27,104 +29,105 @@ namespace emcal /// \since March 6, 2019 /// \ingroup EMCALDataFormat /// -/// # Base format for EMCAL cell information in the Compressed Timeframe +/// # Cell content /// /// The cell class contains the relevant information for each tower per event /// - Tower ID /// - Energy of the raw fit /// - Time of the raw fit /// - Type of the cell +/// +/// # Compression for CTF +/// /// While cell type and tower ID have a predefined range based on the hardware /// design, energy and time have a finite resolution influenced by the resolution /// of the digitizer. This is used in order to compress the information stored /// in the compressed timeframe by not storing the full double values but instead /// assigning a certain amount of bits to each information. Therefore for certain -/// information (energy, time) precision loss has to be taken into account. -/// -/// # Internal structure and resolution -/// -/// The internal structure is a bit field compressing the information to -/// 48 bits. The definition of the bit field as well as the value range and the resolution -/// is listed in the table below: +/// information (energy, time) precision loss has to be taken into account. The number +/// of bits assigned to each data member in the encoding are as follows: /// -/// | Bits | Content | Resolution | Range | -/// |-------|---------------|---------------|-----------------------------| -/// | 0-14 | Tower ID | - | 0 to 17644 | -/// | 15-26 | Time (ns) | 0.73 ns | -600 to 900 ns | -/// | 27-40 | Energy (GeV) | 0.0153 GeV | 0 to 250 GeV | -/// | 41-42 | Cell type | - | 0=LG, 1=HG, 2=LEMon, 4=TRU | +/// | Content | Number of bits |Resolution | Range | +/// |---------------|----------------|--------------|-----------------------------| +/// | Tower ID | 15 | - | 0 to 17644 | +/// | Time (ns) | 11 | 0.73 ns | -600 to 900 ns | +/// | Energy (GeV) | 14 | 0.0153 GeV | 0 to 250 GeV | +/// | Cell type | 2 | - | 0=LG, 1=HG, 2=LEMon, 4=TRU | /// /// The remaining bits are 0 class Cell { public: - Cell(); - Cell(short tower, float energy, float time, ChannelType_t ctype = ChannelType_t::LOW_GAIN); + enum class EncoderVersion { + EncodingV0, + EncodingV1, + EncodingV2 + }; + /// \brief Default constructor + Cell() = default; + + /// \brief Constructor + /// \param tower Tower ID + /// \param energy Energy + /// \param timestamp Cell time + /// \param ctype Channel type + Cell(short tower, float energy, float timestamp, ChannelType_t ctype = ChannelType_t::LOW_GAIN); + + /// \brief Constructor, from encoded bit representation + /// \param tower Tower bitsets + /// \param energy Energy bits + /// \param timestamp Cell time bits + /// \param ctype Channel type bits + /// \param version Encoding version + Cell(uint16_t towerBits, uint16_t energyBits, uint16_t timestampBits, uint16_t channelBits, EncoderVersion version = EncoderVersion::EncodingV1); + + /// \brief Destructor ~Cell() = default; // override - void setTower(short tower) { getDataRepresentation()->mTowerID = tower; } - short getTower() const { return getDataRepresentation()->mTowerID; } + /// \brief Set the tower ID + /// \param tower Tower ID + void setTower(short tower) { mTowerID = tower; } + + /// \brief Get the tower ID + /// \return Tower ID + short getTower() const { return mTowerID; } /// \brief Set the time stamp - /// \param time Time in ns - /// - /// The time stamp is expressed in ns and has - /// a resolution of 1 ns. The time range which can - /// be stored is from -1023 to 1023 ns. In case the - /// range is exceeded the time is set to the limit - /// of the range. - void setTimeStamp(float time); + /// \param timestamp Time in ns + void setTimeStamp(float timestamp) { mTimestamp = timestamp; } /// \brief Get the time stamp /// \return Time in ns - /// - /// Time has a resolution of 1 ns and can cover - /// a range from -1023 to 1023 ns - float getTimeStamp() const; + float getTimeStamp() const { return mTimestamp; } /// \brief Set the energy of the cell /// \brief Energy of the cell in GeV - /// - /// The energy range covered by the cell - /// is 0 - 250 GeV, with a resolution of - /// 0.0153 GeV. In case an energy exceeding - /// the limits is provided the energy is - /// set to the limits (0 in case of negative - /// energy, 250. in case of energies > 250 GeV) - void setEnergy(float energy); + void setEnergy(float energy) { mEnergy = energy; } /// \brief Get the energy of the cell /// \return Energy of the cell - /// - /// The energy is truncated to a range - /// covering 0 to 250 GeV with a resolution - /// of 0.0153 GeV - float getEnergy() const; + float getEnergy() const { return mEnergy; } /// \brief Set the amplitude of the cell /// \param amplitude Cell amplitude - /// - /// See setEnergy for more information void setAmplitude(float amplitude) { setEnergy(amplitude); } /// \brief Get cell amplitude - /// \return cell Amplitude - /// - /// Set getEnergy for more information + /// \return Cell amplitude in GeV float getAmplitude() const { return getEnergy(); } /// \brief Set the type of the cell /// \param ctype Type of the cell (HIGH_GAIN, LOW_GAIN, LEDMON, TRU) - void setType(ChannelType_t ctype) { getDataRepresentation()->mCellStatus = static_cast(ctype); } + void setType(ChannelType_t ctype) { mChannelType = ctype; } /// \brief Get the type of the cell /// \return Type of the cell (HIGH_GAIN, LOW_GAIN, LEDMON, TRU) - ChannelType_t getType() const { return static_cast(getDataRepresentation()->mCellStatus); } + ChannelType_t getType() const { return mChannelType; } /// \brief Check whether the cell is of a given type /// \param ctype Type of the cell (HIGH_GAIN, LOW_GAIN, LEDMON, TRU) /// \return True if the type of the cell matches the requested type, false otherwise - bool isChannelType(ChannelType_t ctype) const { return getType() == ctype; } + bool isChannelType(ChannelType_t ctype) const { return mChannelType == ctype; } /// \brief Mark cell as low gain cell void setLowGain() { setType(ChannelType_t::LOW_GAIN); } @@ -154,38 +157,101 @@ class Cell /// \return True if the cell type is TRU, false otherwise Bool_t getTRU() const { return isChannelType(ChannelType_t::TRU); } + /// \brief Apply compression as done during writing to / reading from CTF + /// \param version Encoder version + void truncate(EncoderVersion version = EncoderVersion::EncodingV1); + void PrintStream(std::ostream& stream) const; - /// used for CTF encoding/decoding: access to packed data - void setPacked(uint16_t tower, uint16_t t, uint16_t en, uint16_t status) + /// \brief Initialize cell class from bit representation (for CTF decoding) + /// \param towerIDBits Encoded tower ID + /// \param timestampBits Encoded timestamp + /// \param energyBits Encoded energy + /// \param celltypeBits Encoded cell type + /// \param version Encoder version + void initialiseFromEncoded(uint16_t towerIDBits, uint16_t timestampBits, uint16_t energyBits, uint16_t celltypeBits, EncoderVersion version = EncoderVersion::EncodingV1) { - auto dt = getDataRepresentation(); - dt->mTowerID = tower; - dt->mTime = t; - dt->mEnergy = en; - dt->mCellStatus = status; + setEnergyEncoded(energyBits, static_cast(celltypeBits), version); + setTimestampEncoded(timestampBits); + setTowerIDEncoded(towerIDBits); + setChannelTypeEncoded(celltypeBits); } - auto getPackedTowerID() const { return getDataRepresentation()->mTowerID; } - auto getPackedTime() const { return getDataRepresentation()->mTime; } - auto getPackedEnergy() const { return getDataRepresentation()->mEnergy; } - auto getPackedCellStatus() const { return getDataRepresentation()->mCellStatus; } + /// \brief Get encoded bit representation of tower ID (for CTF) + /// \return Encoded bit representation + /// + /// Same as getTower - no compression applied for tower ID + uint16_t getTowerIDEncoded() const; + + /// \brief Get encoded bit representation of timestamp (for CTF) + /// \return Encoded bit representation + /// + /// The time stamp is expressed in ns and has + /// a resolution of 1 ns. The time range which can + /// be stored is from -1023 to 1023 ns. In case the + /// range is exceeded the time is set to the limit + /// of the range. + uint16_t getTimeStampEncoded() const; + + /// \brief Get encoded bit representation of energy (for CTF) + /// \param version Encoding verions + /// \return Encoded bit representation + /// + /// The energy range covered by the cell + /// is 0 - 250 GeV, with a resolution of + /// 0.0153 GeV. In case an energy exceeding + /// the limits is provided the energy is + /// set to the limits (0 in case of negative + /// energy, 250. in case of energies > 250 GeV) + uint16_t getEnergyEncoded(EncoderVersion version = EncoderVersion::EncodingV2) const; + + /// \brief Get encoded bit representation of cell type (for CTF) + /// \return Encoded bit representation + uint16_t getCellTypeEncoded() const; + + void initializeFromPackedBitfieldV0(const char* bitfield); + + static float getEnergyFromPackedBitfieldV0(const char* bitfield); + static float getTimeFromPackedBitfieldV0(const char* bitfield); + static ChannelType_t getCellTypeFromPackedBitfieldV0(const char* bitfield); + static short getTowerFromPackedBitfieldV0(const char* bitfield); + + static uint16_t encodeTime(float timestamp); + static uint16_t encodeEnergyV0(float energy); + static uint16_t encodeEnergyV1(float energy, ChannelType_t celltype); + static uint16_t encodeEnergyV2(float energy, ChannelType_t celltype); + static uint16_t V0toV1(uint16_t energybits, ChannelType_t celltype); + static uint16_t V0toV2(uint16_t energybits, ChannelType_t celltype); + static uint16_t V1toV2(uint16_t energybits, ChannelType_t celltype); + static float decodeTime(uint16_t timestampBits); + static float decodeEnergyV0(uint16_t energybits); + static float decodeEnergyV1(uint16_t energybits, ChannelType_t celltype); + static float decodeEnergyV2(uint16_t energybits, ChannelType_t celltype); private: - struct __attribute__((packed)) CellData { - uint16_t mTowerID : 15; ///< bits 0-14 Tower ID - uint16_t mTime : 11; ///< bits 15-25: Time (signed, can become negative after calibration) - uint16_t mEnergy : 14; ///< bits 26-39: Energy - uint16_t mCellStatus : 2; ///< bits 40-41: Cell status - uint16_t mZerod : 6; ///< bits 42-47: Zerod - }; + /// \brief Set cell energy from encoded bit representation (from CTF) + /// \param energyBits Bit representation of energy + /// \param cellTypeBits Bit representation of cell type + void setEnergyEncoded(uint16_t energyBits, uint16_t cellTypeBits, EncoderVersion version = EncoderVersion::EncodingV1); + + /// \brief Set cell time from encoded bit representation (from CTF) + /// \param timestampBits Bit representation of timestamp + void setTimestampEncoded(uint16_t timestampBits); + + /// \brief Set tower ID from encoded bit representation (from CTF) + /// \param towerIDBits Bit representation of towerID + void setTowerIDEncoded(uint16_t towerIDBits); - CellData* getDataRepresentation() { return reinterpret_cast(mCellWords); } - const CellData* getDataRepresentation() const { return reinterpret_cast(mCellWords); } + /// \brief Set cell type from encoded bit representation (from CTF) + /// \param channelTypeBits Bit representation of cell type + void setChannelTypeEncoded(uint16_t channelTypeBits); - uint16_t mCellWords[3]; ///< data word + float mEnergy = FLT_MIN; ///< Energy + float mTimestamp = FLT_MIN; ///< Timestamp + short mTowerID = SHRT_MAX; ///< Tower ID + ChannelType_t mChannelType = ChannelType_t::HIGH_GAIN; ///< Cell type - ClassDefNV(Cell, 1); + ClassDefNV(Cell, 3); }; /// \brief Stream operator for EMCAL cell diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CellLabel.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CellLabel.h new file mode 100644 index 0000000000000..543e49fb06dd8 --- /dev/null +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CellLabel.h @@ -0,0 +1,80 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_EMCAL_CELLLABEL_H_ +#define ALICEO2_EMCAL_CELLLABEL_H_ + +#include +#include +#include +#include + +namespace o2 +{ + +namespace emcal +{ + +/// \class CellLabel +/// \brief cell class for MC particle IDs and their respective amplitude fraction +/// \ingroup EMCALDataFormat +/// \author Marvin Hemmer , Goethe university Frankfurt +/// \since December 13, 2023 +/// + +class CellLabel +{ + public: + // CellLabel() = default; + + /// \brief Constructor using std::vector by moving NOT copying + /// \param labels list of mc labels + /// \param amplitudeFractions list of amplitude fractions + CellLabel(std::vector labels, std::vector amplitudeFractions); + + /// \brief Constructor using gsl::span + /// \param labels list of mc labels + /// \param amplitudeFractions list of amplitude fractions + CellLabel(gsl::span labels, gsl::span amplitudeFractions); + + // ~CellLabel() = default; + // CellLabel(const CellLabel& clus) = default; + // CellLabel& operator=(const CellLabel& source) = default; + + /// \brief Getter of label size + /// \param index index which label to get + size_t GetLabelSize(void) const { return mLabels.size(); } + + /// \brief Getter for label + /// \param index index which label to get + int32_t GetLabel(size_t index) const { return mLabels[index]; } + + /// \brief Getter for labels + std::vector GetLabels() const { return mLabels; } + + /// \brief Getter for amplitude fraction + /// \param index index which amplitude fraction to get + float GetAmplitudeFraction(size_t index) const { return mAmplitudeFraction[index]; } + + /// \brief Getter for amplitude fractions + std::vector GetAmplitudeFractions() const { return mAmplitudeFraction; } + + /// \brief Getter for label with leading amplitude fraction + int32_t GetLeadingMCLabel() const; + + protected: + std::vector mLabels; ///< List of MC particles that generated the cluster, ordered in deposited energy. + std::vector mAmplitudeFraction; ///< List of the fraction of the cell energy coming from a MC particle. Index aligns with mLabels! +}; + +} // namespace emcal +} // namespace o2 +#endif // ALICEO2_EMCAL_CELLLABEL_H_ diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ClusterLabel.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ClusterLabel.h new file mode 100644 index 0000000000000..b6db76f91ff34 --- /dev/null +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ClusterLabel.h @@ -0,0 +1,95 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_EMCAL_CLUSTERLABEL_H_ +#define ALICEO2_EMCAL_CLUSTERLABEL_H_ + +#include +#include +#include +#include "Rtypes.h" + +namespace o2 +{ + +namespace emcal +{ + +/// \class ClusterLabel +/// \brief cluster class for MC particle IDs and their respective energy fraction +/// \ingroup EMCALDataFormat +/// \author Marvin Hemmer , Goethe university Frankfurt +/// \since December 13, 2023 +/// + +class ClusterLabel +{ + public: + /// \struct labelWithE + /// \brief Wrapper structure to make cluster label sortable in energy fraction + struct labelWithE { + + /// \brief Constructor + labelWithE() : label(0), energyFraction(0.) {} + + /// \brief Constructor + /// \param l MC label + /// \param e Energy fraction + labelWithE(int l, float e) : label(l), energyFraction(e) {} + + /// \brief Comparison lower operator comparing cells based on energy + /// + /// std::sort will require operator>= to compile. + /// + /// \param rhs Label to compare to + /// \return True if this cell is has a lower energy, false otherwise + bool operator>=(labelWithE const& rhs) const + { + return energyFraction >= rhs.energyFraction; + } + + float energyFraction; ///< Energy Fraction + int label; ///< MC label + }; + + // ClusterLabel() = default; + // ~ClusterLabel() = default; + // ClusterLabel(const ClusterLabel& clus) = default; + // ClusterLabel& operator=(const ClusterLabel& source) = default; + + /// \brief Clear the member variables + void clear(); + + /// \brief Add label and energy fraction to the + /// \param label MC label + /// \param energyFraction Energy fraction + void addValue(int label, float energyFraction); + + /// \brief Normalize the energy fraction + /// \param factor normalization factor + void normalize(float factor); + + /// \brief Getter for vector of labels + std::vector getLabels(); + + /// \brief Getter for vector of energy fractions + std::vector getEnergyFractions(); + + /// \brief Sort the labels and energy fraction in descending order (largest energy fraction to smallest) + void orderLabels(); + + protected: + std::vector mClusterLabels; ///< List of MC particles that generated the cluster, paired with energy fraction +}; + +} // namespace emcal +} // namespace o2 +#endif // ALICEO2_EMCAL_CLUSTERLABEL_H_ diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CompressedTriggerData.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CompressedTriggerData.h new file mode 100644 index 0000000000000..5fbf2187ab5dd --- /dev/null +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CompressedTriggerData.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_EMCAL_COMPRESSEDTRIGGERDATA_H +#define ALICEO2_EMCAL_COMPRESSEDTRIGGERDATA_H + +#include +#include + +namespace o2::emcal +{ + +/// \struct CompressedTRU +/// \brief Compressed reconstructed TRU information +/// \ingroup EMCALDataFormat +struct CompressedTRU { + uint8_t mTRUIndex; ///< TRU index + uint8_t mTriggerTime; ///< Trigger time of the TRU + bool mFired; ///< Fired status of the TRU + uint8_t mNumberOfPatches; ///< Number of patches found for the TRU +}; + +/// \struct CompressedTriggerPatch +/// \brief Compressed reconstructed L0 trigger patch information +/// \ingroup EMCALDataFormat +struct CompressedTriggerPatch { + uint8_t mTRUIndex; ///< Index of the TRU where the trigger patch has been found + uint8_t mPatchIndexInTRU; ///< Index of the trigger patch in the TRU + uint8_t mTime; ///< Reconstructed time of the trigger patch + uint16_t mADC; ///< ADC sum of the trigger patch +}; + +/// \struct CompressedL0TimeSum +/// \brief Compressed L0 timesum information +/// \ingroup EMCALDataFormat +struct CompressedL0TimeSum { + uint16_t mIndex; ///< Absolute ID of the FastOR + uint16_t mTimesum; ///< ADC value of the time-sum (4-integral) +}; + +/// \brief Output stream operator of the CompressedTRU +/// \param stream Stream to write to +/// \param tru TRU data to be streamed +/// \return Stream after writing +std::ostream& operator<<(std::ostream& stream, const CompressedTRU& tru); + +/// \brief Output stream operator of the CompressedTriggerPatch +/// \param stream Stream to write to +/// \param patch Trigger patch to be streamed +/// \return Stream after writing +std::ostream& operator<<(std::ostream& stream, const CompressedTriggerPatch& patch); + +/// \brief Output stream operator of the CompressedL0TimeSum +/// \param stream Stream to write to +/// \param timesum FastOR L0 timesum to be streamed +/// \return Stream after writing +std::ostream& operator<<(std::ostream& stream, const CompressedL0TimeSum& timesum); + +} // namespace o2::emcal + +#endif // ALICEO2_EMCAL_COMPRESSEDTRIGGERDATA_H \ No newline at end of file diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h index 103a9419b5e08..015c77deccc46 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h @@ -103,6 +103,11 @@ constexpr int MAX_RANGE_ADC = 0x3FF; ///< Dynamic range of the ADCs (1 constexpr double EMCAL_TRU_ADCENERGY = 0.0786; ///< resolution of the TRU digitizer, @TODO check exact value } // namespace constants +namespace triggerbits +{ +constexpr uint32_t Inc = 0x1 << 20; ///< trigger bit marking incomplete event +} + enum FitAlgorithm { Standard = 0, ///< Standard raw fitter Gamma2 = 1, ///< Gamma2 raw fitter @@ -110,6 +115,35 @@ enum FitAlgorithm { NONE = 3 }; +enum STUtype_t { + ESTU = 0, ///< EMCAL STU + DSTU = 1 ///< DCAL STU +}; + +namespace STUparam //[0]->EMCAL STU, [1]->DCAL STU +{ +constexpr int FeeID[2] = {44, 45}; ///< FEE_ID in RDH +constexpr int NTRU[2] = {32, 14}; ///< number of TRUs +constexpr int CFG_nWords[2] = {17, 17}; ///< number of configuration words +constexpr int L1JetIndex_nWords[2] = {11, 11}; ///< number of words with Jet indices +constexpr int L0index_nWords[2] = {96, 42}; ///< number of words with null data +constexpr int L1GammaIndex_nWords[2] = {128, 56}; ///< number of words with Gamma indices +constexpr int Raw_nWords[2] = {1536, 672}; ///< number of words with ADC +constexpr int SubregionsEta[2] = {12, 12}; ///< number of subregions over eta +constexpr int SubregionsPhi[2] = {16, 10}; ///< number of subregions over phi +constexpr int PaloadSizeFull[2] = {1928, 866}; ///< number of words in full payload = 1944/882-16 +constexpr int PaloadSizeShort[2] = {391, 193}; ///< number of words in shorts payload = 407/209-16 +} // namespace STUparam + +namespace TRUparam +{ +constexpr int Nchannels = 96; ///< number of FastORs per TRU +constexpr int NchannelsOverEta = 8; ///< number of FastORs over Eta for full- and 2/3-size SMs +constexpr int NchannelsOverPhi = 12; ///< number of FastORs over Phi for full- and 2/3-size SMs +constexpr int NchannelsOverEta_long = 24; ///< number of FastORs over Eta for 1/3-size SMs +constexpr int NchannelsOverPhi_long = 4; ///< number of FastORs over Phi for 1/3-size SMs +} // namespace TRUparam + } // namespace emcal } // namespace o2 #endif diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h deleted file mode 100644 index 3c014d37e6f9e..0000000000000 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file EMCALChannelData.h -/// \brief - -/// \class EMCALChannelCalibrator -/// \brief Class to store the data format for calibraton of the EMCal -/// \author Hannah Bossi, Yale University -/// \ingroup DetectorEMCAL -/// \since Feb 11, 2021 - -#ifndef ALICEO2_EMCALCHANNELDATA_H -#define ALICEO2_EMCALCHANNELDATA_H - -#include "Rtypes.h" - -namespace o2 -{ -namespace dataformats -{ -class EMCALChannelData -{ - public: - EMCALChannelData(int cellID, int timestamp, int flags = 0, int events) : mEMCALCellID(cellID), mTimestamp(timestamp), mFlags(flags){}; - EMCALChannelData() = default; - ~EMCALChannelData() = default; - - void setEMCALCellID(int index) { mEMCALCellID = index; } - int getEMCALCellID() const { return mEMCALCellID; } - - void setTimestamp(int ts) { mTimestamp = ts; } - int getTimestamp() const { return mTimestamp; } - - void setFlags(int flags) { mFlags = flags; } - float getFlags() const { return mFlags; } - - private: - int mEMCALCellID; ///< EMCal Cell ID - int mTimestamp; ///< timestamp in seconds - unsigned char mFlags; ///< bit mask with quality flags (to be defined) - - ClassDefNV(EMCALChannelData, 1); -}; -} // namespace dataformats -} // namespace o2 -#endif diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ErrorTypeFEE.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ErrorTypeFEE.h index 54f0d3be5f2a8..cc3540cd388d3 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ErrorTypeFEE.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ErrorTypeFEE.h @@ -57,6 +57,9 @@ class ErrorTypeFEE FIT_ERROR, ///< Raw fit failed GEOMETRY_ERROR, ///< Decoded position outside EMCAL GAIN_ERROR, ///< Error due to gain type + LINK_ERROR, ///< Error due to missing DDL links + TRU_ERROR, ///< Errors from TRU data + STU_ERROR, ///< Error from STU data UNDEFINED ///< Error source undefined }; /// \brief Constructor @@ -97,6 +100,18 @@ class ErrorTypeFEE /// \param gainError Error code of the gain type error void setGainErrorType(int gainError) { setError(ErrorSource_t::GAIN_ERROR, gainError); } + /// \brief Set the error type as link error and store the error code + /// \param linkError Error code of the link error + void setLinkErrorTYpe(int linkError) { setError(ErrorSource_t::LINK_ERROR, linkError); } + + /// \brief Set the error as STU decoder error and store the error code + /// \param gainError Error code of the STU decoder error + void setSTUDecoderErrorType(int stuerror) { setError(ErrorSource_t::STU_ERROR, stuerror); } + + /// \brief Set the error as TRU decoder error and store the error code + /// \param gainError Error code of the TRU decoder error + void setTRUDecoderErrorType(int truerror) { setError(ErrorSource_t::TRU_ERROR, truerror); } + /// \brief Set the error type of the object /// \param errorsource Error type of the object void setErrorType(ErrorSource_t errorsource) { mErrorSource = errorsource; } @@ -157,6 +172,18 @@ class ErrorTypeFEE /// \return Error code (-1 in case the object is not a gain type error) int getGainTypeErrorType() const { return getRawErrorForType(ErrorSource_t::GAIN_ERROR); } + /// \brief Get the error code of the obect in case the object is a link error + /// \return Error code (-1 in case the object is not a gain type error) + int getLinkErrorType() const { return getRawErrorForType(ErrorSource_t::LINK_ERROR); } + + /// \brief Get the error code of the obect in case the object is a STU decoder error + /// \return Error code (-1 in case the object is not a STU decoder error) + int getSTUDecoderErrorType() const { return getRawErrorForType(ErrorSource_t::STU_ERROR); } + + /// \brief Get the error code of the obect in case the object is a TRU decoder error + /// \return Error code (-1 in case the object is not a STU decoder error) + int getTRUDecoderErrorType() const { return getRawErrorForType(ErrorSource_t::TRU_ERROR); } + /// \brief Get subspecification of the error /// \return Subspecification of the error int getSubspecification() const { return mSubspecification; } @@ -173,7 +200,7 @@ class ErrorTypeFEE /// \brief Get the number of error types /// \return Number of error types (including undefined) - static constexpr int getNumberOfErrorTypes() { return 7; } + static constexpr int getNumberOfErrorTypes() { return 10; } /// \brief Get the name of the error type /// \param errorTypeID ID of the error type diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventData.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventData.h index ae4fe9f350e76..90c56e7a354d9 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventData.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventData.h @@ -12,9 +12,11 @@ #define ALICEO2_EMCAL_EVENTDATA_H_ #include #include +#include #include "CommonDataFormat/InteractionRecord.h" #include "DataFormatsEMCAL/Cell.h" #include "DataFormatsEMCAL/Cluster.h" +#include "DataFormatsEMCAL/MCLabel.h" namespace o2 { @@ -34,11 +36,12 @@ namespace emcal /// objects are not filled when creating the event structure. template struct EventData { - InteractionRecord mInteractionRecord; ///< Interaction record for the trigger corresponding to this event - gsl::span mClusters; ///< EMCAL clusters - gsl::span mCells; ///< EMCAL cells / digits - gsl::span mCellIndices; ///< Cell indices in cluster - uint64_t mTriggerBits; ///< Trigger bits for the event + InteractionRecord mInteractionRecord; ///< Interaction record for the trigger corresponding to this event + gsl::span mClusters; ///< EMCAL clusters + gsl::span mCells; ///< EMCAL cells / digits + gsl::span mCellIndices; ///< Cell indices in cluster + std::vector> mMCCellLabels; ///< span of MC labels for each cell + uint64_t mTriggerBits; ///< Trigger bits for the event /// \brief Reset event structure with empty interaction record and ranges void reset() @@ -47,6 +50,7 @@ struct EventData { mClusters = gsl::span(); mCells = gsl::span(); mCellIndices = gsl::span(); + mMCCellLabels = std::vector>(); mTriggerBits = 0; } @@ -57,4 +61,4 @@ struct EventData { } // namespace o2 -#endif // ALICEO2_EMCAL_EVENTDATA_H_ \ No newline at end of file +#endif // ALICEO2_EMCAL_EVENTDATA_H_ diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventHandler.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventHandler.h index 5bdd06c916b73..326a256d7a802 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventHandler.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EventHandler.h @@ -15,13 +15,16 @@ #include #include #include +#include #include "Rtypes.h" #include "fmt/format.h" #include "DataFormatsEMCAL/Cell.h" #include "DataFormatsEMCAL/Cluster.h" #include "DataFormatsEMCAL/Digit.h" #include "DataFormatsEMCAL/EventData.h" +#include "DataFormatsEMCAL/MCLabel.h" #include "DataFormatsEMCAL/TriggerRecord.h" +#include "SimulationDataFormat/MCTruthContainer.h" namespace o2 { @@ -363,6 +366,13 @@ class EventHandler /// \throw NotInitializedException in case the event handler is not initialized for cell const CellRange getCellsForEvent(int eventID) const; + /// \brief Get vector of MC labels belonging to the given event + /// \param eventID ID of the event + /// \return vector of MC labels for the event + /// \throw RangeException in case the required event ID exceeds the maximum number of events + /// \throw NotInitializedException in case the event handler is not initialized for cell + std::vector> getCellMCLabelForEvent(int eventID) const; + /// \brief Get range of cluster cell indices belonging to the given event /// \param eventID ID of the event /// \return Cluster cell index range for the event @@ -404,6 +414,13 @@ class EventHandler mTriggerRecordsCells = triggers; } + /// \brief Setting the pointer for the MCTruthContainer for cells + /// \param mclabels Pointer to the MCTruthContainer for cells in timeframe + void setCellMCTruthContainer(const o2::dataformats::MCTruthContainer* mclabels) + { + mCellLabels = mclabels; + } + /// \brief Reset containers with empty ranges void reset(); @@ -433,9 +450,10 @@ class EventHandler TriggerRange mTriggerRecordsCellIndices; ///< trigger record for cluster cell index type TriggerRange mTriggerRecordsCells; ///< Trigger record for cell type - ClusterRange mClusters; /// container for clusters in timeframe - CellIndexRange mClusterCellIndices; /// container for cell indices in timeframe - CellRange mCells; /// container for cells in timeframe + ClusterRange mClusters; /// container for clusters in timeframe + CellIndexRange mClusterCellIndices; /// container for cell indices in timeframe + CellRange mCells; /// container for cells in timeframe + const o2::dataformats::MCTruthContainer* mCellLabels = nullptr; /// pointer to the MCTruthContainer for cells in timeframe ClassDefNV(EventHandler, 1); }; @@ -443,4 +461,4 @@ class EventHandler } // namespace emcal } // namespace o2 -#endif // ALICEO2_EMCAL_EVENTHANDLER_H__ \ No newline at end of file +#endif // ALICEO2_EMCAL_EVENTHANDLER_H__ diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h index a7e1f72ca1baf..1529eff104400 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h @@ -61,8 +61,9 @@ class TriggerRecord /// \enum TriggerBitsCoded_t /// \brief Position of trigger classes in compressed format enum TriggerBitsCoded_t { - PHYSTRIGGER, ///< Physics trigger - CALIBTRIGGER ///< Calib trigger + PHYSTRIGGER, ///< Physics trigger + CALIBTRIGGER, ///< Calib trigger + REJECTINCOMPLETE ///< Rejected as incomplete }; BCData mBCData; /// Bunch crossing data of the trigger DataRange mDataRange; /// Index of the triggering event (event index and first entry in the container) diff --git a/DataFormats/Detectors/EMCAL/src/Cell.cxx b/DataFormats/Detectors/EMCAL/src/Cell.cxx index 4132d99c2a224..261384d53ca2a 100644 --- a/DataFormats/Detectors/EMCAL/src/Cell.cxx +++ b/DataFormats/Detectors/EMCAL/src/Cell.cxx @@ -17,58 +17,334 @@ using namespace o2::emcal; +namespace TimeEncoding +{ const float TIME_SHIFT = 600., TIME_RANGE = 1500., - TIME_RESOLUTION = TIME_RANGE / 2047., - ENERGY_TRUNCATION = 250., - ENERGY_RESOLUTION = ENERGY_TRUNCATION / 16383.; + TIME_RESOLUTION = TIME_RANGE / 2047.; + +} +namespace EnergyEncoding +{ +namespace v0 +{ +const float + ENERGY_TRUNCATION = 250., + ENERGY_RESOLUTION = ENERGY_TRUNCATION / 16383.; +} + +namespace v1 +{ +const float + ENERGY_BITS = static_cast(0x3FFF), + HGLGTRANSITION = o2::emcal::constants::EMCAL_HGLGTRANSITION * o2::emcal::constants::EMCAL_ADCENERGY, + ENERGY_TRUNCATION = 250., + ENERGY_RESOLUTION_LG = (ENERGY_TRUNCATION - HGLGTRANSITION) / ENERGY_BITS, + ENERGY_RESOLUTION_HG = HGLGTRANSITION / ENERGY_BITS, + ENERGY_RESOLUTION_TRU = ENERGY_TRUNCATION / ENERGY_BITS, + ENERGY_RESOLUTION_LEDMON = ENERGY_TRUNCATION / ENERGY_BITS; +} + +namespace v2 +{ +const float + ENERGY_BITS = static_cast(0x3FFF), + SAFETYMARGIN = 0.2, + HGLGTRANSITION = o2::emcal::constants::OVERFLOWCUT * o2::emcal::constants::EMCAL_ADCENERGY, + OFFSET_LG = HGLGTRANSITION - SAFETYMARGIN, + ENERGY_TRUNCATION = 250., + ENERGY_RESOLUTION_LG = (ENERGY_TRUNCATION - OFFSET_LG) / ENERGY_BITS, + ENERGY_RESOLUTION_HG = HGLGTRANSITION / ENERGY_BITS, + ENERGY_RESOLUTION_TRU = ENERGY_TRUNCATION / ENERGY_BITS, + ENERGY_RESOLUTION_LEDMON = ENERGY_TRUNCATION / ENERGY_BITS; + +} +} // namespace EnergyEncoding + +namespace DecodingV0 +{ +struct __attribute__((packed)) CellDataPacked { + uint16_t mTowerID : 15; ///< bits 0-14 Tower ID + uint16_t mTime : 11; ///< bits 15-25: Time (signed, can become negative after calibration) + uint16_t mEnergy : 14; ///< bits 26-39: Energy + uint16_t mCellStatus : 2; ///< bits 40-41: Cell status + uint16_t mZerod : 6; ///< bits 42-47: Zerod +}; +} // namespace DecodingV0 + +Cell::Cell(short tower, float energy, float timestamp, ChannelType_t ctype) : mTowerID(tower), mEnergy(energy), mTimestamp(timestamp), mChannelType(ctype) +{ +} + +Cell::Cell(uint16_t towerBits, uint16_t energyBits, uint16_t timestampBits, uint16_t channelBits, EncoderVersion version) +{ + initialiseFromEncoded(towerBits, timestampBits, energyBits, channelBits, version); +} + +uint16_t Cell::getTowerIDEncoded() const +{ + return mTowerID; +} + +uint16_t Cell::getTimeStampEncoded() const +{ + return encodeTime(mTimestamp); +} -Cell::Cell() +uint16_t Cell::getEnergyEncoded(EncoderVersion version) const { - memset(mCellWords, 0, sizeof(uint16_t) * 3); + uint16_t energyBits = 0; + switch (version) { + case EncoderVersion::EncodingV0: + energyBits = encodeEnergyV0(mEnergy); + break; + + case EncoderVersion::EncodingV1: + energyBits = encodeEnergyV1(mEnergy, mChannelType); + break; + + case EncoderVersion::EncodingV2: + energyBits = encodeEnergyV2(mEnergy, mChannelType); + break; + } + return energyBits; } -Cell::Cell(short tower, float energy, float time, ChannelType_t ctype) +uint16_t Cell::getCellTypeEncoded() const { - memset(mCellWords, 0, sizeof(uint16_t) * 3); - setTower(tower); - setTimeStamp(time); - setEnergy(energy); - setType(ctype); + return static_cast(mChannelType); } -void Cell::setTimeStamp(float timestamp) +void Cell::setEnergyEncoded(uint16_t energyBits, uint16_t channelTypeBits, EncoderVersion version) { - // truncate: - const float TIME_MIN = -1. * TIME_SHIFT, - TIME_MAX = TIME_RANGE - TIME_SHIFT; - if (timestamp < TIME_MIN) { - timestamp = TIME_MIN; - } else if (timestamp > TIME_MAX) { - timestamp = TIME_MAX; + switch (version) { + case EncoderVersion::EncodingV0: + mEnergy = decodeEnergyV0(energyBits); + break; + case EncoderVersion::EncodingV1: + mEnergy = decodeEnergyV1(energyBits, static_cast(channelTypeBits)); + break; + case EncoderVersion::EncodingV2: + mEnergy = decodeEnergyV2(energyBits, static_cast(channelTypeBits)); + break; } - getDataRepresentation()->mTime = static_cast(std::round((timestamp + TIME_SHIFT) / TIME_RESOLUTION)); } -float Cell::getTimeStamp() const +void Cell::setTimestampEncoded(uint16_t timestampBits) +{ + mTimestamp = decodeTime(timestampBits); +} + +void Cell::setTowerIDEncoded(uint16_t towerIDBits) { - return (static_cast(getDataRepresentation()->mTime) * TIME_RESOLUTION) - TIME_SHIFT; + mTowerID = towerIDBits; } -void Cell::setEnergy(float energy) +void Cell::setChannelTypeEncoded(uint16_t channelTypeBits) +{ + mChannelType = static_cast(channelTypeBits); +} + +void Cell::initializeFromPackedBitfieldV0(const char* bitfield) +{ + auto bitrepresentation = reinterpret_cast(bitfield); + mEnergy = decodeEnergyV0(bitrepresentation->mEnergy); + mTimestamp = decodeTime(bitrepresentation->mTime); + mTowerID = bitrepresentation->mTowerID; + mChannelType = static_cast(bitrepresentation->mCellStatus); +} + +float Cell::getEnergyFromPackedBitfieldV0(const char* bitfield) +{ + return decodeEnergyV0(reinterpret_cast(bitfield)->mEnergy); +} + +float Cell::getTimeFromPackedBitfieldV0(const char* bitfield) +{ + return decodeTime(reinterpret_cast(bitfield)->mTime); +} + +ChannelType_t Cell::getCellTypeFromPackedBitfieldV0(const char* bitfield) +{ + return static_cast(reinterpret_cast(bitfield)->mCellStatus); +} + +short Cell::getTowerFromPackedBitfieldV0(const char* bitfield) +{ + return reinterpret_cast(bitfield)->mTowerID; +} + +void Cell::truncate(EncoderVersion version) +{ + setEnergyEncoded(getEnergyEncoded(version), getCellTypeEncoded(), version); + setTimestampEncoded(getTimeStampEncoded()); +} + +uint16_t Cell::encodeTime(float timestamp) +{ + // truncate + auto timestampTruncated = timestamp; + const float TIME_MIN = -1. * TimeEncoding::TIME_SHIFT, + TIME_MAX = TimeEncoding::TIME_RANGE - TimeEncoding::TIME_SHIFT; + if (timestampTruncated < TIME_MIN) { + timestampTruncated = TIME_MIN; + } else if (timestampTruncated > TIME_MAX) { + timestampTruncated = TIME_MAX; + } + return static_cast(std::round((timestampTruncated + TimeEncoding::TIME_SHIFT) / TimeEncoding::TIME_RESOLUTION)); +} + +uint16_t Cell::encodeEnergyV0(float energy) +{ + auto truncatedEnergy = energy; + if (truncatedEnergy < 0.) { + truncatedEnergy = 0.; + } else if (truncatedEnergy > EnergyEncoding::v0::ENERGY_TRUNCATION) { + truncatedEnergy = EnergyEncoding::v0::ENERGY_TRUNCATION; + } + return static_cast(std::round(truncatedEnergy / EnergyEncoding::v0::ENERGY_RESOLUTION)); +} + +uint16_t Cell::encodeEnergyV1(float energy, ChannelType_t celltype) { double truncatedEnergy = energy; if (truncatedEnergy < 0.) { truncatedEnergy = 0.; - } else if (truncatedEnergy > ENERGY_TRUNCATION) { - truncatedEnergy = ENERGY_TRUNCATION; + } else if (truncatedEnergy > EnergyEncoding::v1::ENERGY_TRUNCATION) { + truncatedEnergy = EnergyEncoding::v1::ENERGY_TRUNCATION; } - getDataRepresentation()->mEnergy = static_cast(std::round(truncatedEnergy / ENERGY_RESOLUTION)); + float resolutionApplied = 0., energyOffset = 0.; + switch (celltype) { + case ChannelType_t::HIGH_GAIN: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_HG; + break; + } + case ChannelType_t::LOW_GAIN: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_LG; + energyOffset = EnergyEncoding::v1::HGLGTRANSITION; + break; + } + case ChannelType_t::TRU: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_TRU; + break; + } + case ChannelType_t::LEDMON: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_LEDMON; + break; + } + } + return static_cast(std::round((truncatedEnergy - energyOffset) / resolutionApplied)); +}; + +uint16_t Cell::encodeEnergyV2(float energy, ChannelType_t celltype) +{ + double truncatedEnergy = energy; + if (truncatedEnergy < 0.) { + truncatedEnergy = 0.; + } else if (truncatedEnergy > EnergyEncoding::v2::ENERGY_TRUNCATION) { + truncatedEnergy = EnergyEncoding::v2::ENERGY_TRUNCATION; + } + float resolutionApplied = 0., energyOffset = 0.; + switch (celltype) { + case ChannelType_t::HIGH_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_HG; + break; + } + case ChannelType_t::LOW_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LG; + energyOffset = EnergyEncoding::v2::OFFSET_LG; + break; + } + case ChannelType_t::TRU: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_TRU; + break; + } + case ChannelType_t::LEDMON: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LEDMON; + break; + } + } + return static_cast(std::round((truncatedEnergy - energyOffset) / resolutionApplied)); +}; + +uint16_t Cell::V0toV1(uint16_t energyBits, ChannelType_t celltype) +{ + auto decodedEnergy = decodeEnergyV0(energyBits); + return encodeEnergyV1(decodedEnergy, celltype); +} + +uint16_t Cell::V0toV2(uint16_t energyBits, ChannelType_t celltype) +{ + auto decodedEnergy = decodeEnergyV0(energyBits); + return encodeEnergyV2(decodedEnergy, celltype); +} + +uint16_t Cell::V1toV2(uint16_t energyBits, ChannelType_t celltype) +{ + auto decodedEnergy = decodeEnergyV1(energyBits, celltype); + return encodeEnergyV2(decodedEnergy, celltype); } -float Cell::getEnergy() const +float Cell::decodeTime(uint16_t timestampBits) { - return static_cast(getDataRepresentation()->mEnergy) * ENERGY_RESOLUTION; + return (static_cast(timestampBits) * TimeEncoding::TIME_RESOLUTION) - TimeEncoding::TIME_SHIFT; +} + +float Cell::decodeEnergyV0(uint16_t energyBits) +{ + return static_cast(energyBits) * EnergyEncoding::v0::ENERGY_RESOLUTION; +} + +float Cell::decodeEnergyV1(uint16_t energyBits, ChannelType_t celltype) +{ + float resolutionApplied = 0., + energyOffset = 0.; + switch (celltype) { + case ChannelType_t::HIGH_GAIN: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_HG; + break; + } + case ChannelType_t::LOW_GAIN: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_LG; + energyOffset = EnergyEncoding::v1::HGLGTRANSITION; + break; + } + case ChannelType_t::TRU: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_TRU; + break; + } + case ChannelType_t::LEDMON: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_LEDMON; + break; + } + } + return (static_cast(energyBits) * resolutionApplied) + energyOffset; +} + +float Cell::decodeEnergyV2(uint16_t energyBits, ChannelType_t celltype) +{ + float resolutionApplied = 0., + energyOffset = 0.; + switch (celltype) { + case ChannelType_t::HIGH_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_HG; + break; + } + case ChannelType_t::LOW_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LG; + energyOffset = EnergyEncoding::v2::OFFSET_LG; + break; + } + case ChannelType_t::TRU: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_TRU; + break; + } + case ChannelType_t::LEDMON: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LEDMON; + break; + } + } + return (static_cast(energyBits) * resolutionApplied) + energyOffset; } void Cell::PrintStream(std::ostream& stream) const @@ -76,7 +352,7 @@ void Cell::PrintStream(std::ostream& stream) const stream << "EMCAL Cell: Type " << getType() << ", Energy " << getEnergy() << ", Time " << getTimeStamp() << ", Tower " << getTower(); } -std::ostream& operator<<(std::ostream& stream, const Cell& c) +std::ostream& o2::emcal::operator<<(std::ostream& stream, const Cell& c) { c.PrintStream(stream); return stream; diff --git a/DataFormats/Detectors/EMCAL/src/CellLabel.cxx b/DataFormats/Detectors/EMCAL/src/CellLabel.cxx new file mode 100644 index 0000000000000..70a1a642c5449 --- /dev/null +++ b/DataFormats/Detectors/EMCAL/src/CellLabel.cxx @@ -0,0 +1,50 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CellLabel.cxx + +#include "DataFormatsEMCAL/CellLabel.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include +#include + +using namespace o2::emcal; + +CellLabel::CellLabel(std::vector labels, std::vector amplitudeFractions) : mLabels(std::move(labels)), mAmplitudeFraction(std::move(amplitudeFractions)) +{ + if (labels.size() != amplitudeFractions.size()) { + LOG(error) << "Size of labels " << labels.size() << " does not match size of amplitude fraction " << amplitudeFractions.size() << " !"; + } +} + +CellLabel::CellLabel(gsl::span labels, gsl::span amplitudeFractions) : mLabels(labels.begin(), labels.end()), mAmplitudeFraction(amplitudeFractions.begin(), amplitudeFractions.end()) +{ + if (labels.size() != amplitudeFractions.size()) { + LOG(error) << "Size of labels " << labels.size() << " does not match size of amplitude fraction " << amplitudeFractions.size() << " !"; + } +} + +int32_t CellLabel::GetLeadingMCLabel() const +{ + size_t maxIndex = 0; + float maxFraction = mAmplitudeFraction[0]; + + for (size_t i = 1; i < mAmplitudeFraction.size(); ++i) { + if (mAmplitudeFraction[i] > maxFraction) { + maxFraction = mAmplitudeFraction[i]; + maxIndex = i; + } + } + return mLabels[maxIndex]; +} diff --git a/DataFormats/Detectors/EMCAL/src/ClusterLabel.cxx b/DataFormats/Detectors/EMCAL/src/ClusterLabel.cxx new file mode 100644 index 0000000000000..9ad1f9be5459f --- /dev/null +++ b/DataFormats/Detectors/EMCAL/src/ClusterLabel.cxx @@ -0,0 +1,75 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ClusterLabel.cxx + +#include "DataFormatsEMCAL/ClusterLabel.h" + +using namespace o2::emcal; + +//_______________________________________________________________________ +void ClusterLabel::clear() +{ + mClusterLabels.clear(); +} + +//_______________________________________________________________________ +void ClusterLabel::addValue(int label, float energyFraction) +{ + auto it = std::find_if(mClusterLabels.begin(), mClusterLabels.end(), + [label](const labelWithE& lWE) { return lWE.label == label; }); + + if (it != mClusterLabels.end()) { + // label already exists, accumulate energy fraction + it->energyFraction += energyFraction; + } else { + // label does not exist, add new energy fraction + mClusterLabels.emplace_back(label, energyFraction); + } +} + +//_______________________________________________________________________ +void ClusterLabel::normalize(float factor) +{ + for (auto& clusterlabel : mClusterLabels) { + clusterlabel.energyFraction = clusterlabel.energyFraction / factor; + } +} + +//_______________________________________________________________________ +std::vector ClusterLabel::getLabels() +{ + std::vector vLabels; + vLabels.reserve(mClusterLabels.size()); + for (auto& clusterlabel : mClusterLabels) { + vLabels.push_back(clusterlabel.label); + } + return vLabels; +} + +//_______________________________________________________________________ +std::vector ClusterLabel::getEnergyFractions() +{ + std::vector vEnergyFractions; + vEnergyFractions.reserve(mClusterLabels.size()); + for (auto& clusterlabel : mClusterLabels) { + vEnergyFractions.push_back(clusterlabel.energyFraction); + } + return vEnergyFractions; +} + +//_______________________________________________________________________ +void ClusterLabel::orderLabels() +{ + // Sort the pairs based on values in descending order + std::sort(mClusterLabels.begin(), mClusterLabels.end(), + [](const labelWithE& a, const labelWithE& b) { return a.energyFraction > b.energyFraction; }); +} diff --git a/DataFormats/Detectors/EMCAL/src/CompressedTriggerData.cxx b/DataFormats/Detectors/EMCAL/src/CompressedTriggerData.cxx new file mode 100644 index 0000000000000..e60b58c958d03 --- /dev/null +++ b/DataFormats/Detectors/EMCAL/src/CompressedTriggerData.cxx @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "DataFormatsEMCAL/CompressedTriggerData.h" + +std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::CompressedTRU& tru) +{ + stream << "TRU " << tru.mTRUIndex << ": Fired " << (tru.mFired ? "yes" : "no") << ", time " << (tru.mFired ? std::to_string(static_cast(tru.mTriggerTime)) : "Undefined") << ", number of patches " << tru.mNumberOfPatches; + return stream; +} + +std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::CompressedTriggerPatch& patch) +{ + stream << "Patch " << patch.mPatchIndexInTRU << " in TRU " << patch.mTRUIndex << ": Time " << patch.mTime << ", ADC " << patch.mADC; + return stream; +} + +std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::CompressedL0TimeSum& timesum) +{ + stream << "FastOR " << timesum.mIndex << ": " << timesum.mTimesum << " ADC counts"; + return stream; +} \ No newline at end of file diff --git a/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h b/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h index 464b611d8b6f2..a648dd9fbbdc1 100644 --- a/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h +++ b/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h @@ -24,6 +24,11 @@ #pragma link C++ class o2::emcal::MCLabel + ; #pragma link C++ class o2::emcal::ErrorTypeFEE + ; +#pragma read \ + sourceClass = "o2::emcal::Cell" targetClass = "o2::emcal::Cell" source = "UShort_t mCellWords[3]" version = "[1]" include = "iostream" target = "mEnergy,mTimestamp,mTowerID,mChannelType" code = "{const char * oldwords = reinterpret_cast(onfile.mCellWords); mEnergy=o2::emcal::Cell::getEnergyFromPackedBitfieldV0(oldwords); mTimestamp=o2::emcal::Cell::getTimeFromPackedBitfieldV0(oldwords); mTowerID=o2::emcal::Cell::getTowerFromPackedBitfieldV0(oldwords); mChannelType=o2::emcal::Cell::getCellTypeFromPackedBitfieldV0(oldwords);}" +#pragma read \ + sourceClass = "o2::emcal::Cell" targetClass = "o2::emcal::Cell" source = "char mCellWords[6]" version = "[2]" include = "iostream" target = "mEnergy,mTimestamp,mTowerID,mChannelType" code = "{mEnergy=o2::emcal::Cell::getEnergyFromPackedBitfieldV0(onfile.mCellWords); mTimestamp=o2::emcal::Cell::getTimeFromPackedBitfieldV0(onfile.mCellWords); mTowerID=o2::emcal::Cell::getTowerFromPackedBitfieldV0(onfile.mCellWords); mChannelType=o2::emcal::Cell::getCellTypeFromPackedBitfieldV0(onfile.mCellWords);}" + #pragma link C++ class std::vector < o2::emcal::TriggerRecord> + ; #pragma link C++ class std::vector < o2::emcal::Cell> + ; #pragma link C++ class std::vector < o2::emcal::Digit> + ; @@ -37,8 +42,6 @@ // For channel type in digits and cells #pragma link C++ enum o2::emcal::ChannelType_t + ; -#pragma link C++ class std::vector < o2::emcal::Cluster> + ; - #pragma link C++ class o2::emcal::EventData < o2::emcal::Cell> + ; #pragma link C++ class o2::emcal::EventData < o2::emcal::Digit> + ; #pragma link C++ class o2::emcal::EventHandler < o2::emcal::Cell> + ; diff --git a/DataFormats/Detectors/EMCAL/src/Digit.cxx b/DataFormats/Detectors/EMCAL/src/Digit.cxx index 45ba800790e8f..a88cc652cdd6d 100644 --- a/DataFormats/Detectors/EMCAL/src/Digit.cxx +++ b/DataFormats/Detectors/EMCAL/src/Digit.cxx @@ -86,8 +86,8 @@ Int_t Digit::getAmplitudeADC(ChannelType_t ctype) const case ChannelType_t::TRU: { int ampADC = std::floor(mAmplitudeGeV / constants::EMCAL_TRU_ADCENERGY); // truncate energy in case dynamic range is saturated - if (ampADC >= constants::EMCAL_TRU_ADCENERGY) { - return constants::EMCAL_TRU_ADCENERGY; + if (ampADC >= constants::MAX_RANGE_ADC) { + return constants::MAX_RANGE_ADC; } return ampADC; }; diff --git a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx b/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx deleted file mode 100644 index 8affa29259f7a..0000000000000 --- a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file EMCALChannelData.cxx -/// \brief Class to store the data format for calibraton of the EMCal - -#include "DataFormatsEMCAL/EMCALChannelData.h" - -using namespace o2::dataformats; - -ClassImp(o2::dataformats::EMCALChannelData; diff --git a/DataFormats/Detectors/EMCAL/src/ErrorTypeFEE.cxx b/DataFormats/Detectors/EMCAL/src/ErrorTypeFEE.cxx index df79498bd97e7..21d985b2a832a 100644 --- a/DataFormats/Detectors/EMCAL/src/ErrorTypeFEE.cxx +++ b/DataFormats/Detectors/EMCAL/src/ErrorTypeFEE.cxx @@ -34,9 +34,18 @@ void ErrorTypeFEE::PrintStream(std::ostream& stream) const case ErrorSource_t::GEOMETRY_ERROR: typestring = "geometry error"; break; - case ErrorTypeFEE::GAIN_ERROR: + case ErrorSource_t::GAIN_ERROR: typestring = "gain type error"; break; + case ErrorSource_t::TRU_ERROR: + typestring = "STU decoder error"; + break; + case ErrorSource_t::STU_ERROR: + typestring = "STU decoder error"; + break; + case ErrorSource_t::LINK_ERROR: + typestring = "Link missing error"; + break; case ErrorSource_t::UNDEFINED: typestring = "unknown error"; break; @@ -66,8 +75,14 @@ const char* ErrorTypeFEE::getErrorTypeName(unsigned int errorTypeID) return "Fit"; case ErrorSource_t::GEOMETRY_ERROR: return "Geometry"; - case ErrorTypeFEE::GAIN_ERROR: + case ErrorSource_t::GAIN_ERROR: return "GainType"; + case ErrorSource_t::TRU_ERROR: + return "TRUDecoding"; + case ErrorSource_t::STU_ERROR: + return "STUDecoding"; + case ErrorSource_t::LINK_ERROR: + return "LinkMissing"; case ErrorSource_t::UNDEFINED: return "Undefined"; default: @@ -88,8 +103,14 @@ const char* ErrorTypeFEE::getErrorTypeTitle(unsigned int errorTypeID) return "Fit"; case ErrorSource_t::GEOMETRY_ERROR: return "Geometry"; - case ErrorTypeFEE::GAIN_ERROR: + case ErrorSource_t::GAIN_ERROR: return "Gain"; + case ErrorSource_t::TRU_ERROR: + return "TRU Decoding"; + case ErrorSource_t::STU_ERROR: + return "STU Decoding"; + case ErrorSource_t::LINK_ERROR: + return "Link missing"; case ErrorSource_t::UNDEFINED: return "Unknown"; default: @@ -97,7 +118,7 @@ const char* ErrorTypeFEE::getErrorTypeTitle(unsigned int errorTypeID) }; } -std::ostream& operator<<(std::ostream& stream, const ErrorTypeFEE& error) +std::ostream& o2::emcal::operator<<(std::ostream& stream, const ErrorTypeFEE& error) { error.PrintStream(stream); return stream; diff --git a/DataFormats/Detectors/EMCAL/src/EventHandler.cxx b/DataFormats/Detectors/EMCAL/src/EventHandler.cxx index de4e880d5bf05..79483f18c3635 100644 --- a/DataFormats/Detectors/EMCAL/src/EventHandler.cxx +++ b/DataFormats/Detectors/EMCAL/src/EventHandler.cxx @@ -138,6 +138,23 @@ const typename EventHandler::CellRange EventHandler +std::vector> EventHandler::getCellMCLabelForEvent(int eventID) const +{ + if (mCellLabels && mTriggerRecordsCells.size()) { + if (eventID >= mTriggerRecordsCells.size()) { + throw RangeException(eventID, mTriggerRecordsCells.size()); + } + auto& trgrecord = mTriggerRecordsCells[eventID]; + std::vector> eventlabels(trgrecord.getNumberOfObjects()); + for (int index = 0; index < trgrecord.getNumberOfObjects(); index++) { + eventlabels[index] = mCellLabels->getLabels(trgrecord.getFirstEntry() + index); + } + return eventlabels; + } + throw NotInitializedException(); +} + template const typename EventHandler::CellIndexRange EventHandler::getClusterCellIndicesForEvent(int eventID) const { @@ -160,6 +177,7 @@ void EventHandler::reset() mClusters = ClusterRange(); mClusterCellIndices = CellIndexRange(); mCells = CellRange(); + mCellLabels = nullptr; } template @@ -177,6 +195,9 @@ EventData EventHandler::buildEvent(int eventID) co if (hasCells()) { outputEvent.mCells = getCellsForEvent(eventID); } + if (mCellLabels) { + outputEvent.mMCCellLabels = getCellMCLabelForEvent(eventID); + } return outputEvent; } @@ -243,4 +264,4 @@ typename EventHandler::EventIterator EventHandler: } template class o2::emcal::EventHandler; -template class o2::emcal::EventHandler; \ No newline at end of file +template class o2::emcal::EventHandler; diff --git a/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx b/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx index f380b944f807d..7ca716dbb4ea2 100644 --- a/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx +++ b/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx @@ -13,6 +13,7 @@ #include #include "DataFormatsEMCAL/TriggerRecord.h" #include "CommonConstants/Triggers.h" +#include "DataFormatsEMCAL/Constants.h" namespace o2 { @@ -29,6 +30,9 @@ uint16_t TriggerRecord::getTriggerBitsCompressed() const if (mTriggerBits & o2::trigger::Cal) { result |= 1 << TriggerBitsCoded_t::CALIBTRIGGER; } + if (mTriggerBits & o2::emcal::triggerbits::Inc) { + result |= 1 << TriggerBitsCoded_t::REJECTINCOMPLETE; + } return result; } @@ -41,6 +45,9 @@ void TriggerRecord::setTriggerBitsCompressed(uint16_t triggerbits) if (triggerbits & (1 << TriggerBitsCoded_t::CALIBTRIGGER)) { mTriggerBits |= o2::trigger::Cal; } + if (triggerbits & (1 << TriggerBitsCoded_t::REJECTINCOMPLETE)) { + mTriggerBits |= o2::emcal::triggerbits::Inc; + } } void TriggerRecord::printStream(std::ostream& stream) const diff --git a/DataFormats/Detectors/FIT/FDD/CMakeLists.txt b/DataFormats/Detectors/FIT/FDD/CMakeLists.txt index fff07003f2700..140ba1165bff8 100644 --- a/DataFormats/Detectors/FIT/FDD/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FDD/CMakeLists.txt @@ -11,11 +11,14 @@ o2_add_library(DataFormatsFDD SOURCES src/RawEventData.cxx + src/RecPoint.cxx src/CTF.cxx + src/LookUpTable.cxx PUBLIC_LINK_LIBRARIES O2::FDDBase O2::DataFormatsFIT O2::SimulationDataFormat O2::CommonDataFormat + O2::DetectorsCommonDataFormats ) o2_target_root_dictionary(DataFormatsFDD @@ -27,3 +30,4 @@ o2_target_root_dictionary(DataFormatsFDD include/DataFormatsFDD/RawEventData.h include/DataFormatsFDD/LookUpTable.h include/DataFormatsFDD/CTF.h) + diff --git a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/CTF.h b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/CTF.h index 3c1df8d89ee4d..594f924e16598 100644 --- a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/CTF.h +++ b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/CTF.h @@ -41,8 +41,8 @@ struct CompressedDigits { // BC data std::vector trigger; // trigger bits - std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc - std::vector orbitInc; // increment in orbit + std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc + std::vector orbitInc; // increment in orbit std::vector nChan; // number of fired channels // channel data diff --git a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h index dbeed65576772..ceac8e26fec7f 100644 --- a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h +++ b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h @@ -9,11 +9,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// file RawEventData.h class for RAW data format -// Alla.Maevskaya -// simple look-up table just to feed digits 2 raw procedure. -// Will be really set after module/electronics connections -// #ifndef ALICEO2_FDD_LOOKUPTABLE_H_ #define ALICEO2_FDD_LOOKUPTABLE_H_ //////////////////////////////////////////////// @@ -21,63 +16,12 @@ ////////////////////////////////////////////// #include "DataFormatsFIT/LookUpTable.h" -#include -#include -#include -#include // std::setfill, std::setw - for stream formating -#include -#include "FDDBase/Constants.h" -#include "CommonUtils/NameConf.h" - +#include "DetectorsCommonDataFormats/DetID.h" namespace o2 { namespace fdd { -namespace new_lut -{ -// Singleton for LookUpTable -template -class SingleLUT : public LUT -{ - private: - SingleLUT() = default; - SingleLUT(const std::string& ccdbPath, const std::string& ccdbPathToLUT) : LUT(ccdbPath, ccdbPathToLUT) {} - SingleLUT(const std::string& pathToFile) : LUT(pathToFile) {} - SingleLUT(const SingleLUT&) = delete; - SingleLUT& operator=(SingleLUT&) = delete; - - public: - static constexpr char sDetectorName[] = "FDD"; - static constexpr char sDefaultLUTpath[] = "FDD/Config/LookupTable"; - static constexpr char sObjectName[] = "LookupTable"; - inline static std::string sCurrentCCDBpath = ""; - inline static std::string sCurrentLUTpath = sDefaultLUTpath; - // Before instance() call, setup url and path - static void setCCDBurl(const std::string& url) { sCurrentCCDBpath = url; } - static void setLUTpath(const std::string& path) { sCurrentLUTpath = path; } - - using Table_t = typename LUT::Table_t; - bool mFirstUpdate{true}; // option in case if LUT should be updated during workflow initialization - static SingleLUT& Instance(const Table_t* table = nullptr) - { - if (sCurrentCCDBpath == "") { - sCurrentCCDBpath = o2::base::NameConf::getCCDBServer(); - } - static SingleLUT instanceLUT; - if (table != nullptr) { - instanceLUT.initFromTable(table); - instanceLUT.mFirstUpdate = false; - } else if (instanceLUT.mFirstUpdate) { - instanceLUT.initCCDB(sCurrentCCDBpath, sCurrentLUTpath); - instanceLUT.mFirstUpdate = false; - } - return instanceLUT; - } -}; -} // namespace new_lut - -using SingleLUT = new_lut::SingleLUT>; - +using SingleLUT = o2::fit::SingleLUT>; } // namespace fdd } // namespace o2 #endif diff --git a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RecPoint.h b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RecPoint.h index 6615dc322180b..f784d99145728 100644 --- a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RecPoint.h +++ b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RecPoint.h @@ -9,8 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file RecPoint.h +/// \file RecPoint.h /// \brief Definition of the FDD RecPoint class + #ifndef ALICEO2_FDD_RECPOINT_H #define ALICEO2_FDD_RECPOINT_H @@ -42,6 +43,7 @@ struct ChannelDataFloat { } void print() const; + bool operator==(const ChannelDataFloat&) const = default; ClassDefNV(ChannelDataFloat, 1); }; @@ -80,6 +82,9 @@ class RecPoint int getFirstEntry() const { return mRef.getFirstEntry(); } int getEntriesInCurrentBC() const { return mRef.getEntries(); } + void print() const; + bool operator==(const RecPoint&) const = default; + private: o2::dataformats::RangeReference mRef; o2::InteractionRecord mIntRecord; diff --git a/DataFormats/Detectors/FIT/FDD/src/LookUpTable.cxx b/DataFormats/Detectors/FIT/FDD/src/LookUpTable.cxx new file mode 100644 index 0000000000000..7965b73d6aa95 --- /dev/null +++ b/DataFormats/Detectors/FIT/FDD/src/LookUpTable.cxx @@ -0,0 +1,15 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFDD/LookUpTable.h" + +using namespace o2::fdd; +template class o2::fit::SingleLUT>; diff --git a/DataFormats/Detectors/FIT/FDD/src/RecPoint.cxx b/DataFormats/Detectors/FIT/FDD/src/RecPoint.cxx new file mode 100644 index 0000000000000..854a09088a2f4 --- /dev/null +++ b/DataFormats/Detectors/FIT/FDD/src/RecPoint.cxx @@ -0,0 +1,33 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RecPoint.cxx +/// \brief Implementation of the FDD RecPoint class +/// \author Andreas Molander andreas.molander@cern.ch + +#include "DataFormatsFDD/RecPoint.h" +#include "Framework/Logger.h" + +using namespace o2::fdd; + +void ChannelDataFloat::print() const +{ + LOG(info) << "ChannelDataFloat data:"; + LOG(info) << "Channel ID: " << mPMNumber << ", Time (ps): " << mTime << ", Charge (ADC): " << mChargeADC << ", QTC chain: " << adcId; +} + +void RecPoint::print() const +{ + LOG(info) << "RecPoint data:"; + LOG(info) << "Collision times: A: " << getCollisionTimeA() << ", C: " << getCollisionTimeC(); + LOG(info) << "Ref first: " << mRef.getFirstEntry() << ", Ref entries: " << mRef.getEntries(); + LOG(info) << "Triggers: " << mTriggers.print(); +} diff --git a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt index 14136966509c5..f7d6a111f4348 100644 --- a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt @@ -17,12 +17,16 @@ o2_add_library(DataFormatsFT0 src/RecPoints.cxx src/RawEventData.cxx src/CTF.cxx + src/LookUpTable.cxx + src/SlewingCoef.cxx + PUBLIC_LINK_LIBRARIES O2::FT0Base O2::DataFormatsFIT O2::SimulationDataFormat O2::CommonDataFormat O2::Headers O2::CCDB + O2::DetectorsCommonDataFormats ) o2_target_root_dictionary(DataFormatsFT0 @@ -42,4 +46,6 @@ o2_target_root_dictionary(DataFormatsFT0 include/DataFormatsFT0/GlobalOffsetsInfoObject.h include/DataFormatsFT0/GlobalOffsetsCalibrationObject.h include/DataFormatsFT0/SpectraInfoObject.h + include/DataFormatsFT0/SlewingCoef.h + include/DataFormatsFT0/EventsPerBc.h ) diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h index fdc073edfd5c1..3ddea05158577 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h @@ -41,8 +41,8 @@ struct CompressedDigits { // trigger data std::vector trigger; // trigger bits - std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc - std::vector orbitInc; // increment in orbit + std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc + std::vector orbitInc; // increment in orbit std::vector nChan; // number of fired channels std::vector eventStatus; // special flags about event conditions: pile-up, not use for collision time, not use for event plane, etc. diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h index 074caf78f66b3..dfe41525af480 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h @@ -13,8 +13,8 @@ /// \brief Class to describe fired and stored channels for the BC and to refer to channel data /// \author Alla.Maevskaya@cern.ch -#ifndef _FT0_CHANNELDATA_H_ -#define _FT0_CHANNELDATA_H_ +#ifndef O2_FT0_CHANNELDATA_H_ +#define O2_FT0_CHANNELDATA_H_ #include #include @@ -76,8 +76,8 @@ struct ChannelData { void print() const; void printLog() const; [[nodiscard]] uint8_t getChannelID() const { return ChId; } - [[nodiscard]] uint16_t getTime() const { return CFDTime; } - [[nodiscard]] uint16_t getAmp() const { return QTCAmpl; } + [[nodiscard]] int16_t getTime() const { return CFDTime; } + [[nodiscard]] int16_t getAmp() const { return QTCAmpl; } bool operator==(ChannelData const& other) const { diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h index 2b7758eeac1f5..c4051fd5b1a7d 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h @@ -50,6 +50,7 @@ struct DetTrigInput { (isSCnt << Triggers::bitSCen)) { } + bool isVertex() const { return mInputs.test(Triggers::bitVertex); } ClassDefNV(DetTrigInput, 1); }; diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/DigitFilterParam.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/DigitFilterParam.h index 11ff50dfeb86b..a00abc013c27d 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/DigitFilterParam.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/DigitFilterParam.h @@ -27,9 +27,9 @@ struct DigitFilterParam : o2::conf::ConfigurableParamHelper { uint8_t mPMbitsGood = (1 << ChannelData::EEventDataBit::kIsCFDinADCgate) | (1 << ChannelData::EEventDataBit::kIsEventInTVDC); uint8_t mPMbitsBad = (1 << ChannelData::EEventDataBit::kIsDoubleEvent) | (1 << ChannelData::EEventDataBit::kIsTimeInfoNOTvalid) | (1 << ChannelData::EEventDataBit::kIsTimeInfoLate) | (1 << ChannelData::EEventDataBit::kIsAmpHigh) | (1 << ChannelData::EEventDataBit::kIsTimeInfoLost); uint8_t mPMbitsToCheck = mPMbitsGood | mPMbitsBad; - uint8_t mTrgBitsGood = (1 << Triggers::bitVertex) | (1 << Triggers::bitDataIsValid); - uint8_t mTrgBitsBad = (1 << Triggers::bitOutputsAreBlocked); - uint8_t mTrgBitsToCheck = mTrgBitsGood | mTrgBitsBad; + uint64_t mTrgBitsGood = Triggers::word(Triggers::bitVertex, Triggers::bitDataIsValid); + uint64_t mTrgBitsBad = Triggers::word(Triggers::bitOutputsAreBlocked); + uint64_t mTrgBitsToCheck = mTrgBitsGood | mTrgBitsBad; O2ParamDef(DigitFilterParam, "FT0DigitFilterParam"); }; @@ -41,12 +41,13 @@ struct ChannelFilterParam : o2::conf::ConfigurableParamHelper +#include +#include + +namespace o2::ft0 +{ +struct EventsPerBc { + std::array histogram; + + std::unique_ptr toTH1F(const char* name = "eventsPerBc") const + { + constexpr int N = o2::constants::lhc::LHCMaxBunches; + auto h = std::make_unique(name, name, N, 0, N); + for (int i = 0; i < N; ++i) { + h->SetBinContent(i + 1, histogram[i]); + } + return h; + } + + ClassDefNV(EventsPerBc, 1); +}; +} // namespace o2::ft0 +#endif \ No newline at end of file diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h index 3f1d98d29197c..1d31b079c2ac0 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h @@ -9,85 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// file RawEventData.h class for RAW data format -// Alla.Maevskaya -// simple look-up table just to feed digits 2 raw procedure. -// Will be really set after module/electronics connections -// #ifndef ALICEO2_FT0_LOOKUPTABLE_H_ #define ALICEO2_FT0_LOOKUPTABLE_H_ //////////////////////////////////////////////// // Look Up Table FT0 ////////////////////////////////////////////// - -#include "CCDB/BasicCCDBManager.h" -#include "FT0Base/Constants.h" #include "DataFormatsFIT/LookUpTable.h" -#include "CommonUtils/NameConf.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "DetectorsCommonDataFormats/DetID.h" namespace o2 { namespace ft0 { - -namespace new_lut -{ -// Singleton for LookUpTable -template -class SingleLUT : public LUT -{ - private: - SingleLUT() = default; - SingleLUT(const std::string& ccdbPath, const std::string& ccdbPathToLUT) : LUT(ccdbPath, ccdbPathToLUT) {} - SingleLUT(const std::string& pathToFile) : LUT(pathToFile) {} - SingleLUT(const SingleLUT&) = delete; - SingleLUT& operator=(SingleLUT&) = delete; - - public: - static constexpr char sDetectorName[] = "FT0"; - static constexpr char sDefaultLUTpath[] = "FT0/Config/LookupTable"; - static constexpr char sObjectName[] = "LookupTable"; - inline static std::string sCurrentCCDBpath = ""; - inline static std::string sCurrentLUTpath = sDefaultLUTpath; - // Before instance() call, setup url and path - static void setCCDBurl(const std::string& url) { sCurrentCCDBpath = url; } - static void setLUTpath(const std::string& path) { sCurrentLUTpath = path; } - - using Table_t = typename LUT::Table_t; - bool mFirstUpdate{true}; // option in case if LUT should be updated during workflow initialization - static SingleLUT& Instance(const Table_t* table = nullptr) - { - if (sCurrentCCDBpath == "") { - sCurrentCCDBpath = o2::base::NameConf::getCCDBServer(); - } - static SingleLUT instanceLUT; - if (table != nullptr) { - instanceLUT.initFromTable(table); - instanceLUT.mFirstUpdate = false; - } else if (instanceLUT.mFirstUpdate) { - instanceLUT.initCCDB(sCurrentCCDBpath, sCurrentLUTpath); - instanceLUT.mFirstUpdate = false; - } - return instanceLUT; - } -}; -} // namespace new_lut - -using SingleLUT = new_lut::SingleLUT>; - +using SingleLUT = o2::fit::SingleLUT>; } // namespace ft0 } // namespace o2 #endif diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RecPoints.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RecPoints.h index 1178cc20a4da0..0503e4f39948f 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RecPoints.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RecPoints.h @@ -24,7 +24,9 @@ #include "Rtypes.h" #include #include - +#include +#include +#include namespace o2 { namespace ft0 @@ -32,10 +34,10 @@ namespace ft0 struct ChannelDataFloat { - int ChId = -1; //channel Id - int ChainQTC = -1; //QTC chain - float CFDTime = -20000; //time in ps, 0 at the LHC clk center - float QTCAmpl = -20000; // Amplitude mV + int ChId = -1; // channel Id + int ChainQTC = -1; // QTC chain + float CFDTime = -20000; // time in ps, 0 at the LHC clk center + float QTCAmpl = -20000; // Amplitude mV ChannelDataFloat() = default; ChannelDataFloat(int iPmt, float time, float charge, int chainQTC) @@ -47,6 +49,7 @@ struct ChannelDataFloat { } void print() const; + bool operator==(const ChannelDataFloat&) const = default; ClassDefNV(ChannelDataFloat, 1); }; @@ -55,10 +58,39 @@ class RecPoints { public: - enum : int { TimeMean, - TimeA, - TimeC, - Vertex }; + enum ETimeType { kTimeMean, + kTimeA, + kTimeC, + kTimeVertex }; + + // Enum for trigger nits specified in rec-points and AOD data + enum ETriggerBits { kOrA = 0, // OrA time-trigger signal + kOrC = 1, // OrC time-trigger signal + kSemiCentral = 2, // Semi-central amplitude-trigger signal + kCentral = 3, // Central amplitude-trigger signal + kVertex = 4, // Vertex time-trigger signal + kIsActiveSideA = 5, // Side-A has at least one channel active + kIsActiveSideC = 6, // Side-C has at least one channel active + kIsFlangeEvent = 7 // Flange event at Side-C, at least one channel has time which corresponds to -82 cm area + }; + static const inline std::map sMapTriggerBits = { + {ETriggerBits::kOrA, "OrA"}, + {ETriggerBits::kOrC, "OrC"}, + {ETriggerBits::kSemiCentral, "Semicentral"}, + {ETriggerBits::kCentral, "Central"}, + {ETriggerBits::kVertex, "Vertex"}, + {ETriggerBits::kIsActiveSideA, "IsActiveSideA"}, + {ETriggerBits::kIsActiveSideC, "IsActiveSideC"}, + {ETriggerBits::kIsFlangeEvent, "IsFlangeEvent"}}; + + enum ETechnicalBits { kLaser = 0, // indicates the laser was triggered in this BC + kOutputsAreBlocked = 1, // indicates that laser-induced pulses should arrive from detector to FEE in this BC (and trigger outputs are blocked) + kDataIsValid = 2, // data is valid for processing + }; + static const inline std::map sMapTechnicalBits = { + {ETechnicalBits::kLaser, "Laser"}, + {ETechnicalBits::kOutputsAreBlocked, "OutputsAreBlocked"}, + {ETechnicalBits::kDataIsValid, "DataIsValid"}}; o2::dataformats::RangeReference ref; o2::InteractionRecord mIntRecord; // Interaction record (orbit, bc) @@ -72,38 +104,63 @@ class RecPoints mIntRecord = iRec; mTriggers = chTrig; } - ~RecPoints() = default; + RecPoints(int chDataFirstEntryPos, + int chDataNEntries, + const o2::InteractionRecord& ir, + const std::array& arrTimes, + const o2::fit::Triggers& digitTriggers, + uint8_t extraTriggerWord) : mIntRecord(ir), mCollisionTime(arrTimes), mTriggers(digitTriggers) + { + ref.setFirstEntry(chDataFirstEntryPos); + ref.setEntries(chDataNEntries); + initRecPointTriggers(digitTriggers, extraTriggerWord); + } - void print() const; + ~RecPoints() = default; short getCollisionTime(int side) const { return mCollisionTime[side]; } - short getCollisionTimeMean() const { return getCollisionTime(TimeMean); } - short getCollisionTimeA() const { return getCollisionTime(TimeA); } - short getCollisionTimeC() const { return getCollisionTime(TimeC); } + short getCollisionTimeMean() const { return getCollisionTime(kTimeMean); } + short getCollisionTimeA() const { return getCollisionTime(kTimeA); } + short getCollisionTimeC() const { return getCollisionTime(kTimeC); } bool isValidTime(int side) const { return getCollisionTime(side) < o2::InteractionRecord::DummyTime; } void setCollisionTime(short time, int side) { mCollisionTime[side] = time; } - short getVertex() const { return getCollisionTime(Vertex); } - void setVertex(short vertex) { mCollisionTime[Vertex] = vertex; } + short getVertex() const { return getCollisionTime(kTimeVertex); } + void setVertex(short vertex) { mCollisionTime[kTimeVertex] = vertex; } o2::fit::Triggers getTrigger() const { return mTriggers; } void setTriggers(o2::fit::Triggers trig) { mTriggers = trig; } + uint8_t getTechnicalWord() const { return mTechnicalWord; } + static constexpr uint8_t makeExtraTrgWord(bool isActiveA = true, bool isActiveC = true, bool isFlangeEvent = true) + { + return (static_cast(isActiveA) << kIsActiveSideA) | + (static_cast(isActiveC) << kIsActiveSideC) | + (static_cast(isFlangeEvent) << kIsFlangeEvent); + } o2::InteractionRecord getInteractionRecord() const { return mIntRecord; }; - - // void SetMgrEventTime(Double_t time) { mTimeStamp = time; } - gsl::span getBunchChannelData(const gsl::span tfdata) const; short static constexpr sDummyCollissionTime = 32767; + void print() const; + bool operator==(const RecPoints&) const = default; + private: + void initRecPointTriggers(const o2::fit::Triggers& digitTriggers, uint8_t extraTrgWord = 0) + { + const auto digitTriggerWord = digitTriggers.getTriggersignals(); + const auto trgAndTechWordPair = o2::fit::Triggers::parseDigitTriggerWord(digitTriggerWord, true); + mTriggers.setTriggers(trgAndTechWordPair.first | extraTrgWord); + mTechnicalWord = trgAndTechWordPair.second; + } + std::array mCollisionTime = {sDummyCollissionTime, sDummyCollissionTime, sDummyCollissionTime, sDummyCollissionTime}; o2::fit::Triggers mTriggers; // pattern of triggers in this BC - - ClassDefNV(RecPoints, 3); + uint8_t mTechnicalWord{0}; // field for keeping ETechnicalBits + ClassDefNV(RecPoints, 4); }; } // namespace ft0 } // namespace o2 diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/SlewingCoef.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/SlewingCoef.h new file mode 100644 index 0000000000000..704f4e3fd2324 --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/SlewingCoef.h @@ -0,0 +1,47 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_FT0_SLEWINGCOEF_H_ +#define ALICEO2_FT0_SLEWINGCOEF_H_ +//////////////////////////////////////////////// +// Slewing coefficients for FT0 +////////////////////////////////////////////// +#include "TGraph.h" +#include "Rtypes.h" + +#include +#include +#include + +namespace o2 +{ +namespace ft0 +{ + +struct SlewingCoef { + constexpr static int sNCHANNELS = 208; + constexpr static int sNAdc = 2; + using VecPoints_t = std::vector; // Set of points + using VecPlot_t = std::pair; // Plot as pair of two set of points + using VecSlewingCoefs_t = std::array, sNAdc>; // 0 - adc0, 1 - adc1 + typedef std::array, sNAdc> SlewingPlots_t; + VecSlewingCoefs_t mSlewingCoefs{}; + SlewingPlots_t makeSlewingPlots() const; + constexpr static const char* getObjectPath() + { + return "FT0/Calib/SlewingCoef"; + } + ClassDefNV(SlewingCoef, 1) +}; +} // namespace ft0 +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h index 22dd7a0a8aa8e..7f8c17a0cd191 100644 --- a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h +++ b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h @@ -53,4 +53,9 @@ #pragma link C++ class o2::ft0::GlobalOffsetsCalibrationObject + ; #pragma link C++ class o2::ft0::RecoCalibInfoObject + ; #pragma link C++ class o2::ft0::GlobalOffsetsInfoObject + ; +#pragma link C++ class std::pair < std::vector < double>, std::vector < double>> + ; +#pragma link C++ class o2::ft0::SlewingCoef + ; + +#pragma link C++ class o2::ft0::EventsPerBc + ; + #endif diff --git a/DataFormats/Detectors/FIT/FT0/src/LookUpTable.cxx b/DataFormats/Detectors/FIT/FT0/src/LookUpTable.cxx new file mode 100644 index 0000000000000..aa29f589acdec --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/src/LookUpTable.cxx @@ -0,0 +1,15 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFT0/LookUpTable.h" + +using namespace o2::ft0; +template class o2::fit::SingleLUT>; diff --git a/DataFormats/Detectors/FIT/FT0/src/RecPoints.cxx b/DataFormats/Detectors/FIT/FT0/src/RecPoints.cxx index f580d0dd1ea8c..afd244f977f71 100644 --- a/DataFormats/Detectors/FIT/FT0/src/RecPoints.cxx +++ b/DataFormats/Detectors/FIT/FT0/src/RecPoints.cxx @@ -21,14 +21,22 @@ using namespace o2::ft0; +void ChannelDataFloat::print() const +{ + printf(" ChID% d | CFDtime=%f | QTCampl=%f QTC chain %d\n", ChId, CFDTime, QTCAmpl, ChainQTC); +} + gsl::span RecPoints::getBunchChannelData(const gsl::span tfdata) const { // extract the span of channel data for this bunch from the whole TF data return ref.getEntries() ? gsl::span(tfdata).subspan(ref.getFirstEntry(), ref.getEntries()) : gsl::span(); } -void ChannelDataFloat::print() const +void RecPoints::print() const { - - printf(" ChID% d | CFDtime=%f | QTCampl=%f QTC chain %d\n", ChId, CFDTime, QTCAmpl, ChainQTC); + LOG(info) << "RecPoint data:"; + LOG(info) << "Collision times: mean: " << getCollisionTimeMean() << ", A: " << getCollisionTimeA() << ", C: " << getCollisionTimeC(); + LOG(info) << "Vertex: " << getVertex(); + LOG(info) << "Ref first: " << ref.getFirstEntry() << ", Ref entries: " << ref.getEntries(); + LOG(info) << "Triggers: " << mTriggers.print(); } diff --git a/DataFormats/Detectors/FIT/FT0/src/SlewingCoef.cxx b/DataFormats/Detectors/FIT/FT0/src/SlewingCoef.cxx new file mode 100644 index 0000000000000..e1d8bda5ac4ea --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/src/SlewingCoef.cxx @@ -0,0 +1,32 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFT0/SlewingCoef.h" +#include +using namespace o2::ft0; + +SlewingCoef::SlewingPlots_t SlewingCoef::makeSlewingPlots() const +{ + typename o2::ft0::SlewingCoef::SlewingPlots_t plots{}; + for (int iAdc = 0; iAdc < sNAdc; iAdc++) { + const auto& slewingCoefs = mSlewingCoefs[iAdc]; + auto& plotsAdc = plots[iAdc]; + for (int iCh = 0; iCh < sNCHANNELS; iCh++) { + const auto& points_x = slewingCoefs[iCh].first; + const auto& points_y = slewingCoefs[iCh].second; + assert(points_x.size() == points_y.size()); + const int nPoints = points_x.size(); + auto& plot = plotsAdc[iCh]; + plot = TGraph(nPoints, points_x.data(), points_y.data()); + } + } + return plots; +} diff --git a/DataFormats/Detectors/FIT/FV0/CMakeLists.txt b/DataFormats/Detectors/FIT/FV0/CMakeLists.txt index 9eea4dd092d7a..35bc653a8234e 100644 --- a/DataFormats/Detectors/FIT/FV0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FV0/CMakeLists.txt @@ -16,11 +16,13 @@ o2_add_library(DataFormatsFV0 src/RecPoints.cxx src/RawEventData.cxx src/CTF.cxx + src/LookUpTable.cxx PUBLIC_LINK_LIBRARIES O2::FV0Base O2::DataFormatsFIT O2::SimulationDataFormat O2::CommonDataFormat Microsoft.GSL::GSL + O2::DetectorsCommonDataFormats ) o2_target_root_dictionary(DataFormatsFV0 @@ -33,5 +35,4 @@ o2_target_root_dictionary(DataFormatsFV0 include/DataFormatsFV0/RecPoints.h include/DataFormatsFV0/RawEventData.h include/DataFormatsFV0/LookUpTable.h - include/DataFormatsFV0/CTF.h -) + include/DataFormatsFV0/CTF.h) diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h index e71e42ca2f4ad..275b682a15276 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h @@ -41,8 +41,8 @@ struct CompressedDigits { // trigger data std::vector trigger; // trigger bits - std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc - std::vector orbitInc; // increment in orbit + std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc + std::vector orbitInc; // increment in orbit std::vector nChan; // number of fired channels // channel data diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/ChannelData.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/ChannelData.h index 054b336510c4f..29447dfa04202 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/ChannelData.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/ChannelData.h @@ -76,8 +76,8 @@ struct ChannelData { void print() const; void printLog() const; [[nodiscard]] uint8_t getChannelID() const { return ChId; } - [[nodiscard]] uint16_t getTime() const { return CFDTime; } - [[nodiscard]] uint16_t getAmp() const { return QTCAmpl; } + [[nodiscard]] int16_t getTime() const { return CFDTime; } + [[nodiscard]] int16_t getAmp() const { return QTCAmpl; } bool operator==(ChannelData const& other) const { diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h index 6c10d56a733e3..4c77b0143d1dc 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h @@ -9,71 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// file RawEventData.h class for RAW data format -// Alla.Maevskaya -// simple look-up table just to feed digits 2 raw procedure. -// Will be really set after module/electronics connections -// #ifndef ALICEO2_FV0_LOOKUPTABLE_H_ #define ALICEO2_FV0_LOOKUPTABLE_H_ //////////////////////////////////////////////// // Look Up Table FV0 ////////////////////////////////////////////// - #include "DataFormatsFIT/LookUpTable.h" -#include "CommonUtils/NameConf.h" - +#include "DetectorsCommonDataFormats/DetID.h" namespace o2 { namespace fv0 { - -namespace new_lut - -{ -// Singleton for LookUpTable -template -class SingleLUT : public LUT -{ - private: - SingleLUT() = default; - SingleLUT(const std::string& ccdbPath, const std::string& ccdbPathToLUT) : LUT(ccdbPath, ccdbPathToLUT) {} - SingleLUT(const std::string& pathToFile) : LUT(pathToFile) {} - SingleLUT(const SingleLUT&) = delete; - SingleLUT& operator=(SingleLUT&) = delete; - - public: - static constexpr char sDetectorName[] = "FV0"; - static constexpr char sDefaultLUTpath[] = "FV0/Config/LookupTable"; - static constexpr char sObjectName[] = "LookupTable"; - inline static std::string sCurrentCCDBpath = ""; - inline static std::string sCurrentLUTpath = sDefaultLUTpath; - // Before instance() call, setup url and path - static void setCCDBurl(const std::string& url) { sCurrentCCDBpath = url; } - static void setLUTpath(const std::string& path) { sCurrentLUTpath = path; } - - using Table_t = typename LUT::Table_t; - bool mFirstUpdate{true}; // option in case if LUT should be updated during workflow initialization - static SingleLUT& Instance(const Table_t* table = nullptr) - { - if (sCurrentCCDBpath == "") { - sCurrentCCDBpath = o2::base::NameConf::getCCDBServer(); - } - static SingleLUT instanceLUT; - if (table != nullptr) { - instanceLUT.initFromTable(table); - instanceLUT.mFirstUpdate = false; - } else if (instanceLUT.mFirstUpdate) { - instanceLUT.initCCDB(sCurrentCCDBpath, sCurrentLUTpath); - instanceLUT.mFirstUpdate = false; - } - return instanceLUT; - } -}; -} // namespace new_lut - -using SingleLUT = new_lut::SingleLUT>; - +using SingleLUT = o2::fit::SingleLUT>; } // namespace fv0 } // namespace o2 #endif diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h index c5e0a827de132..b3527fdd049d2 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h @@ -28,7 +28,7 @@ namespace fv0 struct ChannelDataFloat { int channel = -1; // channel Id - double time = -20000; // time in ps, 0 at the LHC clk center + double time = -20000; // time in ns, 0 at the LHC clk center double charge = -20000; // charge [channels] int adcId = -1; // QTC chain @@ -42,6 +42,7 @@ struct ChannelDataFloat { } void print() const; + bool operator==(const ChannelDataFloat&) const = default; ClassDefNV(ChannelDataFloat, 1); }; @@ -77,6 +78,9 @@ class RecPoints gsl::span getBunchChannelData(const gsl::span tfdata) const; short static constexpr sDummyCollissionTime = 32767; + void print() const; + bool operator==(const RecPoints&) const = default; + private: o2::dataformats::RangeReference mRef; o2::InteractionRecord mIntRecord; diff --git a/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h b/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h index 3cd6a237f8e05..ab23b4aa727bc 100644 --- a/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h +++ b/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h @@ -18,6 +18,8 @@ #pragma link C++ class o2::fv0::Hit + ; #pragma link C++ class vector < o2::fv0::Hit> + ; #pragma link C++ class o2::fv0::MCLabel + ; +#include "SimulationDataFormat/MCTruthContainer.h" +#pragma link C++ class o2::dataformats::MCTruthContainer < o2::fv0::MCLabel> + ; #pragma link C++ class o2::fv0::ChannelData + ; #pragma link C++ class o2::fv0::Digit + ; diff --git a/DataFormats/Detectors/FIT/FV0/src/LookUpTable.cxx b/DataFormats/Detectors/FIT/FV0/src/LookUpTable.cxx new file mode 100644 index 0000000000000..7a1d5969abb79 --- /dev/null +++ b/DataFormats/Detectors/FIT/FV0/src/LookUpTable.cxx @@ -0,0 +1,15 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFV0/LookUpTable.h" + +using namespace o2::fv0; +template class o2::fit::SingleLUT>; diff --git a/DataFormats/Detectors/FIT/FV0/src/RecPoints.cxx b/DataFormats/Detectors/FIT/FV0/src/RecPoints.cxx index 18f2effc281e8..ef1554acf5419 100644 --- a/DataFormats/Detectors/FIT/FV0/src/RecPoints.cxx +++ b/DataFormats/Detectors/FIT/FV0/src/RecPoints.cxx @@ -14,13 +14,22 @@ using namespace o2::fv0; -gsl::span RecPoints::getBunchChannelData(const gsl::span tfdata) const +void ChannelDataFloat::print() const { - // extract the span of channel data for this bunch from the whole TF data - return mRef.getEntries() ? gsl::span(tfdata).subspan(mRef.getFirstEntry(), mRef.getEntries()) : gsl::span(); + printf(" Channel=%d | time=%f | charge=%f | adcId=%d\n", channel, time, charge, adcId); } -void ChannelDataFloat::print() const +void RecPoints::print() const { - printf(" Channel=%d | time=%f | charge=%f | adcId=%d\n", channel, time, charge, adcId); + printf("RecPoint data:"); + printf("Collision times: first: %f, global mean: %f, selected mean: %f\n", getCollisionFirstTime(), getCollisionGlobalMeanTime(), getCollisionSelectedMeanTime()); + printf("Ref first: %d, Ref entries: %d\n", mRef.getFirstEntry(), mRef.getEntries()); + printf("Triggers: "); + mTriggers.print(); +} + +gsl::span RecPoints::getBunchChannelData(const gsl::span tfdata) const +{ + // extract the span of channel data for this bunch from the whole TF data + return mRef.getEntries() ? gsl::span(tfdata).subspan(mRef.getFirstEntry(), mRef.getEntries()) : gsl::span(); } diff --git a/DataFormats/Detectors/FIT/common/CMakeLists.txt b/DataFormats/Detectors/FIT/common/CMakeLists.txt index a42142fb55fb0..61dbcabc7f087 100644 --- a/DataFormats/Detectors/FIT/common/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/common/CMakeLists.txt @@ -12,10 +12,15 @@ o2_add_library(DataFormatsFIT SOURCES src/RawEventData.cxx src/Triggers.cxx + src/RawDataMetric.cxx + src/LookUpTable.cxx PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + O2::DetectorsCommonDataFormats O2::CCDB) o2_target_root_dictionary(DataFormatsFIT HEADERS include/DataFormatsFIT/DCSDPValues.h include/DataFormatsFIT/LookUpTable.h - include/DataFormatsFIT/Triggers.h) + include/DataFormatsFIT/Triggers.h + include/DataFormatsFIT/ChannelData.h + include/DataFormatsFIT/Digit.h) diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/ChannelData.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/ChannelData.h new file mode 100644 index 0000000000000..452307d05f200 --- /dev/null +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/ChannelData.h @@ -0,0 +1,70 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ChannelData.h +/// \brief Class represents enity with info per channel in given event +/// \author Artur Furs afurs@cern.ch + +#ifndef _FT0_CHANNELDATA_H_ +#define _FT0_CHANNELDATA_H_ + +#include +#include +#include +#include "DetectorsCommonDataFormats/DetID.h" + +namespace o2 +{ +namespace fit +{ + +template +struct ChannelData { + static constexpr o2::detectors::DetID sDetID = o2::detectors::DetID(DetID); + static constexpr uint8_t sDUMMY_CHANNEL_ID = 0xff; + static constexpr uint8_t sDUMMY_PM_WORD = 0xff; + static constexpr int16_t sDUMMY_TIME = -5000; + static constexpr int16_t sDUMMY_AMP = -5000; + uint8_t mChannelID = sDUMMY_CHANNEL_ID; // channel id + uint8_t mWordPM = sDUMMY_PM_WORD; // PM word, based on EBitsPM + int16_t mTime = sDUMMY_TIME; // time in TDC units + int16_t mAmp = sDUMMY_AMP; // amplitude in ADC units + enum EBitsPM { + kNumberADC, + kIsDoubleEvent, + kIsTimeInfoNotValid, + kIsCFDinADCgate, + kIsTimeInfoLate, + kIsAmpNotValid, + kIsVertexEvent, + kIsTimeInfoLost + }; + static const inline std::map sMapBitsPM = { + {EBitsPM::kNumberADC, "NumberADC"}, + {EBitsPM::kIsDoubleEvent, "IsDoubleEvent"}, + {EBitsPM::kIsTimeInfoNotValid, "IsTimeInfoNotValid"}, + {EBitsPM::kIsCFDinADCgate, "IsCFDinADCgate"}, + {EBitsPM::kIsTimeInfoLate, "IsTimeInfoLate"}, + {EBitsPM::kIsAmpNotValid, "IsAmpNotValid"}, + {EBitsPM::kIsVertexEvent, "IsVertexEvent"}, + {EBitsPM::kIsTimeInfoLost, "IsTimeInfoLost"}}; + ChannelData() = default; + ChannelData(uint8_t channelID, uint8_t wordPM, int16_t time, int16_t amp) : mChannelID(channelID), mWordPM(wordPM), mTime(time), mAmp(amp) + { + } + // void print() const; + bool operator<=>(ChannelData const& other) const = default; + bool operator==(ChannelData const& other) const = default; + ClassDefNV(ChannelData, 1); +}; +} // namespace fit +} // namespace o2 +#endif diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h index 26e1969fca968..97bbe982e6aac 100644 --- a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h @@ -14,22 +14,23 @@ #include #include "Framework/Logger.h" +#include "Framework/O2LongInt.h" namespace o2 { namespace fit { struct DCSDPValues { - std::vector> values; + std::vector> values; DCSDPValues() { - values = std::vector>(); + values = std::vector>(); } void add(uint64_t timestamp, int64_t value) { - values.push_back(std::pair(timestamp, value)); + values.push_back(std::pair(timestamp, value)); } bool empty() @@ -63,4 +64,4 @@ struct DCSDPValues { } // namespace fit } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Digit.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Digit.h new file mode 100644 index 0000000000000..adaef4ec8349a --- /dev/null +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Digit.h @@ -0,0 +1,79 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// \file Digit.h +/// \brief FIT event entity +/// \author Artur Furs afurs@cern.ch + +#ifndef O2_FIT_DIGIT_H_ +#define O2_FIT_DIGIT_H_ + +#include "CommonDataFormat/InteractionRecord.h" +#include "CommonDataFormat/RangeReference.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsFIT/ChannelData.h" +#include "DataFormatsFIT/Triggers.h" + +#include +#include + +namespace o2 +{ +namespace fit +{ +using Triggers = o2::fit::Triggers; + +template +struct DigitBase { + static constexpr o2::detectors::DetID sDetID = o2::detectors::DetID(DetID); + typedef ChannelData ChannelData_t; // related ChannelData entity + o2::InteractionRecord mIR{}; // Interaction record (orbit, bc) + o2::dataformats::RangeReference mReference{}; + DigitBase() = default; + DigitBase(int first, int nEntries, const o2::InteractionRecord& ir) : mIR(ir) + { + mReference.setFirstEntry(first); + mReference.setEntries(nEntries); + } + uint32_t getOrbit() const { return mIR.orbit; } + uint16_t getBC() const { return mIR.bc; } + const o2::InteractionRecord& getIntRecord() const { return mIR; }; + gsl::span getBunchChannelData(const gsl::span channelData) const + { + return mReference.getEntries() ? gsl::span(&channelData[mReference.getFirstEntry()], mReference.getEntries()) : gsl::span(); + } + ClassDefNV(DigitBase, 1); +}; + +template +struct Digit : public DigitBase { + uint8_t mTriggerWord{}; + Digit() = default; + Digit(int first, int nEntries, const o2::InteractionRecord& ir, uint8_t trgWord) : DigitBase(first, nEntries, ir), mTriggerWord(trgWord) + { + } + ClassDefNV(Digit, 1); +}; + +template +struct DigitExt : public DigitBase { + Triggers mTriggers{}; + DigitExt() = default; + DigitExt(int first, int nEntries, const o2::InteractionRecord& ir, const Triggers& triggers) : DigitBase(first, nEntries, ir), mTriggers(triggers) + { + } + ClassDefNV(DigitExt, 1); +}; + +} // namespace fit +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/LookUpTable.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/LookUpTable.h index 490389b3bd30a..4a6d61340641f 100644 --- a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/LookUpTable.h +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/LookUpTable.h @@ -15,12 +15,16 @@ // Look Up Table FIT ////////////////////////////////////////////// -#include "CCDB/BasicCCDBManager.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/NameConf.h" +#include "Framework/Logger.h" +#define BOOST_BIND_GLOBAL_PLACEHOLDERS #include #include #include #include #include +#include #include #include #include @@ -156,8 +160,7 @@ enum class EModuleType : int { kUnknown, kTCM }; template , - typename MapEntryPM2ChannelID = std::unordered_map, - typename = typename std::enable_if_t::value>> + typename MapEntryPM2ChannelID = std::unordered_map> class LookupTableBase { public: @@ -172,9 +175,9 @@ class LookupTableBase typedef EntryPM_t Topo_t; // temporary for common interface LookupTableBase() = default; - LookupTableBase(const Table_t& vecEntryFEE) { initFromTable(vecEntryFEE); } + LookupTableBase(const Table_t* vecEntryFEE) { initFromTable(vecEntryFEE); } LookupTableBase(const std::string& pathToFile) { initFromFile(pathToFile); } - LookupTableBase(const std::string& urlCCDB, const std::string& pathToStorageInCCDB) { initCCDB(urlCCDB, pathToStorageInCCDB); } + LookupTableBase(const std::string& urlCCDB, const std::string& pathToStorageInCCDB, long timestamp = -1) { initCCDB(urlCCDB, pathToStorageInCCDB, timestamp); } // Map of str module names -> enum types const std::map mMapModuleTypeStr2Enum = {{"PM", EModuleType::kPM}, {"PM-LCS", EModuleType::kPM_LCS}, {"TCM", EModuleType::kTCM}}; // Warning! To exclude double mapping do not use isTCM and isPM in the same time @@ -182,6 +185,12 @@ class LookupTableBase { return mEntryCRU_TCM.mLinkID == linkID && mEntryCRU_TCM.mEndPointID == epID; } + + bool isPM(int linkID, int epID) const + { + return isPM(EntryCRU_t{linkID, epID}); + } + bool isTCM(const EntryCRU_t& entryCRU) const { if (getModuleType(entryCRU) == EModuleType::kTCM) { @@ -228,20 +237,16 @@ class LookupTableBase } inputDir += "/share/Detectors/FT0/files/"; filepath = inputDir + "LookupTable_FT0.json"; - filepath = gSystem->ExpandPathName(filepath.data()); // Expand $(ALICE_ROOT) into real system path + TString expandedFilepath = filepath; + gSystem->ExpandPathName(expandedFilepath); // Expand $(ALICE_ROOT) into real system path + filepath = expandedFilepath.Data(); } else { filepath = pathToFile; } prepareEntriesFEE(filepath); prepareLUT(); } - void initCCDB(const std::string& urlCCDB, const std::string& pathToStorageInCCDB) - { - auto& mgr = o2::ccdb::BasicCCDBManager::instance(); - mgr.setURL(urlCCDB); - mVecEntryFEE = *(mgr.get(pathToStorageInCCDB)); - prepareLUT(); - } + void initCCDB(const std::string& urlCCDB, const std::string& pathToStorageInCCDB, long timestamp = -1); void initFromTable(const Table_t* vecEntryFEE) { mVecEntryFEE = *vecEntryFEE; @@ -411,7 +416,68 @@ class LookupTableBase Table_t mVecEntryFEE; MapEntryCRU2ModuleType_t mMapEntryCRU2ModuleType; MapEntryPM2ChannelID_t mMapEntryPM2ChannelID; + typedef std::enable_if_t::value> CheckChannelIDtype; // should be integral }; + +// Singleton for LookUpTable, coomon for all three FIT detectors +template +class SingleLUT : public LUT +{ + private: + SingleLUT() = default; + SingleLUT(const std::string& ccdbPath, const std::string& ccdbPathToLUT) : LUT(ccdbPath, ccdbPathToLUT) {} + SingleLUT(const std::string& pathToFile) : LUT(pathToFile) {} + SingleLUT(const SingleLUT&) = delete; + SingleLUT& operator=(SingleLUT&) = delete; + constexpr static bool isValidDet() + { + return (DetID == o2::detectors::DetID::FDD) || (DetID == o2::detectors::DetID::FT0) || (DetID == o2::detectors::DetID::FV0); + } + + public: + typedef LUT LookupTable_t; + typedef typename LookupTable_t::Table_t Table_t; + + constexpr static const char* getObjectPath() + { + static_assert(isValidDet(), "Invalid detector type(o2::detectors::DetID::ID)! Should be one of the FIT detector!"); + if constexpr (DetID == o2::detectors::DetID::FDD) { + return "FDD/Config/LookupTable"; + } else if constexpr (DetID == o2::detectors::DetID::FT0) { + return "FT0/Config/LookupTable"; + } else if constexpr (DetID == o2::detectors::DetID::FV0) { + return "FV0/Config/LookupTable"; + } + return ""; + } + static constexpr o2::detectors::DetID sDetID = o2::detectors::DetID(DetID); + static constexpr const char* sDetectorName = o2::detectors::DetID::getName(DetID); + static constexpr const char* sDefaultLUTpath = getObjectPath(); + static constexpr const char sObjectName[] = "LookupTable"; + inline static std::string sCurrentCCDBpath = ""; + inline static std::string sCurrentLUTpath = sDefaultLUTpath; + // Before instance() call, setup url and path + static void setCCDBurl(const std::string& url) { sCurrentCCDBpath = url; } + static void setLUTpath(const std::string& path) { sCurrentLUTpath = path; } + bool mFirstUpdate{true}; // option in case if LUT should be updated during workflow initialization + static SingleLUT& Instance(const Table_t* table = nullptr, long timestamp = -1) + { + if (sCurrentCCDBpath == "") { + sCurrentCCDBpath = o2::base::NameConf::getCCDBServer(); + } + static SingleLUT instanceLUT; + if (table != nullptr) { + instanceLUT.initFromTable(table); + instanceLUT.mFirstUpdate = false; + } else if (instanceLUT.mFirstUpdate) { + instanceLUT.initCCDB(sCurrentCCDBpath, sCurrentLUTpath, timestamp); + instanceLUT.mFirstUpdate = false; + } + return instanceLUT; + } +}; + } // namespace fit } // namespace o2 + #endif diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawDataMetric.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawDataMetric.h new file mode 100644 index 0000000000000..79aba23841abc --- /dev/null +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawDataMetric.h @@ -0,0 +1,83 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// file RawDataMetric.h class Struct for collecting metrics during decoding +// with Artur.Furs@cern.ch +// +#ifndef ALICEO2_FIT_RAWDATAMETRIC_H_ +#define ALICEO2_FIT_RAWDATAMETRIC_H_ + +#include +#include +#include +#include + +namespace o2 +{ +namespace fit +{ +struct RawDataMetric { + RawDataMetric(uint8_t linkID, uint8_t EPID, uint16_t FEEID, bool isRegisteredFEE = true) : mLinkID(linkID), mEPID(EPID), mFEEID(FEEID), mIsRegisteredFEE(isRegisteredFEE) {} + ~RawDataMetric() = default; + + enum EStatusBits { + kIncompletePayload, // Incomplete payload + kWrongDescriptor, // Wrong descriptors in header + kWrongChannelMapping, // Wrong channel mapping + kEmptyDataBlock, // Only header in data block + kDecodedDataBlock // Decoded w/o any issue data block + }; + typedef uint8_t Status_t; + constexpr static uint8_t sNbits = 5; + inline bool checkBadDataBlock(Status_t metric) + { + bool result = checkStatusBit(metric, EStatusBits::kIncompletePayload); + if (!result) { // Incomplete payload has high priority among errors + result |= checkStatusBit(metric, EStatusBits::kEmptyDataBlock); + // Lets just check this for a while, w/o any decision for data block + // result |= checkStatusBit(metric,EStatusBits::kWrongDescriptor); + checkStatusBit(metric, EStatusBits::kWrongDescriptor); + } + return result; + } + inline void addStatusBit(EStatusBits statusBit, bool val = true) + { + mBitStats[statusBit] += static_cast(val); + } + + inline bool checkStatusBit(Status_t metric, EStatusBits statusBit) + { + const bool result = (metric & (1 << statusBit)) > 0; + mBitStats[statusBit] += static_cast(result); + return result; + } + + inline static bool isBitActive(Status_t metric, EStatusBits statusBit) + { + return (metric & (1 << statusBit)) > 0; + } + + inline static void setStatusBit(Status_t& metric, EStatusBits statusBit, bool val = true) + { + metric |= (static_cast(val) << statusBit); + } + void print() const; + static Status_t getAllBitsActivated(); + uint8_t mLinkID; + uint8_t mEPID; + uint16_t mFEEID; + bool mIsRegisteredFEE; + std::array mBitStats{}; + const static std::map sMapBitsToNames; +}; +} // namespace fit +} // namespace o2 +#endif diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawEventData.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawEventData.h index a7edf6785e69e..ec04f770f8061 100644 --- a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawEventData.h +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawEventData.h @@ -28,6 +28,7 @@ struct EventHeader { static constexpr size_t PayloadPerGBTword = 10; static constexpr size_t MinNelements = 1; static constexpr size_t MaxNelements = 1; + static constexpr uint64_t sDescriptor = 0xf; uint64_t bc : 12; uint64_t orbit : 32; uint64_t phase : 3; @@ -44,6 +45,7 @@ struct EventHeader { bc = intRec.bc; orbit = intRec.orbit; } + inline bool isBadDescriptor() const { return startDescriptor != sDescriptor; } void print() const; } __attribute__((__packed__)); @@ -54,7 +56,7 @@ struct EventData { static constexpr size_t MaxNelements = 12; int16_t time : 12; int16_t charge : 13; - uint8_t pmBits : 8; + uint16_t pmBits : 8; uint8_t reservedField : 3; uint8_t channelID : 4; void generateFlags() @@ -76,7 +78,7 @@ struct EventData { struct TCMdata { static constexpr size_t PayloadSize = 10; static constexpr size_t PayloadPerGBTword = 10; - static constexpr size_t MinNelements = 1; + static constexpr size_t MinNelements = 0; static constexpr size_t MaxNelements = 1; uint64_t orA : 1, // 0 bit (0 byte) diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Triggers.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Triggers.h index dbdf4759c1dce..a660d77820207 100644 --- a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Triggers.h +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Triggers.h @@ -25,10 +25,15 @@ namespace o2 { namespace fit { - class Triggers { public: + template + constexpr static uint64_t word(TrgBits&&... trgBits) + { + return ((1ull << std::forward(trgBits)) | ...); + } + enum { bitA = 0, bitC = 1, // alias of bitAOut (FT0/FDD) bitAOut = 1, // alias of bitC (FV0) @@ -40,7 +45,8 @@ class Triggers bitAIn = 4, // alias of bitVertex (FV0) bitLaser = 5, // indicates the laser was triggered in this BC bitOutputsAreBlocked = 6, // indicates that laser-induced pulses should arrive from detector to FEE in this BC (and trigger outputs are blocked) - bitDataIsValid = 7 }; // data is valid for processing + bitDataIsValid = 7, // data is valid for processing + bitMinBias = 8 }; // extra calculated bit, vrt & (cern || semicent) static const int16_t DEFAULT_TIME = -5000; // for average of one side (A or C) static const int16_t DEFAULT_AMP = 0; static const int16_t DEFAULT_ZERO = 0; @@ -56,6 +62,21 @@ class Triggers timeA = atimeA; timeC = atimeC; } + inline static bool checkMinBiasFT0(uint64_t trgWord) + { + return static_cast(trgWord & word(bitVertex)) && static_cast(trgWord & word(bitSCen, bitCen)); + } + static uint64_t makeExtendedTrgWord(uint64_t trgWord) + { + return trgWord | (static_cast(checkMinBiasFT0(trgWord)) << bitMinBias); + } + static constexpr std::pair parseDigitTriggerWord(uint8_t digitWord, bool shiftTechBitsToBegin = false) + { + const uint8_t techWordMask = word(bitLaser, bitOutputsAreBlocked, bitDataIsValid); + const uint8_t shiftTechWordPos = shiftTechBitsToBegin ? bitLaser : 0; + return {(digitWord & (~techWordMask)), (digitWord & techWordMask) >> shiftTechWordPos}; + } + bool getOrA() const { return (triggersignals & (1 << bitA)) != 0; } bool getOrC() const { return (triggersignals & (1 << bitC)) != 0; } // only used by FT0/FDD (same bit as OrAOut in FV0) bool getOrAOut() const { return (triggersignals & (1 << bitAOut)) != 0; } // only used by FV0 (same bit as OrC in FT0/FDD) @@ -68,6 +89,8 @@ class Triggers bool getLaser() const { return (triggersignals & (1 << bitLaser)) != 0; } bool getOutputsAreBlocked() const { return (triggersignals & (1 << bitOutputsAreBlocked)) != 0; } bool getDataIsValid() const { return (triggersignals & (1 << bitDataIsValid)) != 0; } + bool getMinBiasFT0() const { return checkMinBiasFT0(static_cast(triggersignals)); } + uint64_t getExtendedTrgWordFT0() const { return makeExtendedTrgWord(static_cast(triggersignals)); } uint8_t getTriggersignals() const { return triggersignals; } uint8_t getNChanA() const { return nChanA; } @@ -87,6 +110,10 @@ class Triggers timeA = atimeA; timeC = atimeC; } + void setTriggers(uint8_t trgsig) + { + triggersignals = trgsig; + } void setTriggers(Bool_t isA, Bool_t isC, Bool_t isVrtx, Bool_t isCnt, Bool_t isSCnt, uint8_t chanA, uint8_t chanC, int32_t aamplA, int32_t aamplC, int16_t atimeA, int16_t atimeC, Bool_t isLaser, Bool_t isOutputsAreBlocked, Bool_t isDataValid) diff --git a/DataFormats/Detectors/FIT/common/src/DataFormatsFITLinkDef.h b/DataFormats/Detectors/FIT/common/src/DataFormatsFITLinkDef.h index 6f41ab93eed79..e005315e2d6a0 100644 --- a/DataFormats/Detectors/FIT/common/src/DataFormatsFITLinkDef.h +++ b/DataFormats/Detectors/FIT/common/src/DataFormatsFITLinkDef.h @@ -25,6 +25,19 @@ #pragma link C++ std::vector < std::pair < uint64_t, int>> + ; #pragma link C++ struct o2::fit::DCSDPValues + ; +/* +#include "DetectorsCommonDataFormats/DetID.h" +#pragma link C++ struct o2::fit::ChannelData + ; +#pragma link C++ struct o2::fit::Digit + ; + +#pragma link C++ struct o2::fit::ChannelData + ; +#pragma link C++ struct o2::fit::Digit + ; + +#pragma link C++ struct o2::fit::ChannelData + ; +#pragma link C++ struct o2::fit::Digit + ; +*/ +// #pragma link C++ struct o2::fit::ChannelData; +// #pragma link C++ struct o2::fit::Digit; // TODO AM: Set this here when unused class warning is solved. // #pragma link C++ class std::unordered_map < o2::dcs::DataPointIdentifier, o2::fit::DCSDPValues> + ; diff --git a/DataFormats/Detectors/FIT/common/src/LookUpTable.cxx b/DataFormats/Detectors/FIT/common/src/LookUpTable.cxx new file mode 100644 index 0000000000000..73c0b1bf1bb9e --- /dev/null +++ b/DataFormats/Detectors/FIT/common/src/LookUpTable.cxx @@ -0,0 +1,26 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFIT/LookUpTable.h" +#include "CCDB/BasicCCDBManager.h" +#include +using namespace o2::fit; +template +void LookupTableBase::initCCDB(const std::string& urlCCDB, const std::string& pathToStorageInCCDB, long timestamp) +{ + + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(urlCCDB); + mVecEntryFEE = *(mgr.getForTimeStamp::Table_t>(pathToStorageInCCDB, timestamp)); + prepareLUT(); +} +template class o2::fit::LookupTableBase, + std::unordered_map>; diff --git a/DataFormats/Detectors/FIT/common/src/RawDataMetric.cxx b/DataFormats/Detectors/FIT/common/src/RawDataMetric.cxx new file mode 100644 index 0000000000000..2b686afc6cfcd --- /dev/null +++ b/DataFormats/Detectors/FIT/common/src/RawDataMetric.cxx @@ -0,0 +1,38 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFIT/RawDataMetric.h" +#include +using namespace o2::fit; +const std::map RawDataMetric::sMapBitsToNames = { + {RawDataMetric::EStatusBits::kIncompletePayload, "IncompletePayload"}, + {RawDataMetric::EStatusBits::kWrongDescriptor, "WrongDescriptor"}, + {RawDataMetric::EStatusBits::kWrongChannelMapping, "WrongChannelMapping"}, + {RawDataMetric::EStatusBits::kEmptyDataBlock, "EmptyDataBlock"}, + {RawDataMetric::EStatusBits::kDecodedDataBlock, "DecodedDataBlock"}}; +void RawDataMetric::print() const +{ + LOG(info) << "=============================================================="; + LOG(info) << "Raw data metric: linkID " << static_cast(mLinkID) << " mEPID " << static_cast(mEPID) << " FEEID " << static_cast(mFEEID); + LOG(info) << "Is registered FEE: " << mIsRegisteredFEE; + for (const auto& entry : sMapBitsToNames) { + LOG(info) << entry.second << ": " << mBitStats[entry.first]; + } + LOG(info) << "=============================================================="; +} +RawDataMetric::Status_t RawDataMetric::getAllBitsActivated() +{ + Status_t metricStatus{}; + for (const auto& entry : sMapBitsToNames) { + metricStatus |= (1 << entry.first); + } + return metricStatus; +} diff --git a/DataFormats/Detectors/GlobalTracking/CMakeLists.txt b/DataFormats/Detectors/GlobalTracking/CMakeLists.txt index 41f44e02afe30..b219de73f5b47 100644 --- a/DataFormats/Detectors/GlobalTracking/CMakeLists.txt +++ b/DataFormats/Detectors/GlobalTracking/CMakeLists.txt @@ -9,6 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +#add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) + o2_add_library( DataFormatsGlobalTracking SOURCES src/RecoContainer.cxx @@ -34,7 +36,8 @@ o2_add_library( O2::DataFormatsCPV O2::DataFormatsPHOS O2::DataFormatsEMCAL - O2::GPUDataTypeHeaders + O2::GPUDataTypes + $<$:O2::ITS3Reconstruction> PRIVATE_LINK_LIBRARIES O2::Framework) diff --git a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h index 7c3df123124d5..31d531ef19265 100644 --- a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h +++ b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h @@ -19,7 +19,7 @@ #include "CommonDataFormat/InteractionRecord.h" #include "ReconstructionDataFormats/GlobalTrackAccessor.h" #include "CommonDataFormat/RangeReference.h" -#include "ReconstructionDataFormats/DecayNbody.h" +#include "ReconstructionDataFormats/Decay3Body.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "ReconstructionDataFormats/MatchingType.h" #include "CommonDataFormat/AbstractRefAccessor.h" @@ -37,6 +37,7 @@ namespace o2::tpc class TrackTPC; using TPCClRefElem = uint32_t; struct ClusterNativeAccess; +struct TriggerInfoDLBZS; namespace internal { struct getWorkflowTPCInput_ret; @@ -163,7 +164,12 @@ class PrimaryVertex; class VtxTrackIndex; class VtxTrackRef; class V0; +class V0Index; class Cascade; +class CascadeIndex; +class Decay3Body; +class Decay3BodyIndex; +class StrangeTrack; class TrackCosmics; class GlobalFwdTrack; class MatchInfoFwd; @@ -219,12 +225,14 @@ struct DataRequest { void requestITSClusters(bool mc); void requestMFTClusters(bool mc); void requestTPCClusters(bool mc); + void requestTPCOccMap(); + void requestTPCTriggers(); void requestTOFClusters(bool mc); void requestTRDTracklets(bool mc); void requestMCHClusters(bool mc); void requestMIDClusters(bool mc); void requestHMPClusters(bool mc); - // void requestHMPMatches(bool mc); // no input available at the moment + void requestHMPMatches(bool mc); // no input available at the moment void requestCTPDigits(bool mc); @@ -234,11 +242,16 @@ struct DataRequest { void requestCoscmicTracks(bool mc); - void requestPrimaryVertertices(bool mc); - void requestPrimaryVerterticesTMP(bool mc); - void requestSecondaryVertertices(bool mc); + void requestPrimaryVertices(bool mc); + void requestPrimaryVerticesTMP(bool mc); + void requestSecondaryVertices(bool mc); + void requestStrangeTracks(bool mc); void requestIRFramesITS(); + +#ifdef ENABLE_UPGRADES + void requestIT3Clusters(bool mc); +#endif }; // Helper class to requested data. @@ -278,14 +291,24 @@ struct RecoContainer { NPVTXSLOTS }; // slots to register secondary vertex data - enum SVTXSlots { V0S, // V0 objects + enum SVTXSlots { V0SIDX, // V0s indices + V0S, // V0 objects PVTX_V0REFS, // PV -> V0 references + CASCSIDX, // Cascade indices CASCS, // Cascade objects PVTX_CASCREFS, // PV -> Cascade reference + DECAY3BODYIDX, // 3-body decay indices DECAY3BODY, // 3-body decay objects PVTX_3BODYREFS, // PV -> 3-body decay references NSVTXSLOTS }; + // slots to register strangeness tracking data + enum STRKSlots { + STRACK, + STRACK_MC, + NSTRKSLOTS + }; + // slots for cosmics enum CosmicsSlots { COSM_TRACKS, COSM_TRACKS_MC, @@ -294,15 +317,19 @@ struct RecoContainer { using AccSlots = o2::dataformats::AbstractRefAccessor; // int here is a dummy placeholder using PVertexAccessor = o2::dataformats::AbstractRefAccessor; using SVertexAccessor = o2::dataformats::AbstractRefAccessor; + using STrackAccessor = o2::dataformats::AbstractRefAccessor; using CosmicsAccessor = o2::dataformats::AbstractRefAccessor; using GTrackID = o2::dataformats::GlobalTrackID; using GlobalIDSet = std::array; + static constexpr float PS2MUS = 1e-6; + o2::InteractionRecord startIR; // TF start IR std::array commonPool; PVertexAccessor pvtxPool; // containers for primary vertex related objects SVertexAccessor svtxPool; // containers for secondary vertex related objects + STrackAccessor strkPool; // containers for strangeness tracking related objects CosmicsAccessor cosmPool; // containers for cosmics track data std::unique_ptr> mcITSClusters; @@ -318,6 +345,7 @@ struct RecoContainer { o2::ctp::LumiInfo mCTPLumi; gsl::span clusterShMapTPC; ///< externally set TPC clusters sharing map + gsl::span occupancyMapTPC; ///< externally set TPC clusters occupancy map std::unique_ptr inputsTPCclusters; // special struct for TPC clusters access std::unique_ptr inputsTRD; // special struct for TRD tracklets, trigger records @@ -343,14 +371,15 @@ struct RecoContainer { void addTOFMatchesTPCTRD(o2::framework::ProcessingContext& pc, bool mc); void addTOFMatchesITSTPCTRD(o2::framework::ProcessingContext& pc, bool mc); - // void addHMPMatches(o2::framework::ProcessingContext& pc, bool mc); // no input available for the moment - + void addHMPMatches(o2::framework::ProcessingContext& pc, bool mc); void addMFTMCHMatches(o2::framework::ProcessingContext& pc, bool mc); void addMCHMIDMatches(o2::framework::ProcessingContext& pc, bool mc); void addITSClusters(o2::framework::ProcessingContext& pc, bool mc); void addMFTClusters(o2::framework::ProcessingContext& pc, bool mc); - void addTPCClusters(o2::framework::ProcessingContext& pc, bool mc, bool shmap); + void addTPCClusters(o2::framework::ProcessingContext& pc, bool mc, bool shmap, bool occmap); + void addTPCOccMap(o2::framework::ProcessingContext& pc); + void addTPCTriggers(o2::framework::ProcessingContext& pc); void addTOFClusters(o2::framework::ProcessingContext& pc, bool mc); void addHMPClusters(o2::framework::ProcessingContext& pc, bool mc); void addTRDTracklets(o2::framework::ProcessingContext& pc, bool mc); @@ -375,8 +404,14 @@ struct RecoContainer { void addPVerticesTMP(o2::framework::ProcessingContext& pc, bool mc); void addSVertices(o2::framework::ProcessingContext& pc, bool); + void addStrangeTracks(o2::framework::ProcessingContext& pc, bool mc); + void addIRFramesITS(o2::framework::ProcessingContext& pc); +#ifdef ENABLE_UPGRADES + void addIT3Clusters(o2::framework::ProcessingContext& pc, bool mc); +#endif + // custom getters // get contributors from single detectors: return array with sources set to all contributing GTrackIDs @@ -514,6 +549,7 @@ struct RecoContainer { auto getTPCTrackMCLabel(GTrackID id) const { return getObject(id, MCLABELS); } const o2::tpc::ClusterNativeAccess& getTPCClusters() const; const o2::dataformats::ConstMCTruthContainerView* getTPCClustersMCLabels() const; + auto getTPCTriggers() const { return getSpan(GTrackID::TPC, MATCHES); } // ITS-TPC const o2::dataformats::TrackTPCITS& getTPCITSTrack(GTrackID gid) const { return getTrack(gid); } @@ -599,13 +635,9 @@ struct RecoContainer { auto getITSTPCTRDTOFMatches() const { return getSpan(GTrackID::ITSTPCTRDTOF, MATCHES); } auto getITSTPCTRDTOFMatchesMCLabels() const { return getSpan(GTrackID::ITSTPCTRDTOF, MCLABELS); } - /* no input available for the moment // HMPID matches - auto getHMPMatchTriggers() const { return getSpan(GTrackID::HMP, TRACKREFS); } auto getHMPMatches() const { return getSpan(GTrackID::HMP, MATCHES); } - auto getHMPPhotsClusterCharges() const { return getSpan(GTrackID::HMP, PATTERNS); } auto getHMPMatchesMCLabels() const { return getSpan(GTrackID::HMP, MCLABELS); } - */ // TOF clusters auto getTOFClusters() const { return getSpan(GTrackID::TOF, CLUSTERS); } @@ -664,15 +696,29 @@ struct RecoContainer { auto getPrimaryVertexMCLabels() const { return pvtxPool.getSpan(PVTX_MCTR); } // Secondary vertices + const o2::dataformats::V0Index& getV0Idx(int i) const { return svtxPool.get_as(V0SIDX, i); } const o2::dataformats::V0& getV0(int i) const { return svtxPool.get_as(V0S, i); } + const o2::dataformats::CascadeIndex& getCascadeIdx(int i) const { return svtxPool.get_as(CASCSIDX, i); } const o2::dataformats::Cascade& getCascade(int i) const { return svtxPool.get_as(CASCS, i); } + + auto getV0sIdx() const { return svtxPool.getSpan(V0SIDX); } auto getV0s() const { return svtxPool.getSpan(V0S); } auto getPV2V0Refs() { return svtxPool.getSpan>(PVTX_V0REFS); } + + auto getCascadesIdx() const { return svtxPool.getSpan(CASCSIDX); } auto getCascades() const { return svtxPool.getSpan(CASCS); } auto getPV2CascadesRefs() { return svtxPool.getSpan>(PVTX_CASCREFS); } - auto getDecays3Body() const { return svtxPool.getSpan(DECAY3BODY); } + + auto getDecays3BodyIdx() const { return svtxPool.getSpan(DECAY3BODYIDX); } + auto getDecays3Body() const { return svtxPool.getSpan(DECAY3BODY); } auto getPV2Decays3BodyRefs() { return svtxPool.getSpan>(PVTX_3BODYREFS); } + // Strangeness track + auto getStrangeTracks() const { return strkPool.getSpan(STRACK); } + auto getStrangeTracksMCLabels() const { return strkPool.getSpan(STRACK_MC); } + const o2::dataformats::StrangeTrack& getStrangeTrack(int i) const { return strkPool.get_as(STRACK, i); } + + // Cosmic tracks const o2::dataformats::TrackCosmics& getCosmicTrack(int i) const { return cosmPool.get_as(COSM_TRACKS, i); } auto getCosmicTrackMCLabel(int i) const { return cosmPool.get_as(COSM_TRACKS_MC, i); } auto getCosmicTracks() const { return cosmPool.getSpan(COSM_TRACKS); } @@ -680,6 +726,18 @@ struct RecoContainer { // IRFrames where ITS was reconstructed and tracks were seen (e.g. sync.w-flow mult. selection) auto getIRFramesITS() const { return getSpan(GTrackID::ITS, VARIA); } + + void getTrackTimeITSTPCTRDTOF(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeTPCTRDTOF(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeITSTPCTOF(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeITSTPCTRD(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeTPCTRD(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeITSTPC(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeTPCTOF(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeITS(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeTPC(GTrackID gid, float& t, float& tErr) const; + + void getTrackTime(GTrackID gid, float& t, float& tErr) const; }; } // namespace globaltracking diff --git a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h index 9730f8c7ac506..ecce5a4d58b2f 100644 --- a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h +++ b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h @@ -17,6 +17,7 @@ #include "Framework/InputSpec.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DataFormatsTPC/WorkflowHelper.h" +#include "DataFormatsTPC/ZeroSuppression.h" #include "DataFormatsTRD/RecoInputContainer.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITS/TrackITS.h" @@ -149,8 +150,8 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID const auto& match = matchesITSTPCTRDTOF[i]; auto gidx = match.getTrackRef(); // this should be corresponding ITS-TPC-TRD track // no need to check isUsed: by construction this ITS-TPC-TRD was not used elsewhere - const auto& tofCl = tofClusters[match.getTOFClIndex()]; - float timeTOFMUS = (tofCl.getTime() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + // const auto& tofCl = tofClusters[match.getTOFClIndex()]; + float timeTOFMUS = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF const float timeErr = 0.010f; // assume 10 ns error FIXME if (creator(tracksITSTPCTRD[gidx.getIndex()], {i, currentSource}, timeTOFMUS, timeErr)) { flagUsed(gidx); // flag used ITS-TPC-TRD tracks @@ -169,9 +170,9 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID } for (unsigned i = 0; i < matchesTPCTRDTOF.size(); i++) { const auto& match = matchesTPCTRDTOF[i]; - auto gidx = match.getTrackRef(); // this should be corresponding TPC-TRD track - const auto& tofCl = tofClusters[match.getTOFClIndex()]; - float timeTOFMUS = (tofCl.getTime() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + auto gidx = match.getTrackRef(); // this should be corresponding TPC-TRD track + // const auto& tofCl = tofClusters[match.getTOFClIndex()]; + float timeTOFMUS = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF const float timeErr = 0.010f; // assume 10 ns error FIXME if (creator(tracksTPCTRD[gidx.getIndex()], {i, currentSource}, timeTOFMUS, timeErr)) { flagUsed(gidx); // flag used TPC-TRD tracks @@ -195,12 +196,11 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID flagUsed(trc.getRefGlobalTrackId()); // flag seeding ITS-TPC track continue; } - float t0 = t0Trig, t0Err = 5.e-3; // 5ns nominal error - if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time - t0 += trc.getPileUpTimeShiftMUS(); + float t0Err = 5.e-3; // 5ns nominal error + if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time t0Err += trc.getPileUpTimeErrorMUS(); } - if (creator(trc, {i, currentSource}, t0, t0Err)) { // assign 1ns error to BC + if (creator(trc, {i, currentSource}, t0Trig, t0Err)) { // assign 1ns error to BC flagUsed(trc.getRefGlobalTrackId()); // flag seeding ITS-TPC track } } @@ -223,8 +223,8 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID continue; } // no need to check isUsed: by construction this ITS-TPC was not used elsewhere - const auto& tofCl = tofClusters[match.getTOFClIndex()]; - float timeTOFMUS = (tofCl.getTime() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + // const auto& tofCl = tofClusters[match.getTOFClIndex()]; + float timeTOFMUS = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF const float timeErr = 0.010f; // assume 10 ns error FIXME if (creator(tracksTPCITS[gidx.getIndex()], {i, currentSource}, timeTOFMUS, timeErr)) { flagUsed(gidx); // flag used ITS-TPC tracks @@ -248,12 +248,11 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID flagUsed(trc.getRefGlobalTrackId()); // flag seeding TPC track continue; } - float t0 = t0Trig, t0Err = 5.e-3; // 5ns nominal error - if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time - t0 += trc.getPileUpTimeShiftMUS(); + float t0Err = 5.e-3; // 5ns nominal error + if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time t0Err += trc.getPileUpTimeErrorMUS(); } - if (creator(trc, {i, currentSource}, t0, t0Err)) { // assign 1ns error to BC + if (creator(trc, {i, currentSource}, t0Trig, t0Err)) { // assign 1ns error to BC flagUsed(trc.getRefGlobalTrackId()); // flag seeding TPC track } } @@ -286,7 +285,7 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID if (srcSel[currentSource]) { if (matchesTPCTOF.size() && !tracksTPCTOF.size()) { throw std::runtime_error(fmt::format("TPC-TOF matched tracks ({}) require TPCTOF matches ({}) and TPCTOF tracks ({})", - matchesTPCTOF.size(), tracksTPCTOF.size())); + -1, matchesTPCTOF.size(), tracksTPCTOF.size())); } for (unsigned i = 0; i < matchesTPCTOF.size(); i++) { const auto& match = matchesTPCTOF[i]; @@ -322,7 +321,7 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID if (srcSel[currentSource]) { if (matchesMCHMID.size() && !tracksMCH.size()) { throw std::runtime_error(fmt::format("MCH-MID matched tracks ({}) require MCHMID matches ({}) and MCH tracks ({})", - matchesMCHMID.size(), tracksMCH.size())); + -1, matchesMCHMID.size(), tracksMCH.size())); } for (unsigned i = 0; i < matchesMCHMID.size(); i++) { const auto& match = matchesMCHMID[i]; diff --git a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/TrackTuneParams.h b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/TrackTuneParams.h index be156f44f8cc3..3ca1a33325b96 100644 --- a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/TrackTuneParams.h +++ b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/TrackTuneParams.h @@ -16,6 +16,7 @@ #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" +#include namespace o2 { @@ -32,13 +33,19 @@ struct TrackTuneParams : public o2::conf::ConfigurableParamHelper getCovInnerTotal(float scale) const; + std::array getCovOuterTotal(float scale) const; O2ParamDef(TrackTuneParams, "trackTuneParams"); }; diff --git a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx index 81cfe4c00e039..141d4520229fe 100644 --- a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx +++ b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx @@ -23,16 +23,18 @@ #include "ReconstructionDataFormats/VtxTrackRef.h" #include "ReconstructionDataFormats/PrimaryVertex.h" #include "SimulationDataFormat/MCEventLabel.h" +#include "ReconstructionDataFormats/DecayNBodyIndex.h" #include "ReconstructionDataFormats/V0.h" #include "ReconstructionDataFormats/Cascade.h" -#include "ReconstructionDataFormats/DecayNbody.h" +#include "ReconstructionDataFormats/Decay3Body.h" +#include "ReconstructionDataFormats/StrangeTrack.h" #include "ReconstructionDataFormats/VtxTrackIndex.h" #include "ReconstructionDataFormats/VtxTrackRef.h" #include "ReconstructionDataFormats/TrackCosmics.h" #include "ReconstructionDataFormats/TrackMCHMID.h" #include "DataFormatsITSMFT/TrkClusRef.h" #include "DataFormatsITSMFT/TopologyDictionary.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" // FIXME: ideally, the data formats definition should be independent of the framework // collectData is using the input of ProcessingContext to extract the first valid // header and the TF orbit from it @@ -40,6 +42,10 @@ #include "Framework/DataRefUtils.h" #include "Framework/CCDBParamSpec.h" +#ifdef ENABLE_UPGRADES +#include "ITS3Reconstruction/TopologyDictionary.h" +#endif + using namespace o2::globaltracking; using namespace o2::framework; namespace o2d = o2::dataformats; @@ -117,6 +123,7 @@ void DataRequest::requestTPCTracks(bool mc) addInput({"trackTPCClRefs", "TPC", "CLUSREFS", 0, Lifetime::Timeframe}); if (requestMap.find("clusTPC") != requestMap.end()) { addInput({"clusTPCshmap", "TPC", "CLSHAREDMAP", 0, Lifetime::Timeframe}); + requestTPCOccMap(); } if (mc) { addInput({"trackTPCMCTR", "TPC", "TRACKSMCLBL", 0, Lifetime::Timeframe}); @@ -224,14 +231,29 @@ void DataRequest::requestITSClusters(bool mc) addInput({"clusITS", "ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe}); addInput({"clusITSPatt", "ITS", "PATTERNS", 0, Lifetime::Timeframe}); addInput({"clusITSROF", "ITS", "CLUSTERSROF", 0, Lifetime::Timeframe}); - addInput({"cldictITS", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")}); addInput({"alpparITS", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")}); if (mc) { addInput({"clusITSMC", "ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe}); } + addInput({"cldictITS", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")}); requestMap["clusITS"] = mc; } +#ifdef ENABLE_UPGRADES +void DataRequest::requestIT3Clusters(bool mc) +{ + addInput({"clusITS", "ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe}); + addInput({"clusITSPatt", "ITS", "PATTERNS", 0, Lifetime::Timeframe}); + addInput({"clusITSROF", "ITS", "CLUSTERSROF", 0, Lifetime::Timeframe}); + addInput({"alpparITS", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")}); + if (mc) { + addInput({"clusITSMC", "ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe}); + } + addInput({"cldictIT3", "IT3", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("IT3/Calib/ClusterDictionary")}); + requestMap["clusIT3"] = mc; +} +#endif + void DataRequest::requestMFTClusters(bool mc) { addInput({"clusMFT", "MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe}); @@ -245,11 +267,21 @@ void DataRequest::requestMFTClusters(bool mc) requestMap["clusMFT"] = mc; } +void DataRequest::requestTPCOccMap() +{ + addInput({"clusTPCoccmap", "TPC", "TPCOCCUPANCYMAP", 0, Lifetime::Timeframe}); + requestMap["TPCOcc"] = false; +} + void DataRequest::requestTPCClusters(bool mc) { addInput({"clusTPC", ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}, Lifetime::Timeframe}); + if (!getenv("DPL_DISABLE_TPC_TRIGGER_READER") || atoi(getenv("DPL_DISABLE_TPC_TRIGGER_READER")) != 1) { + requestTPCTriggers(); + } if (requestMap.find("trackTPC") != requestMap.end()) { addInput({"clusTPCshmap", "TPC", "CLSHAREDMAP", 0, Lifetime::Timeframe}); + requestTPCOccMap(); } if (mc) { addInput({"clusTPCMC", ConcreteDataTypeMatcher{"TPC", "CLNATIVEMCLBL"}, Lifetime::Timeframe}); @@ -257,6 +289,12 @@ void DataRequest::requestTPCClusters(bool mc) requestMap["clusTPC"] = mc; } +void DataRequest::requestTPCTriggers() +{ + addInput({"trigTPC", "TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe}); + requestMap["trigTPC"] = false; +} + void DataRequest::requestTOFClusters(bool mc) { addInput({"tofcluster", "TOF", "CLUSTERS", 0, Lifetime::Timeframe}); @@ -367,7 +405,7 @@ void DataRequest::requestCoscmicTracks(bool mc) requestMap["Cosmics"] = mc; } -void DataRequest::requestPrimaryVertertices(bool mc) +void DataRequest::requestPrimaryVertices(bool mc) { addInput({"pvtx", "GLO", "PVTX", 0, Lifetime::Timeframe}); addInput({"pvtx_trmtc", "GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe}); // global ids of associated tracks @@ -378,7 +416,7 @@ void DataRequest::requestPrimaryVertertices(bool mc) requestMap["PVertex"] = mc; } -void DataRequest::requestPrimaryVerterticesTMP(bool mc) // primary vertices before global vertex-track matching +void DataRequest::requestPrimaryVerticesTMP(bool mc) // primary vertices before global vertex-track matching { addInput({"pvtx", "GLO", "PVTX", 0, Lifetime::Timeframe}); addInput({"pvtx_cont", "GLO", "PVTX_CONTID", 0, Lifetime::Timeframe}); // global ids of contributors @@ -389,17 +427,29 @@ void DataRequest::requestPrimaryVerterticesTMP(bool mc) // primary vertices befo requestMap["PVertexTMP"] = mc; } -void DataRequest::requestSecondaryVertertices(bool) +void DataRequest::requestSecondaryVertices(bool) { + addInput({"v0sIdx", "GLO", "V0S_IDX", 0, Lifetime::Timeframe}); addInput({"v0s", "GLO", "V0S", 0, Lifetime::Timeframe}); addInput({"p2v0s", "GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe}); + addInput({"cascsIdx", "GLO", "CASCS_IDX", 0, Lifetime::Timeframe}); addInput({"cascs", "GLO", "CASCS", 0, Lifetime::Timeframe}); addInput({"p2cascs", "GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe}); + addInput({"decay3bodyIdx", "GLO", "DECAYS3BODY_IDX", 0, Lifetime::Timeframe}); addInput({"decay3body", "GLO", "DECAYS3BODY", 0, Lifetime::Timeframe}); addInput({"p2decay3body", "GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe}); requestMap["SVertex"] = false; // no MC provided for secondary vertices } +void DataRequest::requestStrangeTracks(bool mc) +{ + addInput({"strangetracks", "GLO", "STRANGETRACKS", 0, Lifetime::Timeframe}); + if (mc) { + addInput({"strack_mc", "GLO", "STRANGETRACKS_MC", 0, Lifetime::Timeframe}); + } + requestMap["STracker"] = mc; // no MC for the time being +} + void DataRequest::requestCTPDigits(bool mc) { addInput({"CTPDigits", "CTP", "DIGITS", 0, Lifetime::Timeframe}); @@ -441,18 +491,14 @@ void DataRequest::requestEMCALCells(bool mc) requestMap["EMCCells"] = mc; } -/* void DataRequest::requestHMPMatches(bool mc) { addInput({"matchHMP", "HMP", "MATCHES", 0, Lifetime::Timeframe}); - addInput({"matchTriggerHMP", "HMP", "TRACKREFS", 0, Lifetime::Timeframe}); - addInput({"matchPhotsCharge", "HMP", "PATTERNS", 0, Lifetime::Timeframe}); if (mc) { addInput({"clsHMP_GLO_MCTR", "HMP", "MCLABELS", 0, Lifetime::Timeframe}); } requestMap["matchHMP"] = mc; } -*/ void DataRequest::requestTracks(GTrackID::mask_t src, bool useMC) { @@ -521,9 +567,9 @@ void DataRequest::requestTracks(GTrackID::mask_t src, bool useMC) if (GTrackID::includesDet(DetID::HMP, src)) { requestHMPClusters(useMC); } - // if (src[GTrackID::HMP]) { - // requestHMPMatches(useMC); - // } + if (src[GTrackID::HMP]) { + requestHMPMatches(useMC); + } } void DataRequest::requestClusters(GTrackID::mask_t src, bool useMC, DetID::mask_t skipDetClusters) @@ -652,14 +698,34 @@ void RecoContainer::collectData(ProcessingContext& pc, const DataRequest& reques addITSClusters(pc, req->second); } +#ifdef ENABLE_UPGRADES + req = reqMap.find("clusIT3"); + if (req != reqMap.end()) { + addIT3Clusters(pc, req->second); + } +#endif + req = reqMap.find("clusMFT"); if (req != reqMap.end()) { addMFTClusters(pc, req->second); } + req = reqMap.find("TPCOcc"); + bool TPCOccDone = false; + if (req != reqMap.end()) { + TPCOccDone = true; + addTPCOccMap(pc); + } + req = reqMap.find("clusTPC"); if (req != reqMap.end()) { - addTPCClusters(pc, req->second, reqMap.find("trackTPC") != reqMap.end()); + auto tracksON = reqMap.find("trackTPC") != reqMap.end(); + addTPCClusters(pc, req->second, tracksON, tracksON && (!TPCOccDone)); + } + + req = reqMap.find("trigTPC"); + if (req != reqMap.end()) { + addTPCTriggers(pc); } req = reqMap.find("clusTOF"); @@ -747,24 +813,32 @@ void RecoContainer::collectData(ProcessingContext& pc, const DataRequest& reques addSVertices(pc, req->second); } + req = reqMap.find("STracker"); + if (req != reqMap.end()) { + addStrangeTracks(pc, req->second); + } + req = reqMap.find("IRFramesITS"); if (req != reqMap.end()) { addIRFramesITS(pc); } - // req = reqMap.find("matchHMP"); - // if (req != reqMap.end()) { - // addHMPMatches(pc, req->second); - // } + req = reqMap.find("matchHMP"); + if (req != reqMap.end()) { + addHMPMatches(pc, req->second); + } } //____________________________________________________________ void RecoContainer::addSVertices(ProcessingContext& pc, bool) { + svtxPool.registerContainer(pc.inputs().get>("v0sIdx"), V0SIDX); svtxPool.registerContainer(pc.inputs().get>("v0s"), V0S); svtxPool.registerContainer(pc.inputs().get>>("p2v0s"), PVTX_V0REFS); + svtxPool.registerContainer(pc.inputs().get>("cascsIdx"), CASCSIDX); svtxPool.registerContainer(pc.inputs().get>("cascs"), CASCS); svtxPool.registerContainer(pc.inputs().get>>("p2cascs"), PVTX_CASCREFS); - svtxPool.registerContainer(pc.inputs().get>("decay3body"), DECAY3BODY); + svtxPool.registerContainer(pc.inputs().get>("decay3bodyIdx"), DECAY3BODYIDX); + svtxPool.registerContainer(pc.inputs().get>("decay3body"), DECAY3BODY); svtxPool.registerContainer(pc.inputs().get>>("p2decay3body"), PVTX_3BODYREFS); // no mc } @@ -772,27 +846,32 @@ void RecoContainer::addSVertices(ProcessingContext& pc, bool) //____________________________________________________________ void RecoContainer::addPVertices(ProcessingContext& pc, bool mc) { - if (!pvtxPool.isLoaded(PVTX)) { // in case was loaded via addPVerticesTMP - pvtxPool.registerContainer(pc.inputs().get>("pvtx"), PVTX); - } + pvtxPool.registerContainer(pc.inputs().get>("pvtx"), PVTX); pvtxPool.registerContainer(pc.inputs().get>("pvtx_trmtc"), PVTX_TRMTC); pvtxPool.registerContainer(pc.inputs().get>("pvtx_tref"), PVTX_TRMTCREFS); - if (mc && !pvtxPool.isLoaded(PVTX_MCTR)) { // in case was loaded via addPVerticesTMP + if (mc) { // in case was loaded via addPVerticesTMP pvtxPool.registerContainer(pc.inputs().get>("pvtx_mc"), PVTX_MCTR); } } //____________________________________________________________ -void RecoContainer::addPVerticesTMP(ProcessingContext& pc, bool mc) +void RecoContainer::addStrangeTracks(ProcessingContext& pc, bool mc) { - if (!pvtxPool.isLoaded(PVTX)) { // in case was loaded via addPVertices - pvtxPool.registerContainer(pc.inputs().get>("pvtx"), PVTX); + strkPool.registerContainer(pc.inputs().get>("strangetracks"), STRACK); + if (mc) { + strkPool.registerContainer(pc.inputs().get>("strack_mc"), STRACK_MC); } +} + +//____________________________________________________________ +void RecoContainer::addPVerticesTMP(ProcessingContext& pc, bool mc) +{ + pvtxPool.registerContainer(pc.inputs().get>("pvtx"), PVTX); pvtxPool.registerContainer(pc.inputs().get>("pvtx_cont"), PVTX_CONTID); pvtxPool.registerContainer(pc.inputs().get>("pvtx_contref"), PVTX_CONTIDREFS); - if (mc && !pvtxPool.isLoaded(PVTX_MCTR)) { // in case was loaded via addPVertices + if (mc) { // in case was loaded via addPVertices pvtxPool.registerContainer(pc.inputs().get>("pvtx_mc"), PVTX_MCTR); } } @@ -979,18 +1058,14 @@ void RecoContainer::addTOFMatchesITSTPCTRD(ProcessingContext& pc, bool mc) } } -/* //__________________________________________________________ void RecoContainer::addHMPMatches(ProcessingContext& pc, bool mc) { - commonPool[GTrackID::HMP].registerContainer(pc.inputs().get>("matchHMP"), MATCHES); // HMPID match info, no real tracks - commonPool[GTrackID::HMP].registerContainer(pc.inputs().get>("matchTriggerHMP"), TRACKREFS); // HMPID triggers - commonPool[GTrackID::HMP].registerContainer(pc.inputs().get>("matchPhotsCharge"), PATTERNS); // HMPID photon cluster charges + commonPool[GTrackID::HMP].registerContainer(pc.inputs().get>("matchHMP"), MATCHES); // HMPID match info, no real tracks if (mc) { commonPool[GTrackID::HMP].registerContainer(pc.inputs().get>("clsHMP_GLO_MCTR"), MCLABELS); } } -*/ //__________________________________________________________ void RecoContainer::addITSClusters(ProcessingContext& pc, bool mc) @@ -1007,6 +1082,22 @@ void RecoContainer::addITSClusters(ProcessingContext& pc, bool mc) } } +#ifdef ENABLE_UPGRADES +void RecoContainer::addIT3Clusters(ProcessingContext& pc, bool mc) +{ + if (pc.services().get().globalRunNumberChanged) { // this params need to be queried only once + pc.inputs().get*>("alpparITS"); // note: configurable param does not need finaliseCCDB + pc.inputs().get("cldictIT3"); // just to trigger the finaliseCCDB + } + commonPool[GTrackID::ITS].registerContainer(pc.inputs().get>("clusITSROF"), CLUSREFS); + commonPool[GTrackID::ITS].registerContainer(pc.inputs().get>("clusITS"), CLUSTERS); + commonPool[GTrackID::ITS].registerContainer(pc.inputs().get>("clusITSPatt"), PATTERNS); + if (mc) { + mcITSClusters = pc.inputs().get*>("clusITSMC"); + } +} +#endif + //__________________________________________________________ void RecoContainer::addMFTClusters(ProcessingContext& pc, bool mc) { @@ -1023,12 +1114,27 @@ void RecoContainer::addMFTClusters(ProcessingContext& pc, bool mc) } //__________________________________________________________ -void RecoContainer::addTPCClusters(ProcessingContext& pc, bool mc, bool shmap) +void RecoContainer::addTPCOccMap(ProcessingContext& pc) +{ + occupancyMapTPC = pc.inputs().get>("clusTPCoccmap"); +} + +//__________________________________________________________ +void RecoContainer::addTPCClusters(ProcessingContext& pc, bool mc, bool shmap, bool occmap) { inputsTPCclusters = o2::tpc::getWorkflowTPCInput(pc, 0, mc); if (shmap) { clusterShMapTPC = pc.inputs().get>("clusTPCshmap"); } + if (occmap) { + occupancyMapTPC = pc.inputs().get>("clusTPCoccmap"); + } +} + +//__________________________________________________________ +void RecoContainer::addTPCTriggers(ProcessingContext& pc) +{ + commonPool[GTrackID::TPC].registerContainer(pc.inputs().get>("trigTPC"), MATCHES); } //__________________________________________________________ @@ -1079,7 +1185,9 @@ void RecoContainer::addMIDClusters(ProcessingContext& pc, bool mc) void RecoContainer::addCTPDigits(ProcessingContext& pc, bool mc) { commonPool[GTrackID::CTP].registerContainer(pc.inputs().get>("CTPDigits"), CLUSTERS); - mCTPLumi = pc.inputs().get("CTPLumi"); + if (pc.inputs().get>("CTPLumi").size() == sizeof(o2::ctp::LumiInfo)) { + mCTPLumi = pc.inputs().get("CTPLumi"); + } if (mc) { // pc.inputs().get*>("CTPDigitsMC"); } @@ -1168,12 +1276,12 @@ const o2::tpc::ClusterNativeAccess& RecoContainer::getTPCClusters() const gsl::span RecoContainer::getTRDTracklets() const { - return inputsTRD->mTracklets; + return inputsTRD ? inputsTRD->mTracklets : gsl::span(); } gsl::span RecoContainer::getTRDCalibratedTracklets() const { - return inputsTRD->mSpacePoints; + return inputsTRD ? inputsTRD->mSpacePoints : gsl::span(); } gsl::span RecoContainer::getTRDTriggerRecords() const @@ -1192,7 +1300,7 @@ gsl::span RecoContainer::getTRDTriggerRecords() co const o2::dataformats::MCTruthContainer* RecoContainer::getTRDTrackletsMCLabels() const { - return inputsTRD->mTrackletLabels.get(); + return inputsTRD ? inputsTRD->mTrackletLabels.get() : nullptr; } const o2::dataformats::ConstMCTruthContainerView* RecoContainer::getTPCClustersMCLabels() const @@ -1332,8 +1440,7 @@ RecoContainer::GlobalIDSet RecoContainer::getSingleDetectorRefs(GTrackID gidx) c table[GTrackID::TRD] = parent0.getTrackRef(); // there is no standalone TRD track, so use the index for the ITSTPCTRD track array } else if (src == GTrackID::TPCTRDTOF) { const auto& parent0 = getTOFMatch(gidx); // TPCTRD : TOF - const auto& parent1 = getITSTPCTRDTrack(parent0.getTrackRef()); - const auto& parent2 = getTPCITSTrack(parent1.getRefGlobalTrackId()); + const auto& parent1 = getTPCTRDTrack(parent0.getTrackRef()); table[GTrackID::TPCTRD] = parent0.getTrackRef(); table[GTrackID::TPC] = parent1.getRefGlobalTrackId(); table[GTrackID::TOF] = {unsigned(parent0.getIdxTOFCl()), GTrackID::TOF}; @@ -1358,7 +1465,7 @@ RecoContainer::GlobalIDSet RecoContainer::getSingleDetectorRefs(GTrackID gidx) c table[GTrackID::MCH] = parent0.getMCHRef(); table[GTrackID::MID] = parent0.getMIDRef(); } - return std::move(table); + return table; } //________________________________________________________ @@ -1383,6 +1490,10 @@ GTrackID RecoContainer::getTPCContributorGID(GTrackID gidx) const } else if (src == GTrackID::TPCTOF) { const auto& parent0 = getTPCTOFMatch(gidx); // TPC : TOF return parent0.getTrackRef(); + } else if (src == GTrackID::TPCTRDTOF) { + const auto& parent0 = getTOFMatch(gidx); // TPC/TRD : TOF + const auto& parent1 = getTPCTRDTrack(parent0.getTrackRef()); + return parent1.getRefGlobalTrackId(); } else if (src == GTrackID::TPCTRD) { const auto& parent0 = getTPCTRDTrack(gidx); return parent0.getRefGlobalTrackId(); @@ -1430,3 +1541,133 @@ const o2::dataformats::MCTruthContainer* RecoContainer::getE { return mcEMCCells.get(); } + +//________________________________________________________ +void RecoContainer::getTrackTimeITSTPCTRDTOF(GTrackID gid, float& t, float& tErr) const +{ + const auto& match = getITSTPCTRDTOFMatches()[gid]; + t = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + tErr = 0.010f; +} + +//________________________________________________________ +void RecoContainer::getTrackTimeTPCTRDTOF(GTrackID gid, float& t, float& tErr) const +{ + const auto& match = getTPCTRDTOFMatches()[gid]; + t = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + tErr = 0.010f; +} + +//________________________________________________________ +void RecoContainer::getTrackTimeITSTPCTOF(GTrackID gid, float& t, float& tErr) const +{ + const auto& match = getITSTPCTOFMatches()[gid]; + t = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + tErr = 0.010f; +} + +//________________________________________________________ +void RecoContainer::getTrackTimeITSTPCTRD(GTrackID gid, float& t, float& tErr) const +{ + const auto trigITSTPCTRD = getITSTPCTRDTriggers(); + // very slow: find the trigger this track belongs to + for (const auto& trig : trigITSTPCTRD) { + if (trig.getTrackRefs().getEntriesBound() > gid.getIndex()) { + t = trig.getBCData().differenceInBC(startIR) * o2::constants::lhc::LHCBunchSpacingMUS; + tErr = 5.e-3; + const auto& trc = getITSTPCTRDTracks()[gid]; + if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time + tErr += trc.getPileUpTimeErrorMUS(); + } + return; + } + } +} + +//________________________________________________________ +void RecoContainer::getTrackTimeTPCTRD(GTrackID gid, float& t, float& tErr) const +{ + const auto trigTPCTRD = getTPCTRDTriggers(); + // very slow: find the trigger this track belongs to + for (const auto& trig : trigTPCTRD) { + if (trig.getTrackRefs().getEntriesBound() > gid.getIndex()) { + t = trig.getBCData().differenceInBC(startIR) * o2::constants::lhc::LHCBunchSpacingMUS; + tErr = 5.e-3; + const auto& trc = getTPCTRDTracks()[gid]; + if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time + tErr += trc.getPileUpTimeErrorMUS(); + } + return; + } + } +} + +//________________________________________________________ +void RecoContainer::getTrackTimeITSTPC(GTrackID gid, float& t, float& tErr) const +{ + const auto& trc = getTPCITSTrack(gid); + t = trc.getTimeMUS().getTimeStamp(); + tErr = trc.getTimeMUS().getTimeStampError(); +} + +//________________________________________________________ +void RecoContainer::getTrackTimeTPCTOF(GTrackID gid, float& t, float& tErr) const +{ + const auto& trc = getTPCTOFTrack(gid); + t = trc.getTimeMUS().getTimeStamp(); + tErr = trc.getTimeMUS().getTimeStampError(); +} + +//________________________________________________________ +void RecoContainer::getTrackTimeITS(GTrackID gid, float& t, float& tErr) const +{ + const auto& rofrs = getITSTracksROFRecords(); + for (const auto& rof : rofrs) { // slow + if (gid.getIndex() < rof.getFirstEntry() + rof.getNEntries()) { + t = rof.getBCData().differenceInBC(startIR) * o2::constants::lhc::LHCBunchSpacingMUS; + tErr = 0.5; + return; + } + } +} + +//________________________________________________________ +void RecoContainer::getTrackTimeTPC(GTrackID gid, float& t, float& tErr) const +{ + const auto& trc = getTPCTrack(gid); + t = trc.getTime0() + 0.5 * (trc.getDeltaTFwd() - trc.getDeltaTBwd()); + tErr = 0.5 * (trc.getDeltaTFwd() + trc.getDeltaTBwd()); +} + +//________________________________________________________ +void RecoContainer::getTrackTime(GTrackID gid, float& t, float& tErr) const +{ + auto src = gid.getSource(); + if (src == GTrackID::ITSTPCTRDTOF) { + return getTrackTimeITSTPCTRDTOF(gid, t, tErr); + } + if (src == GTrackID::TPCTRDTOF) { + return getTrackTimeTPCTRDTOF(gid, t, tErr); + } + if (src == GTrackID::ITSTPCTOF) { + return getTrackTimeITSTPCTOF(gid, t, tErr); + } + if (src == GTrackID::ITSTPCTRD) { + return getTrackTimeITSTPCTRD(gid, t, tErr); + } + if (src == GTrackID::TPCTRD) { + return getTrackTimeTPCTRD(gid, t, tErr); + } + if (src == GTrackID::ITSTPC) { + return getTrackTimeITSTPC(gid, t, tErr); + } + if (src == GTrackID::TPCTOF) { + return getTrackTimeTPCTOF(gid, t, tErr); + } + if (src == GTrackID::ITS) { + return getTrackTimeITS(gid, t, tErr); + } + if (src == GTrackID::TPC) { + return getTrackTimeTPC(gid, t, tErr); + } +} diff --git a/DataFormats/Detectors/GlobalTracking/src/TrackTuneParams.cxx b/DataFormats/Detectors/GlobalTracking/src/TrackTuneParams.cxx index ee5dfc954ad7e..138f91937b250 100644 --- a/DataFormats/Detectors/GlobalTracking/src/TrackTuneParams.cxx +++ b/DataFormats/Detectors/GlobalTracking/src/TrackTuneParams.cxx @@ -15,3 +15,25 @@ #include "DataFormatsGlobalTracking/TrackTuneParams.h" O2ParamImpl(o2::globaltracking::TrackTuneParams); + +using namespace o2::globaltracking; + +std::array TrackTuneParams::getCovInnerTotal(float scale) const +{ + std::array cov{}; + for (int i = 0; i < 5; i++) { + cov[i] = tpcCovInner[i] + scale * tpcCovInnerSlope[i]; + cov[i] *= cov[i]; + } + return cov; +} + +std::array TrackTuneParams::getCovOuterTotal(float scale) const +{ + std::array cov{}; + for (int i = 0; i < 5; i++) { + cov[i] = tpcCovOuter[i] + scale * tpcCovOuterSlope[i]; + cov[i] *= cov[i]; + } + return cov; +} diff --git a/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Cluster.h b/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Cluster.h index 3649c13fffb8d..9c777d5afb718 100644 --- a/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Cluster.h +++ b/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Cluster.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -40,18 +40,22 @@ class Cluster kEmp = -1 }; // status flags public: - Cluster() : mCh(-1), mSi(-1), mSt(kEmp), mBox(-1), mNlocMax(-1), mMaxQpad(-1), mMaxQ(-1), mQRaw(0), mQ(0), mErrQ(-1), mXX(0), mErrX(-1), mYY(0), mErrY(-1), mChi2(-1), mParam(o2::hmpid::Param::instanceNoGeo()) { mDigs.clear(); }; - // Cluster(int chamber, int size, int NlocMax, float QRaw, float Q, float X, float Y) - // : mCh(chamber), mSi(size), mNlocMax(NlocMax), mQRaw(QRaw), mQ(Q), mXX(X), mYY(Y) { }; + Cluster() : mCh(-1), mSi(-1), mSt(kEmp), mBox(-1), mNlocMax(-1), mMaxQpad(-1), mMaxQ(-1), mQRaw(0), mQ(0), mErrQ(-1), mXX(0), mErrX(-1), mYY(0), mErrY(-1), mChi2(-1) {} // Methods // void draw(Option_t *opt=""); //overloaded TObject::Print() to draw cluster in current canvas void print(Option_t* opt = "") const; // overloaded TObject::Print() to print cluster info static void fitFunc(int& iNpars, double* deriv, double& chi2, double* par, int iflag); // fit function to be used by MINUIT + void cleanPointers() + { + mDigs = nullptr; + } void coG(); // calculates center of gravity void corrSin(); // sinoidal correction void digAdd(const o2::hmpid::Digit* pDig); // add new digit to the cluster - const o2::hmpid::Digit* dig(int i) const { return mDigs[i]; } // pointer to i-th digi + const o2::hmpid::Digit* dig(int i) const { return mDigs ? (*mDigs)[i] : nullptr; } // pointer to i-th digi + const std::vector* getDigits() const { return mDigs; } + void setDigits(std::vector* v = nullptr) { mDigs = v; } inline bool isInPc(); // check if is in the current PC void reset(); // cleans the cluster // void setClusterParams(float xL, float yL, int iCh); //Set AliCluster3D part @@ -95,26 +99,25 @@ class Cluster // public: protected: - int mCh; // chamber number - int mSi; // size of the formed cluster from which this cluster deduced - int mSt; // flag to mark the quality of the cluster - int mBox; // box contaning this cluster - int mNlocMax; // number of local maxima in formed cluster - int mMaxQpad; // abs pad number of a pad with the highest charge - double mMaxQ; // that max charge value - double mQRaw; // QDC value of the raw cluster - double mQ; // QDC value of the actual cluster - double mErrQ; // error on Q - double mXX; // local x postion, [cm] - double mErrX; // error on x postion, [cm] - double mYY; // local y postion, [cm] - double mErrY; // error on y postion, [cm] - double mChi2; // some estimator of the fit quality - std::vector mDigs; //! list of digits forming this cluster + int mCh; // chamber number + int mSi; // size of the formed cluster from which this cluster deduced + int mSt; // flag to mark the quality of the cluster + int mBox; // box contaning this cluster + int mNlocMax; // number of local maxima in formed cluster + int mMaxQpad; // abs pad number of a pad with the highest charge + double mMaxQ; // that max charge value + double mQRaw; // QDC value of the raw cluster + double mQ; // QDC value of the actual cluster + double mErrQ; // error on Q + double mXX; // local x postion, [cm] + double mErrX; // error on x postion, [cm] + double mYY; // local y postion, [cm] + double mErrY; // error on y postion, [cm] + double mChi2; // some estimator of the fit quality + std::vector* mDigs = nullptr; //! list of digits forming this cluster public: static bool fgDoCorrSin; // flag to switch on/off correction for Sinusoidal to cluster reco - Param* mParam; //! Pointer to AliHMPIDParam ClassDefNV(Cluster, 3); }; diff --git a/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Digit.h b/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Digit.h index b58d12ff6a259..63b04d454e66a 100644 --- a/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Digit.h +++ b/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Digit.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -9,7 +9,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// /// \file Digit.h /// \author Antonio Franco - INFN Bari /// \version 1.0 diff --git a/DataFormats/Detectors/HMPID/src/Cluster.cxx b/DataFormats/Detectors/HMPID/src/Cluster.cxx index 56017ef1cd114..e14075c16c842 100644 --- a/DataFormats/Detectors/HMPID/src/Cluster.cxx +++ b/DataFormats/Detectors/HMPID/src/Cluster.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -12,6 +12,7 @@ #include #include #include "DataFormatsHMP/Cluster.h" +#include "Framework/Logger.h" #include #include #include @@ -24,84 +25,20 @@ namespace hmpid bool o2::hmpid::Cluster::fgDoCorrSin = true; -/*Cluster::Cluster() -{ - mCh = -1; - mSi = -1; - mSt = kEmp; - mBox = -1; - mNlocMax = -1; - mMaxQpad = -1; - mMaxQ = -1; - mQRaw = 0; - mQ = 0; - mErrQ = -1; - mXX = 0; - mErrX = -1; - mYY = 0; - mErrY = -1; - mChi2 = -1; - mDigs.clear(); - - // delete [] &mDigs; - mParam = (o2::hmpid::Param::instanceNoGeo()); - // mParam = (o2::hmpid::Param()); -}; -*/ -/*Cluster::~Cluster() -{ - delete mDigs; -} -*/ -/*void Cluster::setClusterParams(float xL,float yL,int iCh) -{ - //------------------------------------------------------------------------ - //Set the cluster properties for the AliCluster3D part - //------------------------------------------------------------------------ - mParam = o2::hmpid::Param::instance(); - if(!mParam->getInstType()) { //if there is no geometry we cannot retrieve the volId (only for monitoring) - // new(this) AliCluster3D(); return; - } - //Get the volume ID from the previously set PNEntry - uint16_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kHMPID, iCh); - - //get L->T cs matrix for a given chamber - const TGeoHMatrix *t2l = AliGeomManager::GetTracking2LocalMatrix(volId); - mParam = o2::hmpid::Param::instance(); - //transformation from the pad cs to local - xL -= 0.5 * mParam->sizeAllX(); //size of all pads with dead zones included - yL -= 0.5 * mParam->sizeAllY(); - - // Get the position in the tracking cs - float posL[3]={xL, yL, 0.}; //this is the LORS of HMPID - float posT[3]; - t2l->MasterToLocal(posL,posT); - - //Get the cluster covariance matrix in the tracking cs - float covL[9] = { 0.8 * 0.8 / 12.0, 0.0, 0.0, //pad size X - 0.0, 0.84 * 0.84 / 12.0, 0.0, //pad size Y - 0.0, 0.0, 0.1 }; //just 1 , no Z dimension ??? - - TGeoHMatrix m; - m.SetRotation(covL); - m.Multiply(t2l); - const TGeoHMatrix& t2li = t2l->Inverse(); - m.MultiplyLeft(&t2li); - float *covT = m.GetRotationMatrix(); - - // ===> new(this) AliCluster3D(volId,posT[0],posT[1],posT[2],covT[0],covT[1],covT[2],covT[4],covT[5],covT[8],0x0); // No MC labels ? -} -*/ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Calculates naive cluster position as a center of gravity of its digits. void Cluster::coG() { + if (!mDigs) { + LOGP(fatal, "digits are missing in the cluster"); + } + int minPadX = 999; int minPadY = 999; int maxPadX = -1; int maxPadY = -1; // for box finding - if (mDigs.size() == 0) { + if (mDigs->size() == 0) { return; } // no digits in this cluster mXX = mYY = mQRaw = 0; // init summable parameters @@ -110,9 +47,9 @@ void Cluster::coG() int maxQ = -1; // to calculate the pad with the highest charge o2::hmpid::Digit* pDig = nullptr; - for (int iDig = 0; iDig < mDigs.size(); iDig++) { // digits loop + for (int iDig = 0; iDig < mDigs->size(); iDig++) { // digits loop int x, y, mod; - int padId = mDigs[iDig]->getPadID(); + int padId = (*mDigs)[iDig]->getPadID(); o2::hmpid::Digit::pad2Absolute(padId, &mod, &x, &y); if (x > maxPadX) { maxPadX = x; @@ -127,7 +64,7 @@ void Cluster::coG() minPadY = y; } // MinY - float q = mDigs[iDig]->mQ; // get QDC + float q = (*mDigs)[iDig]->mQ; // get QDC mXX += o2::hmpid::Digit::lorsX(padId) * q; mYY += o2::hmpid::Digit::lorsY(padId) * q; // add digit center weighted by QDC mQRaw += q; // increment total charge @@ -144,7 +81,7 @@ void Cluster::coG() mYY /= mQRaw; } // final center of gravity - if (mDigs.size() > 1 && fgDoCorrSin) { + if (mDigs->size() > 1 && fgDoCorrSin) { corrSin(); } // correct it by sinoid @@ -162,12 +99,14 @@ void Cluster::coG() // Correction of cluster x position due to sinoid, see HMPID TDR page 30 void Cluster::corrSin() { + const auto param = o2::hmpid::Param::instanceNoGeo(); int pc; int px; int py; o2::hmpid::Param::lors2Pad(mXX, mYY, pc, px, py); // tmp digit to get it center - double x = mXX - mParam->lorsX(pc, px); // diff between cluster x and center of the pad contaning this cluster - mXX += 3.31267e-2 * sin(2 * M_PI / 0.8 * x) - 2.66575e-3 * sin(4 * M_PI / 0.8 * x) + 2.80553e-3 * sin(6 * M_PI / 0.8 * x) + 0.0070; + double x = mXX - param->lorsX(pc, px); // diff between cluster x and center of the pad contaning this cluster + double xpi8on10 = M_PI / 0.8 * x; + mXX += 3.31267e-2 * sin(2.0 * xpi8on10) - 2.66575e-3 * sin(4.0 * xpi8on10) + 2.80553e-3 * sin(6.0 * xpi8on10) + 0.0070; return; } @@ -197,8 +136,11 @@ void Cluster::fitFunc(int& iNpars, double* deriv, double& chi2, double* par, int for (int i = 0; i < nPads; i++) { // loop on all pads of the cluster double dQpadMath = 0; for (int j = 0; j < iNshape; j++) { // Mathiesons loop as all of them may contribute to this pad - double fracMathi = o2::hmpid::Digit::intMathieson(par[3 * j], par[3 * j + 1], pClu->dig(i)->getPadID()); - dQpadMath += par[3 * j + 2] * fracMathi; // par[3*j+2] is charge par[3*j] is x par[3*j+1] is y of current Mathieson + int baseOff = 3 * j; + int baseOff1 = baseOff + 1; + int baseOff2 = baseOff + 2; + double fracMathi = o2::hmpid::Digit::intMathieson(par[baseOff], par[baseOff1], pClu->dig(i)->getPadID()); + dQpadMath += par[baseOff2] * fracMathi; // par[3*j+2] is charge par[3*j] is x par[3*j+1] is y of current Mathieson } if (dQpadMath > 0 && pClu->dig(i)->mQ > 0) { chi2 += std::pow((pClu->dig(i)->mQ - dQpadMath), 2.0) / pClu->dig(i)->mQ; // chi2 function to be minimized @@ -206,45 +148,46 @@ void Cluster::fitFunc(int& iNpars, double* deriv, double& chi2, double* par, int } //---calculate gradients... if (iflag == 2) { - float** derivPart; - derivPart = new float*[iNpars]; - for (int j = 0; j < iNpars; j++) { - deriv[j] = 0; - derivPart[j] = new float[nPads]; - for (int i = 0; i < nPads; i++) { - derivPart[j][i] = 0; - } - } - for (int i = 0; i < nPads; i++) { // loop on all pads of the cluster + std::vector> derivPart(iNpars, std::vector(nPads, 0.0f)); + double dHalfPadX = o2::hmpid::Param::sizeHalfPadX(); + double dHalfPadY = o2::hmpid::Param::sizeHalfPadY(); + + for (int i = 0; i < nPads; i++) { // loop on all pads of the cluster + int iPadId = pClu->dig(i)->getPadID(); + double lx = o2::hmpid::Digit::lorsX(iPadId); + double ly = o2::hmpid::Digit::lorsY(iPadId); for (int j = 0; j < iNshape; j++) { // Mathiesons loop as all of them may contribute to this pad - double fracMathi = o2::hmpid::Digit::intMathieson(par[3 * j], par[3 * j + 1], pClu->dig(i)->getPadID()); - double lx = o2::hmpid::Digit::lorsX(pClu->dig(i)->getPadID()); - double ly = o2::hmpid::Digit::lorsY(pClu->dig(i)->getPadID()); - derivPart[3 * j][i] += par[3 * j + 2] * (o2::hmpid::Digit::mathiesonX(par[3 * j] - lx - 0.5 * o2::hmpid::Param::sizePadX()) - o2::hmpid::Digit::mathiesonX(par[3 * j] - lx + 0.5 * o2::hmpid::Param::sizePadX())) * - o2::hmpid::Digit::intPartMathiY(par[3 * j + 1], pClu->dig(i)->getPadID()); - derivPart[3 * j + 1][i] += par[3 * j + 2] * (o2::hmpid::Digit::mathiesonY(par[3 * j + 1] - ly - 0.5 * o2::hmpid::Param::sizePadY()) - o2::hmpid::Digit::mathiesonY(par[3 * j + 1] - ly + 0.5 * o2::hmpid::Param::sizePadY())) * - o2::hmpid::Digit::intPartMathiX(par[3 * j], pClu->dig(i)->getPadID()); - derivPart[3 * j + 2][i] += fracMathi; + int baseOff = 3 * j; + int baseOff1 = baseOff + 1; + int baseOff2 = baseOff + 2; + double fracMathi = o2::hmpid::Digit::intMathieson(par[baseOff], par[baseOff1], iPadId); + derivPart[baseOff][i] += par[baseOff2] * (o2::hmpid::Digit::mathiesonX(par[baseOff] - lx - dHalfPadX) - o2::hmpid::Digit::mathiesonX(par[baseOff] - lx + dHalfPadX)) * + o2::hmpid::Digit::intPartMathiY(par[baseOff1], iPadId); + derivPart[baseOff1][i] += par[baseOff2] * (o2::hmpid::Digit::mathiesonY(par[baseOff1] - ly - dHalfPadY) - o2::hmpid::Digit::mathiesonY(par[baseOff1] - ly + dHalfPadY)) * + o2::hmpid::Digit::intPartMathiX(par[baseOff], iPadId); + derivPart[baseOff2][i] += fracMathi; } } // loop on all pads of the cluster - for (int i = 0; i < nPads; i++) { // loop on all pads of the cluster - double dQpadMath = 0; // pad charge collector + for (int i = 0; i < nPads; i++) { // loop on all pads of the cluster + int iPadId = pClu->dig(i)->getPadID(); + double dPadmQ = pClu->dig(i)->mQ; + double dQpadMath = 0.0; // pad charge collector + double twoOverMq = 2.0 / dPadmQ; for (int j = 0; j < iNshape; j++) { // Mathiesons loop as all of them may contribute to this pad - float fracMathi = o2::hmpid::Digit::intMathieson(par[3 * j], par[3 * j + 1], pClu->dig(i)->getPadID()); - dQpadMath += par[3 * j + 2] * fracMathi; - if (dQpadMath > 0 && pClu->dig(i)->mQ > 0) { - deriv[3 * j] += 2 / pClu->dig(i)->mQ * (pClu->dig(i)->mQ - dQpadMath) * derivPart[3 * j][i]; - deriv[3 * j + 1] += 2 / pClu->dig(i)->mQ * (pClu->dig(i)->mQ - dQpadMath) * derivPart[3 * j + 1][i]; - deriv[3 * j + 2] += 2 / pClu->dig(i)->mQ * (pClu->dig(i)->mQ - dQpadMath) * derivPart[3 * j + 2][i]; + int baseOff = 3 * j; + int baseOff1 = baseOff + 1; + int baseOff2 = baseOff + 2; + double fracMathi = o2::hmpid::Digit::intMathieson(par[baseOff], par[baseOff1], iPadId); + dQpadMath += par[baseOff2] * fracMathi; + if (dQpadMath > 0 && dPadmQ > 0) { + double appoggio = twoOverMq * (dPadmQ - dQpadMath); + deriv[baseOff] += appoggio * derivPart[baseOff][i]; + deriv[baseOff1] += appoggio * derivPart[baseOff1][i]; + deriv[baseOff2] += appoggio * derivPart[baseOff2][i]; } } } - // delete array... - for (int i = 0; i < iNpars; i++) { - delete[] derivPart[i]; - } - delete[] derivPart; } //---gradient calculations ended // fit ended. Final calculations } // FitFunction() @@ -301,10 +244,10 @@ void Cluster::print(Option_t* opt) const } printf("%sCLU: ch=%i (X = %7.3f, Y = %7.3f) Q=%8.3f Qraw=%8.3f(%3.0f%%) Size=%2i DimBox=%i LocMax=%i Chi2=%7.3f %s\n", opt, mCh, mXX, mYY, mQ, mQRaw, ratio, mSi, mBox, mNlocMax, mChi2, status); - if (mDigs.size() > 0) { + if (mDigs && mDigs->size() > 0) { std::cout << "Digits of Cluster" << std::endl; - for (int i; i < mDigs.size(); i++) { - std::cout << mDigs[i] << std::endl; + for (int i; i < mDigs->size(); i++) { + std::cout << (*mDigs)[i] << std::endl; } } return; @@ -321,6 +264,10 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b // Arguments: pCluLst - cluster list pointer where to add new cluster(s) // isTryUnfold - flag to switch on/off unfolding // Returns: number of local maxima of original cluster + const auto param = o2::hmpid::Param::instanceNoGeo(); + if (!mDigs) { + LOGP(fatal, "digits are missing in the cluster"); + } const int kMaxLocMax = 6; // max allowed number of loc max for fitting coG(); // First calculate CoG for the given cluster int iCluCnt = pCluLst->size(); // get current number of clusters already stored in the list by previous operations @@ -336,13 +283,18 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b // setClusterParams(mXX, mYY, mCh); // 2 - flag is set to FALSE // new ((*pCluLst)[iCluCnt++]) Cluster(*this); // 3 - size = 1 pCluLst->push_back(o2::hmpid::Cluster(*this)); + pCluLst->back().cleanPointers(); return 1; // add this raw cluster } // Phase 0. Initialise Fitter - double arglist[10]; + double arglist[10]{0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}; float ierflg = 0.; TVirtualFitter* fitter = TVirtualFitter::Fitter((TObject*)this, 3 * 6); // initialize Fitter + if (fitter == nullptr) { + LOG(fatal) << "TVirtualFitter could not be created"; + return 1; + } arglist[0] = -1; ierflg = fitter->ExecuteCommand("SET PRI", arglist, 1); // no printout ierflg = fitter->ExecuteCommand("SET NOW", arglist, 0); // no warning messages @@ -352,13 +304,13 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b // Phase 1. Find number of local maxima. Strategy is to check if the current pad has QDC more then all neigbours. Also find the box contaning the cluster mNlocMax = 0; for (int iDig1 = 0; iDig1 < rawSize; iDig1++) { // first digits loop - auto pDig1 = mDigs.at(iDig1); // take next digit + auto pDig1 = (*mDigs)[iDig1]; // take next digit int iCnt = 0; // counts how many neighbouring pads has QDC more then current one for (int iDig2 = 0; iDig2 < rawSize; iDig2++) { // loop on all digits again if (iDig1 == iDig2) { continue; } // the same digit, no need to compare - auto pDig2 = mDigs.at(iDig2); // take second digit to compare with the first one + auto pDig2 = (*mDigs)[iDig2]; // take second digit to compare with the first one int dist = TMath::Sign(int(pDig1->mX - pDig2->mX), 1) + TMath::Sign(int(pDig1->mY - pDig2->mY), 1); // distance between pads if (dist == 1) { // means dig2 is a neighbour of dig1 if (pDig2->mQ >= pDig1->mQ) { @@ -369,10 +321,10 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b if (iCnt == 0 && mNlocMax < kMaxLocMax) { // this pad has Q more then any neighbour so it's local maximum float xStart = o2::hmpid::Digit::lorsX(pDig1->getPadID()); float yStart = o2::hmpid::Digit::lorsY(pDig1->getPadID()); - float xMin = xStart - mParam->sizePadX(); - float xMax = xStart + mParam->sizePadX(); - float yMin = yStart - mParam->sizePadY(); - float yMax = yStart + mParam->sizePadY(); + float xMin = xStart - param->sizePadX(); + float xMax = xStart + param->sizePadX(); + float yMin = yStart - param->sizePadY(); + float yMax = yStart + param->sizePadY(); ierflg = fitter->SetParameter(3 * mNlocMax, Form("x%i", mNlocMax), xStart, 0.1, xMin, xMax); // X,Y,Q initial values of the loc max pad ierflg = fitter->SetParameter(3 * mNlocMax + 1, Form("y%i", mNlocMax), yStart, 0.1, yMin, yMax); // X, Y constrained to be near the loc max ierflg = fitter->SetParameter(3 * mNlocMax + 2, Form("q%i", mNlocMax), pDig1->mQ, 0.1, 0, 10000); // Q constrained to be positive @@ -387,6 +339,7 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b mSt = kNoLoc; // setClusterParams(mXX, mYY, mCh); //need to fill the AliCluster3D part pCluLst->push_back(o2::hmpid::Cluster(*this)); // add new unfolded cluster pCluLst->push_back(o2::hmpid::Cluster(*this)); + pCluLst->back().cleanPointers(); return mNlocMax; } @@ -395,10 +348,12 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b // setClusterParams(mXX, mYY, mCh); // if # of local maxima exceeds kMaxLocMax... mSt = kMax; pCluLst->push_back(o2::hmpid::Cluster(*this)); //...add this raw cluster - } else { // or resonable number of local maxima to fit and user requested it + pCluLst->back().cleanPointers(); + } else { // or resonable number of local maxima to fit and user requested it // Now ready for minimization step - arglist[0] = 500; // number of steps and sigma on pads charges - arglist[1] = 1.; // + arglist[0] = 500; // number of steps and sigma on pads charges + arglist[1] = 1.; // + /* ierflg = fitter->ExecuteCommand("SIMPLEX", arglist, 2); // start fitting with Simplex if (!ierflg) { fitter->ExecuteCommand("MIGRAD", arglist, 2); // fitting improved by Migrad @@ -413,6 +368,14 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b } } } + */ + + double strategy = 2.; + ierflg = fitter->ExecuteCommand("SET STR", &strategy, 1); // change level of strategy + if (!ierflg) { + fitter->ExecuteCommand("MIGRAD", arglist, 2); // fitting improved by Migrad + } + if (ierflg) { mSt = kAbn; // no convergence of the fit... } @@ -427,6 +390,7 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b fitter->GetParameter(3 * i + 1, sName, mYY, mErrY, dummy, dummy); // Y fitter->GetParameter(3 * i + 2, sName, mQ, mErrQ, dummy, dummy); // Q fitter->GetStats(mChi2, edm, errdef, nvpar, nparx); // get fit infos + // Printf("********************loc. max. = %i, X= %f, Y = %f, Q = %f**************************",i,mXX,mYY,mQ); if (mNlocMax > 1) { findClusterSize(i, pSigmaCut); // find clustersize for deconvoluted clusters // after this call, fSi temporarly is the calculated size. Later is set again @@ -447,7 +411,9 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b } } // setClusterParams(mXX, mYY, mCh); //need to fill the AliCluster3D part + // Printf("********************loc. max. = %i, X= %f, Y = %f, Q = %f**************************",i,mXX,mYY,mQ); pCluLst->push_back(o2::hmpid::Cluster(*this)); // add new unfolded cluster + pCluLst->back().cleanPointers(); if (mNlocMax > 1) { setSize(rawSize); // Original raw size is set again to its proper value } @@ -483,9 +449,13 @@ Bool_t Cluster::isInPc() // Check if (X,Y) position is inside the PC limits // Arguments: // Returns: True or False - int pc = mDigs[0]->getPh(); // (o2::hmpid::Digit*)&mDigs.at(iDig) + const auto param = o2::hmpid::Param::instanceNoGeo(); + if (!mDigs) { + LOGP(fatal, "digits are missing in the cluster"); + } + int pc = (*mDigs)[0]->getPh(); // (o2::hmpid::Digit*)&mDigs.at(iDig) - if (mXX < Param::minPcX(pc) || mXX > Param::maxPcX(pc) || mYY < Param::minPcY(pc) || mYY > Param::maxPcY(pc)) { + if (mXX < param->minPcX(pc) || mXX > param->maxPcX(pc) || mYY < param->minPcY(pc) || mYY > param->maxPcY(pc)) { return false; } @@ -497,12 +467,14 @@ void Cluster::digAdd(const Digit* pDig) // Adds a given digit to the list of digits belonging to this cluster, cluster is not owner of digits // Arguments: pDig - pointer to digit to be added // Returns: none - if (mDigs.size() == 0) { // create list of digits in the first invocation + if (!mDigs) { + LOGP(fatal, "digits are not set to the cluster"); + } + if (mDigs->size() == 0) { // create list of digits in the first invocation mSi = 0; - // std::vector fDigs; } // fDigs->Add(pDig); - mDigs.push_back(pDig); + mDigs->push_back(pDig); mSt = kFrm; mSi++; return; @@ -511,11 +483,7 @@ void Cluster::digAdd(const Digit* pDig) void Cluster::reset() { // - // - // - if (mDigs.size() > 0) { - mDigs.clear(); - } + cleanPointers(); // mDigs={0x0}; mSt = kEmp; mQRaw = mQ = 0; diff --git a/DataFormats/Detectors/HMPID/src/Digit.cxx b/DataFormats/Detectors/HMPID/src/Digit.cxx index 397d1a3cd5d0d..89726a5d28c95 100644 --- a/DataFormats/Detectors/HMPID/src/Digit.cxx +++ b/DataFormats/Detectors/HMPID/src/Digit.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -325,8 +325,8 @@ Double_t Digit::qdcTot(Double_t e, Double_t time, Int_t pc, Int_t px, Int_t py, /// @return : a charge fraction [0-1] imposed into the pad double Digit::intPartMathiX(double x, int pad) { - double shift1 = -lorsX(pad) + 0.5 * o2::hmpid::Param::sizePadX(); - double shift2 = -lorsX(pad) - 0.5 * o2::hmpid::Param::sizePadX(); + double shift1 = -lorsX(pad) + o2::hmpid::Param::sizeHalfPadX(); + double shift2 = -lorsX(pad) - o2::hmpid::Param::sizeHalfPadX(); double ux1 = o2::hmpid::Param::sqrtK3x() * tanh(o2::hmpid::Param::k2x() * (x + shift1) / o2::hmpid::Param::pitchAnodeCathode()); double ux2 = o2::hmpid::Param::sqrtK3x() * tanh(o2::hmpid::Param::k2x() * (x + shift2) / o2::hmpid::Param::pitchAnodeCathode()); @@ -343,8 +343,8 @@ double Digit::intPartMathiX(double x, int pad) /// @return : a charge fraction [0-1] imposed into the pad double Digit::intPartMathiY(double y, int pad) { - double shift1 = -lorsY(pad) + 0.5 * o2::hmpid::Param::sizePadY(); - double shift2 = -lorsY(pad) - 0.5 * o2::hmpid::Param::sizePadY(); + double shift1 = -lorsY(pad) + o2::hmpid::Param::sizeHalfPadY(); + double shift2 = -lorsY(pad) - o2::hmpid::Param::sizeHalfPadY(); double uy1 = o2::hmpid::Param::sqrtK3y() * tanh(o2::hmpid::Param::k2y() * (y + shift1) / o2::hmpid::Param::pitchAnodeCathode()); double uy2 = o2::hmpid::Param::sqrtK3y() * tanh(o2::hmpid::Param::k2y() * (y + shift2) / o2::hmpid::Param::pitchAnodeCathode()); diff --git a/DataFormats/Detectors/ITSMFT/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/CMakeLists.txt index 791339ac226d5..5d5e6da13067b 100644 --- a/DataFormats/Detectors/ITSMFT/CMakeLists.txt +++ b/DataFormats/Detectors/ITSMFT/CMakeLists.txt @@ -12,6 +12,3 @@ add_subdirectory(common) add_subdirectory(ITS) add_subdirectory(MFT) -if(ENABLE_UPGRADES) - add_subdirectory(ITS3) -endif() diff --git a/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt index 5a353881e27ba..f05979d749fc0 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt +++ b/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt @@ -11,8 +11,12 @@ o2_add_library(DataFormatsITS SOURCES src/TrackITS.cxx + src/TimeEstBC.cxx PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats + O2::SimulationDataFormat O2::DataFormatsITSMFT) o2_target_root_dictionary(DataFormatsITS - HEADERS include/DataFormatsITS/TrackITS.h) + HEADERS include/DataFormatsITS/TrackITS.h + include/DataFormatsITS/Vertex.h + include/DataFormatsITS/TimeEstBC.h) diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h new file mode 100644 index 0000000000000..695d9aff42858 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h @@ -0,0 +1,103 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKINGITS_TIMEESTBC_H_ +#define O2_TRACKINGITS_TIMEESTBC_H_ + +#include +#include +#include "CommonDataFormat/TimeStamp.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" + +namespace o2::its +{ +// Time estimates are given in BC +// error needs to cover maximum 1 orbit +using TimeStampType = uint32_t; +using TimeStampErrorType = uint16_t; +// this is an symmetric time error [t0-tE, t0+tE] +using TimeStamp = o2::dataformats::TimeStampWithError; +// this is an asymmetric time interval [t0, t0+tE] used for internal calculations +class TimeEstBC : public o2::dataformats::TimeStampWithError +{ + using Base = o2::dataformats::TimeStampWithError; + + public: + GPUhdDefault() TimeEstBC() = default; + GPUhdi() TimeEstBC(TimeStampType t, TimeStampErrorType e) : Base(t, e) {} + + // convert to symmetric center+-half representation + GPUhdi() its::TimeStamp makeSymmetrical() const noexcept + { + const auto start = static_cast(this->getTimeStamp()); + const float half = (float)this->getTimeStampError() / 2.f; + return {start + half, half}; + } + + // check if timestamps overlap within their interval + GPUhdi() bool isCompatible(const TimeEstBC& o) const noexcept + { + return this->upper() > o.lower() && o.upper() > this->lower(); + } + + // check if this time interval is fully contained within o + GPUhdi() bool isContainedIn(const TimeEstBC& o) const noexcept + { + return this->lower() >= o.lower() && this->upper() <= o.upper(); + } + + GPUhdi() TimeEstBC& operator+=(const TimeEstBC& o) noexcept + { + add(o); + return *this; + } + + GPUhdi() TimeEstBC operator+(const TimeEstBC& o) const noexcept + { + TimeEstBC res = *this; + res += o; + return res; + } + + // upper bound of interval t0+tE + GPUhdi() TimeStampType upper() const noexcept + { + TimeStampType t = this->getTimeStamp(); + TimeStampType e = this->getTimeStampError(); + constexpr TimeStampType max = std::numeric_limits::max(); + return (t > (max - e)) ? max : t + e; + } + + // lower bound of interval t0 + GPUhdi() TimeStampType lower() const noexcept + { + return this->getTimeStamp(); + } + + private: + // intersect with the other timestamp + // this assumes already that both overlap + GPUhdi() void add(const TimeEstBC& o) noexcept + { + const TimeStampType lo = o2::gpu::CAMath::Max(this->lower(), o.lower()); + const TimeStampType hi = o2::gpu::CAMath::Min(this->upper(), o.upper()); + this->setTimeStamp(lo); + this->setTimeStampError(static_cast(hi - lo)); + } + + ClassDefNV(TimeEstBC, 1); +}; + +} // namespace o2::its + +#endif diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h index 7c2dd982564c6..9b63509cc9424 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -16,11 +16,12 @@ #ifndef ALICEO2_ITS_TRACKITS_H #define ALICEO2_ITS_TRACKITS_H -#include +#include #include "GPUCommonDef.h" #include "ReconstructionDataFormats/Track.h" #include "CommonDataFormat/RangeReference.h" +#include "DataFormatsITS/TimeEstBC.h" namespace o2 { @@ -34,8 +35,13 @@ namespace its class TrackITS : public o2::track::TrackParCov { + public: + static constexpr unsigned int ExtendedPatternShift = 24; + static constexpr int MaxLayersInTrackPattern = 8; + + private: enum UserBits { - kNextROF = 1 + kSharedClusters = 1 << 28 }; using Cluster = o2::itsmft::Cluster; @@ -45,22 +51,23 @@ class TrackITS : public o2::track::TrackParCov using o2::track::TrackParCov::TrackParCov; // inherit base constructors static constexpr int MaxClusters = 16; - GPUdDefault() TrackITS() = default; - GPUdDefault() TrackITS(const TrackITS& t) = default; - GPUd() TrackITS(const o2::track::TrackParCov& parcov) : o2::track::TrackParCov{parcov} {} - GPUd() TrackITS(const o2::track::TrackParCov& parCov, float chi2, const o2::track::TrackParCov& outer) + GPUhdDefault() TrackITS() = default; + GPUhdDefault() TrackITS(const TrackITS& t) = default; + GPUhd() TrackITS(const o2::track::TrackParCov& parcov) : o2::track::TrackParCov{parcov} {} + GPUhd() TrackITS(const o2::track::TrackParCov& parCov, float chi2, const o2::track::TrackParCov& outer) : o2::track::TrackParCov{parCov}, mParamOut{outer}, mChi2{chi2} {} - GPUdDefault() TrackITS& operator=(const TrackITS& tr) = default; - GPUdDefault() ~TrackITS() = default; + GPUhdDefault() TrackITS& operator=(const TrackITS& tr) = default; + GPUhdDefault() TrackITS& operator=(TrackITS&& tr) = default; + GPUhdDefault() ~TrackITS() = default; // These functions must be provided bool propagate(float alpha, float x, float bz); bool update(const Cluster& c, float chi2); // Other functions - float getChi2() const { return mChi2; } - int getNClusters() const { return mClusRef.getEntries(); } - int getNumberOfClusters() const { return getNClusters(); } + GPUhdi() float getChi2() const { return mChi2; } + GPUhdi() int getNClusters() const { return mClusRef.getEntries(); } + GPUhdi() int getNumberOfClusters() const { return getNClusters(); } int getFirstClusterEntry() const { return mClusRef.getFirstEntry(); } int getClusterEntry(int i) const { return getFirstClusterEntry() + i; } void shiftFirstClusterEntry(int bias) @@ -85,23 +92,58 @@ class TrackITS : public o2::track::TrackParCov } const ClusRefs& getClusterRefs() const { return mClusRef; } - ClusRefs& getClusterRefs() { return mClusRef; } + GPUhdi() ClusRefs& getClusterRefs() { return mClusRef; } - void setChi2(float chi2) { mChi2 = chi2; } + GPUhdi() void setChi2(float chi2) { mChi2 = chi2; } bool isBetter(const TrackITS& best, float maxChi2) const; - o2::track::TrackParCov& getParamIn() { return *this; } - const o2::track::TrackParCov& getParamIn() const { return *this; } + GPUhdi() auto& getTimeStamp() { return mTime; } + GPUhdi() const auto& getTimeStamp() const { return mTime; } - o2::track::TrackParCov& getParamOut() { return mParamOut; } - const o2::track::TrackParCov& getParamOut() const { return mParamOut; } + GPUhdi() o2::track::TrackParCov& getParamIn() { return *this; } + GPUhdi() const o2::track::TrackParCov& getParamIn() const { return *this; } - void setPattern(uint32_t p) { mPattern = p; } - uint32_t getPattern() const { return mPattern; } - bool hasHitOnLayer(int i) { return mPattern & (0x1 << i); } - bool isFakeOnLayer(int i) { return !(mPattern & (0x1 << (16 + i))); } - uint32_t getLastClusterLayer() const + GPUhdi() o2::track::TrackParCov& getParamOut() { return mParamOut; } + GPUhdi() const o2::track::TrackParCov& getParamOut() const { return mParamOut; } + + GPUhdi() void setPattern(uint32_t p) { mPattern = p; } + GPUhdi() uint32_t getPattern() const { return mPattern; } + bool hasHitOnLayer(uint32_t i) const { return mPattern & (0x1 << i); } + bool isFakeOnLayer(uint32_t i) const { return !(mPattern & (0x1 << (16 + i))); } + bool isExtendedOnLayer(uint32_t i) const { return (mPattern & (0x1 << (ExtendedPatternShift + i))); } // only correct if getNClusters <= 8 on layers <= 8 + template + GPUhdi() static constexpr uint32_t getLayerPatternMask() + { + return (NLayers >= 32) ? 0xffffffffu : ((1u << NLayers) - 1u); + } + template + GPUhdi() void setExtendedLayerPattern(uint32_t pattern) + { + pattern &= getLayerPatternMask(); + setUserField(static_cast(pattern)); + if constexpr (NLayers <= MaxLayersInTrackPattern) { + setPattern(getPattern() | (pattern << ExtendedPatternShift)); + } + } + template + GPUhdi() uint32_t getExtendedLayerPattern() const + { + const auto mask = getLayerPatternMask(); + if constexpr (NLayers <= MaxLayersInTrackPattern) { + const auto pattern = (getPattern() >> ExtendedPatternShift) & mask; + if (pattern) { + return pattern; + } + } + return getUserField() & mask; + } + GPUhdi() void clearExtendedLayerPattern() + { + setUserField(0); + getParamOut().setUserField(0); + } + GPUhdi() uint32_t getLastClusterLayer() const { uint32_t r{0}, v{mPattern & ((1 << 16) - 1)}; while (v >>= 1) { @@ -109,7 +151,7 @@ class TrackITS : public o2::track::TrackParCov } return r; } - uint32_t getFirstClusterLayer() const + GPUhdi() uint32_t getFirstClusterLayer() const { int s{0}; while (!(mPattern & (1 << s))) { @@ -117,18 +159,45 @@ class TrackITS : public o2::track::TrackParCov } return s; } - int getNFakeClusters(); + int getNFakeClusters() const; + + void setSharedClusters(bool toggle = true) { mClusterSizes = toggle ? (mClusterSizes | kSharedClusters) : (mClusterSizes & ~kSharedClusters); } + bool hasSharedClusters() const { return mClusterSizes & kSharedClusters; } + + void setClusterSize(int l, int size) + { + if (l >= 8) { + return; + } + if (size > 15) { + size = 15; + } + mClusterSizes &= ~(0xf << (l * 4)); + mClusterSizes |= (size << (l * 4)); + } - void setNextROFbit(bool toggle = true) { setUserField((getUserField() & ~kNextROF) | (-toggle & kNextROF)); } - bool hasHitInNextROF() const { return getUserField() & kNextROF; } + int getClusterSize(int l) + { + if (l >= 7) { + return 0; + } + return (mClusterSizes >> (l * 4)) & 0xf; + } + + int getClusterSizes() const + { + return mClusterSizes; + } private: o2::track::TrackParCov mParamOut; ///< parameter at largest radius ClusRefs mClusRef; ///< references on clusters float mChi2 = 0.; ///< Chi2 for this track uint32_t mPattern = 0; ///< layers pattern + uint32_t mClusterSizes = 0u; ///< 4bit packed cluster sizes + TimeStamp mTime; ///< track time stamp with error in BC since start of TF, symmetrical - ClassDefNV(TrackITS, 5); + ClassDefNV(TrackITS, 7); }; class TrackITSExt : public TrackITS @@ -138,21 +207,19 @@ class TrackITSExt : public TrackITS static constexpr int MaxClusters = 16; /// Prepare for overlaps and new detector configurations using TrackITS::TrackITS; // inherit base constructors - GPUd() TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, - o2::track::TrackParCov&& outer, std::array cls) + GPUh() TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, o2::track::TrackParCov&& outer, std::array cls) : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); } - GPUd() TrackITSExt(o2::track::TrackParCov& parCov, short ncl, float chi2, std::uint32_t rof, - o2::track::TrackParCov& outer, std::array cls) + GPUh() TrackITSExt(o2::track::TrackParCov& parCov, short ncl, float chi2, std::uint32_t rof, o2::track::TrackParCov& outer, std::array cls) : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); } - GPUdDefault() TrackITSExt(const TrackITSExt& t) = default; + GPUhdDefault() TrackITSExt(const TrackITSExt& t) = default; void setClusterIndex(int l, int i) { @@ -161,9 +228,14 @@ class TrackITSExt : public TrackITS getClusterRefs().setEntries(ncl); } - int getClusterIndex(int lr) const { return mIndex[lr]; } + GPUhdi() int getClusterIndex(int lr) const { return mIndex[lr]; } - void setExternalClusterIndex(int layer, int idx, bool newCluster = false) + GPUh() int getFirstLayerClusterIndex() const + { + return getClusterIndex(getFirstClusterLayer()); + } + + GPUhdi() void setExternalClusterIndex(int layer, int idx, bool newCluster = false) { if (newCluster) { getClusterRefs().setEntries(getNumberOfClusters() + 1); @@ -174,14 +246,41 @@ class TrackITSExt : public TrackITS mIndex[layer] = idx; } - std::array& getClusterIndexes() + GPUh() std::array& getClusterIndexes() { return mIndex; } +#ifndef GPUCA_GPUCODE + // build order-independent hash via the external cluster idx (unique within a TF) for the selected layers + // cluster indices are either sorted inward or outward + size_t hash(uint16_t layerMask = 0xFFFF, bool inward = true) const noexcept + { + size_t h1 = 0, h2 = 0; + int from = (int)getLastClusterLayer(), to = -1, step = -1; + if (inward) { + from = (int)getFirstClusterLayer(); + to = MaxClusters; + step = 1; + } + // clusters are stored continously but they do not necesarrily correspond to the layers + for (int layer = from, slot{0}; layer != to; layer += step) { + if (hasHitOnLayer(layer)) { + int idx = mIndex[slot++]; + if (layerMask & (uint16_t(1) << layer)) { + size_t v = std::hash{}(idx); + h1 ^= v; + h2 += v * 0x9e3779b97f4a7c15ULL; // boost's hash_combine + } + } + } + return h1 ^ (h2 << 1); + } +#endif + private: std::array mIndex = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; ///< Indices of associated clusters - ClassDefNV(TrackITSExt, 2); + ClassDefNV(TrackITSExt, 3); }; } // namespace its } // namespace o2 diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h new file mode 100644 index 0000000000000..1e4ed03b753eb --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h @@ -0,0 +1,42 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKINGITS_VERTEX_H_ +#define O2_TRACKINGITS_VERTEX_H_ + +#include "GPUCommonDef.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include +#endif +#include "ReconstructionDataFormats/Vertex.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "DataFormatsITS/TimeEstBC.h" + +namespace o2::its +{ +// NOTE: this uses the internal asymmetrical time reprenstation! +using Vertex = o2::dataformats::Vertex; +using VertexLabel = std::pair; +} // namespace o2::its + +#ifndef GPUCA_GPUCODE_DEVICE +/// Defining ITS Vertex explicitly as messageable +namespace o2::framework +{ +template +struct is_messageable; +template <> +struct is_messageable> : std::true_type { +}; +} // namespace o2::framework +#endif + +#endif diff --git a/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h b/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h index 91a71847148fb..a0d5b25c65b70 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h +++ b/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h @@ -14,7 +14,14 @@ #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; + #pragma link C++ class o2::its::TrackITS + ; #pragma link C++ class std::vector < o2::its::TrackITS> + ; +#pragma link C++ class o2::its::TimeEstBC + ; +#pragma link C++ class std::vector < o2::its::TimeEstBC> + ; + +#pragma link C++ class o2::dataformats::Vertex < o2::its::TimeEstBC> + ; +#pragma link C++ class std::vector < o2::dataformats::Vertex < o2::its::TimeEstBC>> + ; + #endif diff --git a/DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx b/DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx new file mode 100644 index 0000000000000..3af299cf74d25 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsITS/TimeEstBC.h" +ClassImp(o2::its::TimeEstBC); \ No newline at end of file diff --git a/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx b/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx index 4f0a0a5a2ae7f..0244756647120 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx +++ b/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx @@ -29,31 +29,31 @@ bool TrackITS::operator<(const TrackITS& o) const //----------------------------------------------------------------- // This function compares tracks according to the their curvature //----------------------------------------------------------------- - Float_t co = TMath::Abs(o.getPt()); - Float_t c = TMath::Abs(getPt()); - // Float_t co=o.GetSigmaY2()*o.GetSigmaZ2(); - // Float_t c =GetSigmaY2()*GetSigmaZ2(); + float co = TMath::Abs(o.getPt()); + float c = TMath::Abs(getPt()); + // float co=o.GetSigmaY2()*o.GetSigmaZ2(); + // float c =GetSigmaY2()*GetSigmaZ2(); return (c > co); } -void TrackITS::getImpactParams(Float_t x, Float_t y, Float_t z, Float_t bz, Float_t ip[2]) const +void TrackITS::getImpactParams(float x, float y, float z, float bz, float ip[2]) const { //------------------------------------------------------------------ // This function calculates the transverse and longitudinal impact parameters // with respect to a point with global coordinates (x,y,0) // in the magnetic field "bz" (kG) //------------------------------------------------------------------ - Float_t f1 = getSnp(), r1 = TMath::Sqrt((1. - f1) * (1. + f1)); - Float_t xt = getX(), yt = getY(); - Float_t sn = TMath::Sin(getAlpha()), cs = TMath::Cos(getAlpha()); - Float_t a = x * cs + y * sn; + float f1 = getSnp(), r1 = TMath::Sqrt((1. - f1) * (1. + f1)); + float xt = getX(), yt = getY(); + float sn = TMath::Sin(getAlpha()), cs = TMath::Cos(getAlpha()); + float a = x * cs + y * sn; y = -x * sn + y * cs; x = a; xt -= x; yt -= y; - Float_t rp4 = getCurvature(bz); + float rp4 = getCurvature(bz); if ((TMath::Abs(bz) < Almost0) || (TMath::Abs(rp4) < Almost0)) { ip[0] = -(xt * f1 - yt * r1); ip[1] = getZ() + (ip[0] * f1 - xt) / r1 * getTgl() - z; @@ -63,59 +63,59 @@ void TrackITS::getImpactParams(Float_t x, Float_t y, Float_t z, Float_t bz, Floa sn = rp4 * xt - f1; cs = rp4 * yt + r1; a = 2 * (xt * f1 - yt * r1) - rp4 * (xt * xt + yt * yt); - Float_t rr = TMath::Sqrt(sn * sn + cs * cs); + float rr = TMath::Sqrt(sn * sn + cs * cs); ip[0] = -a / (1 + rr); - Float_t f2 = -sn / rr, r2 = TMath::Sqrt((1. - f2) * (1. + f2)); + float f2 = -sn / rr, r2 = TMath::Sqrt((1. - f2) * (1. + f2)); ip[1] = getZ() + getTgl() / rp4 * TMath::ASin(f2 * r1 - f1 * r2) - z; } -Bool_t TrackITS::propagate(Float_t alpha, Float_t x, Float_t bz) +bool TrackITS::propagate(float alpha, float x, float bz) { if (rotate(alpha)) { if (propagateTo(x, bz)) { - return kTRUE; + return true; } } - return kFALSE; + return false; } -Bool_t TrackITS::update(const Cluster& c, Float_t chi2) +bool TrackITS::update(const Cluster& c, float chi2) { //-------------------------------------------------------------------- // Update track params //-------------------------------------------------------------------- if (!o2::track::TrackParCov::update(static_cast&>(c))) { - return kFALSE; + return false; } mChi2 += chi2; - return kTRUE; + return true; } -Bool_t TrackITS::isBetter(const TrackITS& best, Float_t maxChi2) const +bool TrackITS::isBetter(const TrackITS& best, float maxChi2) const { - Int_t ncl = getNumberOfClusters(); - Int_t nclb = best.getNumberOfClusters(); + int ncl = getNumberOfClusters(); + int nclb = best.getNumberOfClusters(); if (ncl >= nclb) { - Float_t chi2 = getChi2(); + float chi2 = getChi2(); if (chi2 < maxChi2) { if (ncl > nclb || chi2 < best.getChi2()) { - return kTRUE; + return true; } } } - return kFALSE; + return false; } -int TrackITS::getNFakeClusters() +int TrackITS::getNFakeClusters() const { int nFake{0}; - int firstClus = getFirstClusterLayer(); - int lastClus = firstClus + getNClusters(); - for (int iCl{firstClus}; iCl < lastClus; ++iCl) { + auto firstClus = getFirstClusterLayer(); + auto lastClus = firstClus + getNClusters(); + for (auto iCl{firstClus}; iCl < lastClus; ++iCl) { if (hasHitOnLayer(iCl) && isFakeOnLayer(iCl)) { ++nFake; } } return nFake; -} \ No newline at end of file +} diff --git a/DataFormats/Detectors/ITSMFT/ITS3/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/ITS3/CMakeLists.txt deleted file mode 100644 index 944ca48df1191..0000000000000 --- a/DataFormats/Detectors/ITSMFT/ITS3/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. -# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -# All rights not expressly granted are reserved. -# -# This software is distributed under the terms of the GNU General Public -# License v3 (GPL Version 3), copied verbatim in the file "COPYING". -# -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization -# or submit itself to any jurisdiction. - -o2_add_library(DataFormatsITS3 - SOURCES src/CompCluster.cxx) - -o2_target_root_dictionary(DataFormatsITS3 - HEADERS include/DataFormatsITS3/CompCluster.h - LINKDEF src/ITS3DataFormatsLinkDef.h) diff --git a/DataFormats/Detectors/ITSMFT/ITS3/include/DataFormatsITS3/CompCluster.h b/DataFormats/Detectors/ITSMFT/ITS3/include/DataFormatsITS3/CompCluster.h deleted file mode 100644 index 03da492fd56c4..0000000000000 --- a/DataFormats/Detectors/ITSMFT/ITS3/include/DataFormatsITS3/CompCluster.h +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CompCluster.h -/// \brief Definition of the ITS3 compact cluster -#ifndef ALICEO2_ITS3_COMPCLUSTER_H -#define ALICEO2_ITS3_COMPCLUSTER_H - -#include - -namespace o2 -{ -namespace its3 -{ - -/// This is a version of the ALPIDE cluster represented by the pattern ID and the address of the -/// top-left (min row,col) pixel of the topololy bounding box - -class CompCluster -{ - public: - static constexpr int NBitsPattID = 11; // number of bits for the pattern ID - private: - static constexpr UShort_t PattIDMask = (0x1 << NBitsPattID) - 1; - static constexpr UShort_t FlagBit = 0x1 << (NBitsPattID); - // - ///< compactified data: bits [0:8] - row, [9-18] - col, [19-30] - pattern ID, bit 31 - special flag - UShort_t mRow; - UShort_t mCol; - UShort_t mPat; - - public: - static constexpr unsigned short InvalidPatternID = (0x1 << NBitsPattID) - 1; // All 11 bits of pattern ID are 1 - CompCluster(UShort_t row = 0, UShort_t col = 0, UShort_t patt = 0) - { - set(row, col, patt); - } - - void set(UShort_t row, UShort_t col, UShort_t patt) - { - setPatternID(patt); - mRow = row; - mCol = col; - } - - UShort_t getRow() const { return mRow; } - UShort_t getCol() const { return mCol; } - UShort_t getPatternID() const { return (mPat & PattIDMask); } - bool getFlag() const { return (mPat & FlagBit) == FlagBit; } - - void setRow(UShort_t r) - { - mRow = r; - } - void setCol(UShort_t c) - { - mCol = c; - }; - void setPatternID(UShort_t p) - { - mPat &= ~(PattIDMask); - mPat |= p; - } - void setFlag(bool v) - { - mPat &= ~FlagBit; - if (v) { - mPat |= FlagBit; - } - } - - void print() const; - - ClassDefNV(CompCluster, 1); -}; - -/// Extension of the compact cluster, augmented by the chipID -/// This is a TEMPORARY class, until we converge to more economical container -class CompClusterExt : public CompCluster -{ - private: - UShort_t mChipID; ///< chip id - - public: - CompClusterExt(UShort_t row = 0, UShort_t col = 0, UShort_t patt = 0, UShort_t chipID = 0) : CompCluster(row, col, patt), mChipID(chipID) - { - } - - void set(UShort_t row, UShort_t col, UShort_t patt, UShort_t chipID) - { - CompCluster::set(row, col, patt); - mChipID = chipID; - } - - UShort_t getChipID() const { return mChipID; } - UShort_t getSensorID() const { return mChipID; } // to have the same signature as BaseCluster - - void setChipID(UShort_t c) { mChipID = c; } - - void print() const; - - ClassDefNV(CompClusterExt, 1); -}; - -} // namespace its3 -} // namespace o2 - -std::ostream& operator<<(std::ostream& stream, const o2::its3::CompCluster& cl); -std::ostream& operator<<(std::ostream& stream, const o2::its3::CompClusterExt& cl); - -#endif /* ALICEO2_ITS3_COMPACTCLUSTER_H */ diff --git a/DataFormats/Detectors/ITSMFT/ITS3/src/CompCluster.cxx b/DataFormats/Detectors/ITSMFT/ITS3/src/CompCluster.cxx deleted file mode 100644 index 046a27be92919..0000000000000 --- a/DataFormats/Detectors/ITSMFT/ITS3/src/CompCluster.cxx +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file Cluster.cxx -/// \brief Implementation of the ITSMFT cluster - -#include "DataFormatsITS3/CompCluster.h" -#include -#include - -using namespace o2::its3; - -std::ostream& operator<<(std::ostream& stream, const CompCluster& cl) -{ - stream << " row: " << cl.getRow() << " col: " << cl.getCol() - << " pattID " << cl.getPatternID() << " [flag: " << cl.getFlag() << "] "; - return stream; -} - -std::ostream& operator<<(std::ostream& stream, const CompClusterExt& cl) -{ - stream << " chip: " << cl.getChipID() << ((const CompCluster&)cl); - return stream; -} - -//______________________________________________________________________________ -void CompCluster::print() const -{ - // print itself - std::cout << *this << "\n"; -} - -//______________________________________________________________________________ -void CompClusterExt::print() const -{ - // print itself - std::cout << *this << "\n"; -} diff --git a/DataFormats/Detectors/ITSMFT/ITS3/src/ITS3DataFormatsLinkDef.h b/DataFormats/Detectors/ITSMFT/ITS3/src/ITS3DataFormatsLinkDef.h deleted file mode 100644 index c139171540f18..0000000000000 --- a/DataFormats/Detectors/ITSMFT/ITS3/src/ITS3DataFormatsLinkDef.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifdef __CLING__ - -#pragma link off all globals; -#pragma link off all classes; -#pragma link off all functions; - -#pragma link C++ class o2::its3::CompCluster + ; -#pragma link C++ class o2::its3::CompClusterExt + ; -#pragma link C++ class std::vector < o2::its3::CompCluster> + ; -#pragma link C++ class std::vector < o2::its3::CompClusterExt> + ; - -#endif diff --git a/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h b/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h index 04ac4fc54263c..04cca15264047 100644 --- a/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h +++ b/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h @@ -70,6 +70,24 @@ class TrackMFT : public o2::track::TrackParCovFwd const o2::track::TrackParCovFwd& getOutParam() const { return mOutParameters; } ///< Returns track parameters fitted outwards void setOutParam(const o2::track::TrackParCovFwd parcov) { mOutParameters = parcov; } ///< Set track out parameters + void setClusterSize(int l, int size) + { + if (l >= 10) { + return; + } + if (size > 63) { + size = 63; + } + + mClusterSizes &= ~(0x3fULL << (l * 6)); + mClusterSizes |= (static_cast(size) << (l * 6)); + } + + uint64_t getClusterSizes() const + { + return mClusterSizes; + } + private: Bool_t mIsCA = false; ///< Track finding method CA vs. LTF @@ -79,8 +97,9 @@ class TrackMFT : public o2::track::TrackParCovFwd Double_t mSeedinvQPtFitChi2 = 0.; ///< Seed InvQPt Chi2 from FCF clusters X,Y positions Double_t mInvQPtSeed; ///< Seed InvQPt from FCF clusters X,Y positions + uint64_t mClusterSizes = 0; ///< MFT cluster sizes per track - ClassDefNV(TrackMFT, 2); + ClassDefNV(TrackMFT, 3); }; class TrackMFTExt : public TrackMFT @@ -94,14 +113,28 @@ class TrackMFTExt : public TrackMFT using TrackMFT::TrackMFT; // inherit base constructors int getExternalClusterIndex(int i) const { return mExtClsIndex[i]; } + int getExternalClusterSize(int i) const { return mExtClsSize[i]; } + int getExternalClusterLayer(int i) const { return mExtClsLayer[i]; } void setExternalClusterIndex(int np, int idx) { mExtClsIndex[np] = idx; } + void setExternalClusterSize(int np, int clsSize) + { + mExtClsSize[np] = clsSize; + } + + void setExternalClusterLayer(int np, int clsLayer) + { + mExtClsLayer[np] = clsLayer; + } + protected: std::array mExtClsIndex = {-1}; ///< External indices of associated clusters + std::array mExtClsSize = {-1}; ///< Cluster size + std::array mExtClsLayer = {-1}; ///< Cluster layer ClassDefNV(TrackMFTExt, 1); }; diff --git a/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt index 2adaa9bcf24f2..b80509c52fcee 100644 --- a/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt +++ b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# Copyright 2019-2026 CERN and copyright holders of ALICE O2. # See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. # All rights not expressly granted are reserved. # @@ -18,16 +18,24 @@ o2_add_library(DataFormatsITSMFT src/ClusterPattern.cxx src/ClusterTopology.cxx src/TopologyDictionary.cxx + src/TimeDeadMap.cxx src/CTF.cxx + src/DPLAlpideParam.cxx + src/DPLAlpideParamInitializer.cxx PUBLIC_LINK_LIBRARIES O2::ITSMFTBase + O2::DetectorsCommonDataFormats O2::ReconstructionDataFormats + O2::CommonUtils Microsoft.GSL::GSL) o2_target_root_dictionary(DataFormatsITSMFT HEADERS include/DataFormatsITSMFT/ROFRecord.h - include/DataFormatsITSMFT/Digit.h - include/DataFormatsITSMFT/GBTCalibData.h - include/DataFormatsITSMFT/NoiseMap.h + include/DataFormatsITSMFT/Digit.h + include/DataFormatsITSMFT/DPLAlpideParam.h + include/DataFormatsITSMFT/GBTCalibData.h + include/DataFormatsITSMFT/NoiseMap.h + include/DataFormatsITSMFT/TimeDeadMap.h + include/DataFormatsITSMFT/StuckPixelData.h include/DataFormatsITSMFT/Cluster.h include/DataFormatsITSMFT/CompCluster.h include/DataFormatsITSMFT/ClusterPattern.h diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h index b465aa5b3d66b..0510b6df5225c 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h @@ -36,7 +36,9 @@ struct CTFHeader : public o2::ctf::CTFDictHeader { uint32_t nPatternBytes = 0; /// number of bytes for explict patterns uint32_t firstOrbit = 0; /// 1st orbit of TF uint16_t firstBC = 0; /// 1st BC of TF - ClassDefNV(CTFHeader, 2); + uint8_t maxStreams = 1; /// Number of streams per TF (== NLayers for staggered ITS/MFT readout, 1 for non-staggered one) + uint8_t streamID = 0; /// ID of the stream (0:maxStreams-1) + ClassDefNV(CTFHeader, 3); }; /// Compressed but not yet entropy-encoded clusters @@ -46,12 +48,12 @@ struct CompressedClusters { // ROF header data std::vector firstChipROF; /// 1st chip ID in the ROF - std::vector bcIncROF; /// increment of ROF BC wrt BC of previous ROF - std::vector orbitIncROF; /// increment of ROF orbit wrt orbit of previous ROF + std::vector bcIncROF; /// increment of ROF BC wrt BC of previous ROF + std::vector orbitIncROF; /// increment of ROF orbit wrt orbit of previous ROF std::vector nclusROF; /// number of clusters in ROF // Chip data - std::vector chipInc; /// increment of chipID wrt that of prev. chip + std::vector chipInc; /// increment of chipID wrt that of prev. chip std::vector chipMul; /// clusters in chip std::vector row; /// row of fired pixel std::vector colInc; /// increment of pixel column wrt that of prev. pixel (sometimes can be slightly negative) diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h index 772a9ae12a81a..361544798dc80 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h @@ -15,6 +15,7 @@ #define ALICEO2_ITSMFT_COMPCLUSTER_H #include +#include namespace o2 { @@ -81,7 +82,13 @@ class CompCluster } } + bool operator==(const CompCluster& cl) const + { + return mData == cl.mData; + } + void print() const; + std::string asString() const; ClassDefNV(CompCluster, 2); }; @@ -91,7 +98,7 @@ class CompCluster class CompClusterExt : public CompCluster { private: - UShort_t mChipID; ///< chip id + UShort_t mChipID; ///< chip id public: CompClusterExt(UShort_t row = 0, UShort_t col = 0, UShort_t patt = 0, UShort_t chipID = 0) : CompCluster(row, col, patt), mChipID(chipID) @@ -110,6 +117,7 @@ class CompClusterExt : public CompCluster void setChipID(UShort_t c) { mChipID = c; } void print() const; + std::string asString() const; ClassDefNV(CompClusterExt, 1); }; diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h new file mode 100644 index 0000000000000..a06ba0745edbd --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h @@ -0,0 +1,104 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_ITSMFTALPIDEPARAM_H_ +#define ALICEO2_ITSMFTALPIDEPARAM_H_ + +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "CommonConstants/LHCConstants.h" +#include + +namespace o2 +{ +namespace itsmft +{ +constexpr float DEFStrobeDelay = o2::constants::lhc::LHCBunchSpacingNS * 4; // ~100 ns delay + +template +struct DPLAlpideParam : public o2::conf::ConfigurableParamHelper> { + static constexpr int getNLayers() + { + return N == o2::detectors::DetID::ITS ? 7 : 10; + } + + static constexpr std::string_view getParamName() + { + return N == o2::detectors::DetID::ITS ? ParamName[0] : ParamName[1]; + } + + int roFrameLengthInBC = DEFROFLengthBC(); ///< ROF length in BC for continuous mode + float roFrameLengthTrig = DEFROFLengthTrig(); ///< length of RO frame in ns for triggered mode + float strobeDelay = DEFStrobeDelay; ///< strobe start (in ns) wrt ROF start + float strobeLengthCont = -1.; ///< if < 0, full ROF length - delay + float strobeLengthTrig = 100.; ///< length of the strobe in ns (sig. over threshold checked in this window only) + int roFrameBiasInBC = DEFROFBiasInBC(); ///< bias of the start of ROF wrt orbit start: t_irof = (irof*roFrameLengthInBC + roFrameBiasInBC)*BClengthMUS + int roFrameLayerLengthInBC[getNLayers()] = {}; ///< staggering ROF length in BC for continuous mode per layer + int roFrameLayerBiasInBC[getNLayers()] = {}; ///< staggering ROF bias in BC for continuous mode per layer + int roFrameLayerDelayInBC[getNLayers()] = {}; ///< staggering ROF delay in BC for continuous mode per layer + + // get ROF length for any layer + int getROFLengthInBC(int layer) const noexcept { return roFrameLayerLengthInBC[layer] ? roFrameLayerLengthInBC[layer] : roFrameLengthInBC; } + int getROFBiasInBC(int layer) const noexcept { return roFrameLayerBiasInBC[layer] ? roFrameLayerBiasInBC[layer] : roFrameBiasInBC; } + int getROFDelayInBC(int layer) const noexcept { return roFrameLayerDelayInBC[layer] ? roFrameLayerDelayInBC[layer] : 0; } + + // boilerplate stuff + make principal key + O2ParamDef(DPLAlpideParam, getParamName().data()); + + private: + static constexpr std::string_view ParamName[2] = {"ITSAlpideParam", "MFTAlpideParam"}; + + static constexpr int DEFROFLengthBC() + { + // default ROF length in BC for continuous mode + // allowed values: 1,2,3,4,6,9,11,12,18,22,27,33,36 + return N == o2::detectors::DetID::ITS ? o2::constants::lhc::LHCMaxBunches / 4 : o2::constants::lhc::LHCMaxBunches / 18; + } + static constexpr float DEFROFLengthTrig() + { + // length of RO frame in ns for triggered mode + return N == o2::detectors::DetID::ITS ? 6000. : 6000.; + } + + static constexpr int DEFROFBiasInBC() + { + // default ROF length bias in MC, see https://github.com/AliceO2Group/AliceO2/pull/11108 for ITS + return N == o2::detectors::DetID::ITS ? 64 : 60; + } + + static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS orDetID:: MFT are allowed"); + static_assert(o2::constants::lhc::LHCMaxBunches % DEFROFLengthBC() == 0); // make sure ROF length is divisor of the orbit +}; + +template +DPLAlpideParam DPLAlpideParam::sInstance; + +} // namespace itsmft + +namespace framework +{ +template +struct is_messageable; +template <> +struct is_messageable> : std::true_type { +}; +template +struct is_messageable; +template <> +struct is_messageable> : std::true_type { +}; + +} // namespace framework + +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h new file mode 100644 index 0000000000000..b3ec20f2a68b7 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h @@ -0,0 +1,42 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_ITSMFTALPIDEPARAM_INITIALIZER_H_ +#define ALICEO2_ITSMFTALPIDEPARAM_INITIALIZER_H_ +#include + +namespace o2 +{ +namespace framework +{ +class ConfigParamSpec; +class ConfigContext; +} // namespace framework +namespace itsmft +{ + +struct DPLAlpideParamInitializer { + static constexpr char stagITSOpt[] = "enable-its-staggering"; + static constexpr char stagMFTOpt[] = "enable-mft-staggering"; + static constexpr bool stagDef = false; + + // DPL workflow options for staggering + static void addConfigOption(std::vector& opts); + static void addITSConfigOption(std::vector& opts); + static bool isITSStaggeringEnabled(o2::framework::ConfigContext const& cfgc); + static void addMFTConfigOption(std::vector& opts); + static bool isMFTStaggeringEnabled(o2::framework::ConfigContext const& cfgc); +}; + +} // namespace itsmft +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h index 4815b688ee7c1..25b7f451b6452 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h @@ -169,7 +169,7 @@ class NoiseMap // Methods required by the calibration framework void print(); void fill(const gsl::span data); - void merge(const NoiseMap* prev) {} + const std::map* getChipMap(int chip) const { return chip < (int)mNoisyPixels.size() ? &mNoisyPixels[chip] : nullptr; } std::map& getChip(int chip) { return mNoisyPixels[chip]; } @@ -202,6 +202,20 @@ class NoiseMap return std::ceil((1. + 1. / t) / (relErr * relErr)); } + NoiseMap merge(const NoiseMap* prev) + { + int incre = 0; + for (size_t i = 0; i < mNoisyPixels.size(); ++i) { + for (const auto& prev_np : prev->mNoisyPixels[i]) { // only enters this for loop if the "i" chip exists. + if (mNoisyPixels[i].find(prev_np.first) == mNoisyPixels[i].end()) { + mNoisyPixels[i][prev_np.first] = prev_np.second; + incre++; + } + } // end of for loop on elements of previous noise map + } // end of for loop on i (chip ID) + return (mNoisyPixels); + } // end of void merge + size_t size() const { return mNoisyPixels.size(); } void setNumOfStrobes(long n) { mNumOfStrobes = n; } void addStrobes(long n) { mNumOfStrobes += n; } diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h index 1f7ac73d0a131..9a6cd0891b6be 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h @@ -31,6 +31,8 @@ class ROFRecord { public: + enum { VtxStdMode = 0, + VtxUPCMode = 1 }; using EvIdx = o2::dataformats::RangeReference; using BCData = o2::InteractionRecord; using ROFtype = unsigned int; @@ -45,6 +47,14 @@ class ROFRecord void setFirstEntry(int idx) { mROFEntry.setFirstEntry(idx); } void setNEntries(int n) { mROFEntry.setEntries(n); } + uint32_t getFlags() const { return mBits; } + void setFlags(uint32_t flags) { mBits = flags; } + void setFlag(uint8_t flagIndex) { mBits |= (1 << flagIndex); } + void resetFlag(uint8_t flagIndex) { mBits &= ~(1 << flagIndex); } + bool getFlag(uint8_t flagIndex) const { return mBits & (1 << flagIndex); } + void clearAllFlags() { mBits = 0; } + void setAllFlags() { mBits = ~0; } + const BCData& getBCData() const { return mBCData; } BCData& getBCData() { return mBCData; } EvIdx getEntry() const { return mROFEntry; } @@ -58,7 +68,6 @@ class ROFRecord mROFEntry.clear(); mBCData.clear(); } - void print() const; template gsl::span getROFData(const gsl::span tfdata) const @@ -84,12 +93,16 @@ class ROFRecord return i < getNEntries() ? &tfdata[getFirstEntry() + i] : nullptr; } + std::string asString() const; + void print() const; + friend std::ostream& operator<<(std::ostream& output, const ROFRecord& rec); + private: o2::InteractionRecord mBCData; // BC data for given trigger EvIdx mROFEntry; //< reference on the 1st object of the ROF in data ROFtype mROFrame = 0; //< frame ID - - ClassDefNV(ROFRecord, 2); + uint32_t mBits = 0; + ClassDefNV(ROFRecord, 3); }; /// this is a simple reference connecting (composed) MC event ID (from the EventRecord of the RunContext) @@ -105,7 +118,10 @@ struct MC2ROFRecord { MC2ROFRecord() = default; MC2ROFRecord(int evID, int rofRecID, ROFtype mnrof, ROFtype mxrof) : eventRecordID(evID), rofRecordID(rofRecID), minROF(mnrof), maxROF(mxrof) {} int getNROFs() const { return (rofRecordID < 0 || minROF > maxROF) ? 0 : (maxROF - minROF); } + std::string asString() const; void print() const; + friend std::ostream& operator<<(std::ostream& output, const MC2ROFRecord& rec); + ClassDefNV(MC2ROFRecord, 1); }; } // namespace itsmft diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/StuckPixelData.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/StuckPixelData.h new file mode 100644 index 0000000000000..1f15545be10f6 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/StuckPixelData.h @@ -0,0 +1,77 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file StuckPixelData.h +/// @brief CCDB-serializable container for stuck (repeating) pixel error records. +/// +/// Design rationale +/// ---------------- +/// TTree-based storage is intentionally avoided for CCDB objects because TTree +/// branches hold internal file-pointer state; serialising an in-memory TTree +/// via CcdbApi::createObjectImage() can silently drop the last unflushed basket. +/// A plain std::vector has no such issue: ROOT's TClass +/// machinery serialises it correctly via the generated dictionary, exactly as +/// it does for TimeDeadMap. + +#ifndef ITSMFT_STUCKPIXELDATA_H +#define ITSMFT_STUCKPIXELDATA_H + +#include +#include +#include // ClassDefNV + +namespace o2 +{ +namespace itsmft +{ + +/// One stuck-pixel (RepeatingPixel error) record. +struct StuckPixelEntry { + Long64_t orbit{0}; ///< first orbit of the TF in which the error was seen + uint16_t chipID{0}; ///< global chip ID (ITS only) + uint16_t row{0}; ///< pixel row + uint16_t col{0}; ///< pixel column + + StuckPixelEntry() = default; + StuckPixelEntry(Long64_t o, uint16_t c, uint16_t r, uint16_t col_) + : orbit(o), chipID(c), row(r), col(col_) {} + + ClassDefNV(StuckPixelEntry, 1); +}; + +/// CCDB payload object: a run-level collection of stuck-pixel records. +class StuckPixelData +{ + public: + StuckPixelData() = default; + ~StuckPixelData() = default; + + void addEntry(Long64_t orbit, uint16_t chipID, uint16_t row, uint16_t col) + { + mEntries.emplace_back(orbit, chipID, row, col); + } + + void clear() { mEntries.clear(); } + + const std::vector& getEntries() const { return mEntries; } + std::size_t size() const { return mEntries.size(); } + bool empty() const { return mEntries.empty(); } + + private: + std::vector mEntries; + + ClassDefNV(StuckPixelData, 1); +}; + +} // namespace itsmft +} // namespace o2 + +#endif // ITSMFT_STUCKPIXELDATA_H \ No newline at end of file diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TimeDeadMap.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TimeDeadMap.h new file mode 100644 index 0000000000000..6c7c01dc888b7 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TimeDeadMap.h @@ -0,0 +1,87 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TimeDeadMap.h +/// \brief Definition of the ITSMFT time-dependend dead map +#ifndef ALICEO2_ITSMFT_TIMEDEADMAP_H +#define ALICEO2_ITSMFT_TIMEDEADMAP_H + +#include + +#include +#include +#include +#include + +namespace o2 +{ + +namespace itsmft +{ + +class NoiseMap; + +class TimeDeadMap +{ + public: + // Constructor + TimeDeadMap(std::map>& deadmap) + { + mEvolvingDeadMap.swap(deadmap); + } + + /// Constructor + TimeDeadMap() = default; + /// Destructor + ~TimeDeadMap() = default; + + void fillMap(unsigned long firstOrbit, const std::vector& deadVect) + { + mEvolvingDeadMap[firstOrbit] = deadVect; + }; + + void fillMap(const std::vector& deadVect) + { + mStaticDeadMap = deadVect; + } + + void clear() + { + mEvolvingDeadMap.clear(); + mStaticDeadMap.clear(); + } + + void decodeMap(NoiseMap& noisemap) const; + void decodeMap(unsigned long orbit, o2::itsmft::NoiseMap& noisemap, bool includeStaticMap = true, long orbitGapAllowed = 330000) const; + std::string getMapVersion() const { return mMAP_VERSION; }; + + unsigned long getEvolvingMapSize() const { return mEvolvingDeadMap.size(); }; + std::vector getEvolvingMapKeys() const; + void getStaticMap(std::vector& mmap) const { mmap = mStaticDeadMap; }; + long getMapAtOrbit(unsigned long orbit, std::vector& mmap) const; + void setMapVersion(std::string version) { mMAP_VERSION = version; }; + + bool isDefault() const { return mIsDefaultObject; }; + void setAsDefault(bool isdef = true) { mIsDefaultObject = isdef; }; + + private: + bool mIsDefaultObject = false; + std::string mMAP_VERSION = "3"; + std::map> mEvolvingDeadMap; ///< Internal dead chip map representation. key = orbit + std::vector mStaticDeadMap; ///< To store map valid for every orbit. Filled starting from version = 4. + + ClassDefNV(TimeDeadMap, 2); +}; + +} // namespace itsmft +} // namespace o2 + +#endif /* ALICEO2_ITSMFT_TIMEDEADMAP_H */ diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h index 00ae135d8610f..cd5e46c9faa26 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h @@ -54,8 +54,8 @@ struct GroupStruct { float mErrZ; ///< Error associated to the hit point in the z direction. float mErr2X; ///< Squared Error associated to the hit point in the x direction. float mErr2Z; ///< Squared Error associated to the hit point in the z direction. - float mXCOG; ///< x position of te COG wrt the boottom left corner of the bounding box - float mZCOG; ///< z position of te COG wrt the boottom left corner of the bounding box + float mXCOG; ///< x position of the COG wrt the bottom left corner of the bounding box + float mZCOG; ///< z position of the COG wrt the bottom left corner of the bounding box int mNpixels; ///< Number of fired pixels ClusterPattern mPattern; ///< Bitmask of pixels. For groups the biggest bounding box for the group is taken, with all /// the bits set to 1. @@ -92,67 +92,67 @@ class TopologyDictionary /// Returns the x position of the COG for the n_th element inline float getXCOG(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mXCOG; } /// Returns the error on the x position of the COG for the n_th element inline float getErrX(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mErrX; } /// Returns the z position of the COG for the n_th element inline float getZCOG(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mZCOG; } /// Returns the error on the z position of the COG for the n_th element inline float getErrZ(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mErrZ; } /// Returns the error^2 on the x position of the COG for the n_th element inline float getErr2X(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mErr2X; } /// Returns the error^2 on the z position of the COG for the n_th element inline float getErr2Z(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mErr2Z; } /// Returns the hash of the n_th element inline unsigned long getHash(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mHash; } /// Returns the number of fired pixels of the n_th element inline int getNpixels(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mNpixels; } /// Returns the frequency of the n_th element; inline double getFrequency(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mFrequency; } /// Returns true if the element corresponds to a group of rare topologies inline bool isGroup(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mIsGroup; } /// Returns the pattern of the topology inline const ClusterPattern& getPattern(int n) const { - assert(n >= 0 || n < (int)mVectorOfIDs.size()); + assert(n >= 0 && n < (int)mVectorOfIDs.size()); return mVectorOfIDs[n].mPattern; } diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h index 164fd850fd539..27b059cc61ecf 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h @@ -25,12 +25,33 @@ namespace itsmft // can refer to max 15 indices in the vector of total length <268435456, i.e. 17895697 tracks in worst case struct TrkClusRef : public o2::dataformats::RangeRefComp<4> { using o2::dataformats::RangeRefComp<4>::RangeRefComp; + uint32_t clsizes = 0; ///< cluster sizes for each layer uint16_t pattern = 0; ///< layers pattern GPUd() int getNClusters() const { return getEntries(); } - bool hasHitOnLayer(int i) { return pattern & (0x1 << i); } - - ClassDefNV(TrkClusRef, 1); + bool hasHitOnLayer(int i) const { return pattern & (0x1 << i); } + + void setClusterSize(int l, int size) + { + if (l >= 8) + return; + if (size > 15) + size = 15; + clsizes &= ~(0xf << (l * 4)); + clsizes |= (size << (l * 4)); + } + + int getClusterSize(int l) const + { + return (l >= 8) ? 0 : (clsizes >> (l * 4)) & 0xf; + } + + int getClusterSizes() const + { + return clsizes; + } + + ClassDefNV(TrkClusRef, 2); }; } // namespace itsmft diff --git a/DataFormats/Detectors/ITSMFT/common/src/CompCluster.cxx b/DataFormats/Detectors/ITSMFT/common/src/CompCluster.cxx index 95ecd73f6e9d5..1f4be3163b3d2 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/CompCluster.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/CompCluster.cxx @@ -15,22 +15,32 @@ #include "DataFormatsITSMFT/CompCluster.h" #include #include +#include using namespace o2::itsmft; std::ostream& operator<<(std::ostream& stream, const CompCluster& cl) { - stream << " row: " << cl.getRow() << " col: " << cl.getCol() - << " pattID " << cl.getPatternID() << " [flag: " << cl.getFlag() << "] "; + stream << cl.asString(); return stream; } std::ostream& operator<<(std::ostream& stream, const CompClusterExt& cl) { - stream << " chip: " << cl.getChipID() << ((const CompCluster&)cl); + stream << cl.asString(); return stream; } +std::string CompCluster::asString() const +{ + return std::format(" row: {:4d} col: {:4d} pattID: {:4d} [flag: {:1d}]", getRow(), getCol(), getPatternID(), getFlag()); +} + +std::string CompClusterExt::asString() const +{ + return std::format(" chip: {:5d} row: {:4d} col: {:4d} pattID: {:4d} [flag: {:1d}]", getChipID(), getRow(), getCol(), getPatternID(), getFlag()); +} + //______________________________________________________________________________ void CompCluster::print() const { diff --git a/Detectors/ITSMFT/common/base/src/DPLAlpideParam.cxx b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParam.cxx similarity index 82% rename from Detectors/ITSMFT/common/base/src/DPLAlpideParam.cxx rename to DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParam.cxx index 1cb9bdf997d68..205f8a008a661 100644 --- a/Detectors/ITSMFT/common/base/src/DPLAlpideParam.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParam.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -9,15 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" -namespace o2 -{ -namespace itsmft +namespace o2::itsmft { // this makes sure that the constructor of the parameters is statically called // so that these params are part of the parameter database static auto& sAlpideParamITS = o2::itsmft::DPLAlpideParam::Instance(); static auto& sAlpideParamMFT = o2::itsmft::DPLAlpideParam::Instance(); -} // namespace itsmft -} // namespace o2 +} // namespace o2::itsmft diff --git a/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx new file mode 100644 index 0000000000000..715ec5d90b813 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx @@ -0,0 +1,46 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" +#include "Framework/ConfigParamsHelper.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/ConfigContext.h" + +namespace o2::itsmft +{ + +void DPLAlpideParamInitializer::addConfigOption(std::vector& opts) +{ + addITSConfigOption(opts); + addMFTConfigOption(opts); +} + +void DPLAlpideParamInitializer::addITSConfigOption(std::vector& opts) +{ + o2::framework::ConfigParamsHelper::addOptionIfMissing(opts, {stagITSOpt, o2::framework::VariantType::Bool, stagDef, {"enable per layer ITS in&out-put for staggered readout"}}); +} + +void DPLAlpideParamInitializer::addMFTConfigOption(std::vector& opts) +{ + o2::framework::ConfigParamsHelper::addOptionIfMissing(opts, {stagMFTOpt, o2::framework::VariantType::Bool, stagDef, {"enable per layer MFT in&out-put for staggered readout"}}); +} + +bool DPLAlpideParamInitializer::isITSStaggeringEnabled(const o2::framework::ConfigContext& cfgc) +{ + return cfgc.options().get(stagITSOpt); +} + +bool DPLAlpideParamInitializer::isMFTStaggeringEnabled(const o2::framework::ConfigContext& cfgc) +{ + return cfgc.options().get(stagMFTOpt); +} + +} // namespace o2::itsmft diff --git a/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h b/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h index 5edd964ce2cd0..f4ced8b1a8353 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h +++ b/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h @@ -15,8 +15,17 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::itsmft::DPLAlpideParam < o2::detectors::DetID::ITS> + ; +#pragma link C++ class o2::itsmft::DPLAlpideParam < o2::detectors::DetID::MFT> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::DPLAlpideParam < o2::detectors::DetID::ITS>> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::DPLAlpideParam < o2::detectors::DetID::MFT>> + ; + #pragma link C++ class o2::itsmft::Digit + ; #pragma link C++ class o2::itsmft::NoiseMap + ; +#pragma link C++ class o2::itsmft::TimeDeadMap + ; +#pragma link C++ class o2::itsmft::StuckPixelEntry + ; +#pragma link C++ class std::vector < o2::itsmft::StuckPixelEntry> + ; +#pragma link C++ class o2::itsmft::StuckPixelData + ; #pragma link C++ class std::vector < o2::itsmft::Digit> + ; #pragma link C++ class o2::itsmft::GBTCalibData + ; @@ -49,6 +58,8 @@ #pragma link C++ class o2::itsmft::CTF + ; #pragma link C++ class o2::ctf::EncodedBlocks < o2::itsmft::CTFHeader, 10, uint32_t> + ; +#pragma link C++ class std::map < unsigned long, std::vector < uint16_t>> + ; + #pragma link C++ function o2::itsmft::getROFData(const gsl::span tfdata) const; #pragma link C++ function o2::itsmft::getROFData(const gsl::span tfdata) const; #pragma link C++ function o2::itsmft::getROFData(const gsl::span tfdata) const; diff --git a/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx b/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx index fcc86ac7e7dca..8dbde0d580efc 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx @@ -9,20 +9,42 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "DataFormatsITSMFT/ROFRecord.h" #include +#include + +#include "DataFormatsITSMFT/ROFRecord.h" +#include "Framework/Logger.h" using namespace o2::itsmft; +std::string ROFRecord::asString() const +{ + return std::format("ROF: {} | {} entries starting from {} | IR: {}", mROFrame, getNEntries(), getFirstEntry(), mBCData.asString()); +} + void ROFRecord::print() const { - std::cout << "ROF: " << mROFrame << " | " << getNEntries() << " entries starting from " - << getFirstEntry() << std::endl; - mBCData.print(); + LOG(info) << asString(); +} + +std::ostream& operator<<(std::ostream& stream, ROFRecord const& rec) +{ + stream << rec.asString(); + return stream; +} + +std::string MC2ROFRecord::asString() const +{ + return std::format("MCEventID: {} ROFs: {}-{} Entry in ROFRecords: {}", eventRecordID, minROF, maxROF, rofRecordID); } void MC2ROFRecord::print() const { - std::cout << "MCEventID: " << eventRecordID << " ROFs: " << minROF << '-' << maxROF - << " Entry in ROFRecords: " << rofRecordID << std::endl; + LOG(info) << asString(); +} + +std::ostream& operator<<(std::ostream& stream, MC2ROFRecord const& rec) +{ + stream << rec.asString(); + return stream; } diff --git a/DataFormats/Detectors/ITSMFT/common/src/TimeDeadMap.cxx b/DataFormats/Detectors/ITSMFT/common/src/TimeDeadMap.cxx new file mode 100644 index 0000000000000..e3df8e7f91f86 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/src/TimeDeadMap.cxx @@ -0,0 +1,100 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TimeDeadMap.cxx +/// \brief Implementation of the time-dependent map + +#include "DataFormatsITSMFT/TimeDeadMap.h" +#include "DataFormatsITSMFT/NoiseMap.h" +#include "Framework/Logger.h" + +using namespace o2::itsmft; + +void TimeDeadMap::decodeMap(o2::itsmft::NoiseMap& noisemap) const +{ // for static part only + if (mMAP_VERSION == "3") { + LOG(error) << "Trying to decode static part of deadmap version " << mMAP_VERSION << ". Not implemented, doing nothing."; + return; + } + for (int iel = 0; iel < mStaticDeadMap.size(); iel++) { + uint16_t w = mStaticDeadMap[iel]; + noisemap.maskFullChip(w & 0x7FFF); + if (w & 0x8000) { + for (int w2 = (w & 0x7FFF) + 1; w2 < mStaticDeadMap.at(iel + 1); w2++) { + noisemap.maskFullChip(w2); + } + } + } +} + +void TimeDeadMap::decodeMap(unsigned long orbit, o2::itsmft::NoiseMap& noisemap, bool includeStaticMap, long orbitGapAllowed) const +{ // for time-dependent and (optionally) static part. Use orbitGapAllowed = -1 to ignore check on orbit difference + + if (mMAP_VERSION != "3" && mMAP_VERSION != "4") { + LOG(error) << "Trying to decode time-dependent deadmap version " << mMAP_VERSION << ". Not implemented, doing nothing."; + return; + } + + if (mEvolvingDeadMap.empty()) { + LOG(warning) << "Time-dependent dead map is empty. Doing nothing."; + return; + } + + std::vector closestVec; + long dT = getMapAtOrbit(orbit, closestVec); + + if (orbitGapAllowed >= 0 && std::abs(dT) > orbitGapAllowed) { + LOG(warning) << "Requested orbit " << orbit << ", found " << orbit - dT << ". Orbit gap is too high, skipping time-dependent map."; + closestVec.clear(); + } + + // add static part if requested. something may be masked twice + if (includeStaticMap && mMAP_VERSION != "3") { + closestVec.insert(closestVec.end(), mStaticDeadMap.begin(), mStaticDeadMap.end()); + } + + // vector encoding: if 1<<15 = 0x8000 is set, the word encodes the first element of a range, with mask (1<<15)-1 = 0x7FFF. The last element of the range is the next in the vector. + + for (int iel = 0; iel < closestVec.size(); iel++) { + uint16_t w = closestVec.at(iel); + noisemap.maskFullChip(w & 0x7FFF); + if (w & 0x8000) { + for (int w2 = (w & 0x7FFF) + 1; w2 < closestVec.at(iel + 1); w2++) { + noisemap.maskFullChip(w2); + } + } + } +} + +std::vector TimeDeadMap::getEvolvingMapKeys() const +{ + std::vector keys; + std::transform(mEvolvingDeadMap.begin(), mEvolvingDeadMap.end(), std::back_inserter(keys), + [](const auto& O) { return O.first; }); + return keys; +} + +long TimeDeadMap::getMapAtOrbit(unsigned long orbit, std::vector& mmap) const +{ // fills mmap and returns requested_orbit - found_orbit. Found orbit is the highest key lower or equal to the requested one + if (mEvolvingDeadMap.empty()) { + LOG(warning) << "Requested orbit " << orbit << "from an empty time-dependent map. Doing nothing"; + return (long)orbit; + } + auto closest = mEvolvingDeadMap.upper_bound(orbit); + if (closest != mEvolvingDeadMap.begin()) { + --closest; + mmap = closest->second; + return (long)orbit - closest->first; + } else { + mmap = mEvolvingDeadMap.begin()->second; + return (long)(orbit)-mEvolvingDeadMap.begin()->first; + } +} diff --git a/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/DsChannelId.h b/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/DsChannelId.h index 6485186b66463..cd11d4b71389c 100644 --- a/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/DsChannelId.h +++ b/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/DsChannelId.h @@ -61,5 +61,13 @@ class DsChannelId ClassDefNV(DsChannelId, 1); // class for MCH readout channel }; + +inline bool operator==(const DsChannelId& a, const DsChannelId& b) { return a.value() == b.value(); } +inline bool operator!=(const DsChannelId& a, const DsChannelId& b) { return !(a == b); } +inline bool operator<(const DsChannelId& a, const DsChannelId& b) { return a.value() < b.value(); } +inline bool operator>(const DsChannelId& a, const DsChannelId& b) { return b < a; } +inline bool operator<=(const DsChannelId& a, const DsChannelId& b) { return !(a > b); } +inline bool operator>=(const DsChannelId& a, const DsChannelId& b) { return !(a < b); } + } // namespace o2::mch #endif diff --git a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx b/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx deleted file mode 100644 index bcf10d74c95ff..0000000000000 --- a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "DataFormatsMCH/DsChannelGroup.h" - -std::string o2::mch::DsChannelId() const -{ -} diff --git a/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/Track.h b/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/Track.h index 5bd9be2609444..060538fc91654 100644 --- a/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/Track.h +++ b/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/Track.h @@ -176,9 +176,21 @@ class Track /// \li \c 3 track can be used to estimate local board efficiency int getEfficiencyFlag() const { return (mEfficiencyWord >> 24) & 0xF; } + /// record that the track misses information on that cathode + void setIncomplete(int cathode) { mIncomplete |= 1 << cathode; } + + /// reset the flag for missing cathode information + void resetIncomplete() { mIncomplete = 0; } + + /// return true is the track misses information on that cathode + bool isIncomplete(int cathode) const { return mIncomplete & (1 << cathode); } + /// Overload ostream operator for MID track friend std::ostream& operator<<(std::ostream& stream, const Track& track); + /// set efficiency word (public function) + void setEfficiencyWord(uint32_t efficiencyWord) { mEfficiencyWord = efficiencyWord; } + private: /// Set portions of the efficiency word /// \param pos Position in the word @@ -192,8 +204,9 @@ class Track float mChi2 = 0.; ///< Chi2 of track int mNDF = 0; ///< Number of chi2 degrees of freedom uint32_t mEfficiencyWord = 0; ///< Efficiency word + uint8_t mIncomplete = 0; //!< Flag for missing cathode information - ClassDefNV(Track, 1); + ClassDefNV(Track, 2); }; } // namespace mid } // namespace o2 diff --git a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventData.h b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventData.h index 4f5d243028177..e503af30d8e7f 100644 --- a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventData.h +++ b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventData.h @@ -13,10 +13,11 @@ #define ALICEO2_PHOS_EVENTDATA_H_ #include +#include #include "CommonDataFormat/InteractionRecord.h" #include "DataFormatsPHOS/Cell.h" #include "DataFormatsPHOS/Cluster.h" - +#include "DataFormatsPHOS/MCLabel.h" namespace o2 { @@ -25,10 +26,11 @@ namespace phos template struct EventData { - InteractionRecord mInteractionRecord; ///< Interaction record for the trigger corresponding to this event - gsl::span mClusters; ///< PHOS clusters - gsl::span mCells; ///< PHOS cells / digits - gsl::span mCellIndices; ///< Cell indices in cluster + InteractionRecord mInteractionRecord; ///< Interaction record for the trigger corresponding to this event + gsl::span mClusters; ///< PHOS clusters + gsl::span mCells; ///< PHOS cells / digits + gsl::span mCellIndices; ///< Cell indices in cluster + std::vector> mMCCellLabels; ///< span of MC labels for each cell /// \brief Reset event structure with empty interaction record and ranges void reset() @@ -37,13 +39,14 @@ struct EventData { mClusters = gsl::span(); mCells = gsl::span(); mCellIndices = gsl::span(); + mMCCellLabels = std::vector>(); } - ClassDefNV(EventData, 1); + ClassDefNV(EventData, 2); }; } // namespace phos } // namespace o2 -#endif // ALICEO2_PHOS_EVENTDATA_H_ \ No newline at end of file +#endif // ALICEO2_PHOS_EVENTDATA_H_ diff --git a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventHandler.h b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventHandler.h index 149bcc5505bf0..0d3285248d7b2 100644 --- a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventHandler.h +++ b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventHandler.h @@ -15,13 +15,16 @@ #include #include #include +#include #include "Rtypes.h" #include "fmt/format.h" #include "DataFormatsPHOS/Cell.h" #include "DataFormatsPHOS/Cluster.h" #include "DataFormatsPHOS/Digit.h" #include "DataFormatsPHOS/EventData.h" +#include "DataFormatsPHOS/MCLabel.h" #include "DataFormatsPHOS/TriggerRecord.h" +#include "SimulationDataFormat/MCTruthContainer.h" namespace o2 { @@ -259,6 +262,13 @@ class EventHandler /// \throw NotInitializedException in case the event handler is not initialized for cell const CellRange getCellsForEvent(int eventID) const; + /// \brief Get vector of MC labels belonging to the given event + /// \param eventID ID of the event + /// \return vector of MC labels for the event + /// \throw RangeException in case the required event ID exceeds the maximum number of events + /// \throw NotInitializedException in case the event handler is not initialized for cell + std::vector> getCellMCLabelForEvent(int eventID) const; + /// \brief Get range of cluster cell indices belonging to the given event /// \param eventID ID of the event /// \return Cluster cell index range for the event @@ -300,6 +310,13 @@ class EventHandler mTriggerRecordsCells = triggers; } + /// \brief Setting the pointer for the MCTruthContainer for cells + /// \param mclabels Pointer to the MCTruthContainer for cells in timeframe + void setCellMCTruthContainer(const o2::dataformats::MCTruthContainer* mclabels) + { + mCellLabels = mclabels; + } + /// \brief Reset containers with empty ranges void reset(); @@ -329,14 +346,15 @@ class EventHandler TriggerRange mTriggerRecordsCellIndices; ///< trigger record for cluster cell index type TriggerRange mTriggerRecordsCells; ///< Trigger record for cell type - ClusterRange mClusters; /// container for clusters in timeframe - CellIndexRange mClusterCellIndices; /// container for cell indices in timeframe - CellRange mCells; /// container for cells in timeframe + ClusterRange mClusters; /// container for clusters in timeframe + CellIndexRange mClusterCellIndices; /// container for cell indices in timeframe + CellRange mCells; /// container for cells in timeframe + const o2::dataformats::MCTruthContainer* mCellLabels = nullptr; /// pointer to the MCTruthContainer for cells in timeframe - ClassDefNV(EventHandler, 1); + ClassDefNV(EventHandler, 2); }; } // namespace phos } // namespace o2 -#endif // ALICEO2_PHOS_EVENTHANDLER_H__ \ No newline at end of file +#endif // ALICEO2_PHOS_EVENTHANDLER_H__ diff --git a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/MCLabel.h b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/MCLabel.h index a9aaa30243c0a..aa171e9825870 100644 --- a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/MCLabel.h +++ b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/MCLabel.h @@ -23,7 +23,7 @@ namespace phos class MCLabel : public o2::MCCompLabel { private: - float mEdep = 0; //deposited energy + float mEdep = 0; // deposited energy public: MCLabel() = default; @@ -44,7 +44,9 @@ class MCLabel : public o2::MCCompLabel float getEdep() const { return mEdep; } - ClassDefNV(MCLabel, 1); + double getAmplitudeFraction() const { return mEdep; } + + ClassDefNV(MCLabel, 2); }; } // namespace phos } // namespace o2 diff --git a/DataFormats/Detectors/PHOS/src/Digit.cxx b/DataFormats/Detectors/PHOS/src/Digit.cxx index bd4168cc19422..0b2a34c2863ad 100644 --- a/DataFormats/Detectors/PHOS/src/Digit.cxx +++ b/DataFormats/Detectors/PHOS/src/Digit.cxx @@ -23,7 +23,7 @@ Digit::Digit(short absId, float amplitude, float time, int label) { } Digit::Digit(short truId, float amplitude, float time, bool isTrigger2x2, int /*dummy*/) - : DigitBase(time), mIsHighGain(true), mAbsId(truId + NREADOUTCHANNELS), mLabel(-1), mAmplitude(amplitude), mTime(time) + : DigitBase(time), mIsHighGain(true), mAbsId(truId), mLabel(-1), mAmplitude(amplitude), mTime(time) { setHighGain(isTrigger2x2); } diff --git a/DataFormats/Detectors/PHOS/src/EventHandler.cxx b/DataFormats/Detectors/PHOS/src/EventHandler.cxx index 1e74bf132687b..26e5fe15490b5 100644 --- a/DataFormats/Detectors/PHOS/src/EventHandler.cxx +++ b/DataFormats/Detectors/PHOS/src/EventHandler.cxx @@ -108,6 +108,23 @@ const typename EventHandler::CellRange EventHandler +std::vector> EventHandler::getCellMCLabelForEvent(int eventID) const +{ + if (mCellLabels && mTriggerRecordsCells.size()) { + if (eventID >= mTriggerRecordsCells.size()) { + throw RangeException(eventID, mTriggerRecordsCells.size()); + } + auto& trgrecord = mTriggerRecordsCells[eventID]; + std::vector> eventlabels(trgrecord.getNumberOfObjects()); + for (int index = 0; index < trgrecord.getNumberOfObjects(); index++) { + eventlabels[index] = mCellLabels->getLabels(trgrecord.getFirstEntry() + index); + } + return eventlabels; + } + throw NotInitializedException(); +} + template const typename EventHandler::CellIndexRange EventHandler::getClusterCellIndicesForEvent(int eventID) const { @@ -130,6 +147,7 @@ void EventHandler::reset() mClusters = ClusterRange(); mClusterCellIndices = CellIndexRange(); mCells = CellRange(); + mCellLabels = nullptr; } template @@ -146,6 +164,9 @@ EventData EventHandler::buildEvent(int eventID) co if (hasCells()) { outputEvent.mCells = getCellsForEvent(eventID); } + if (mCellLabels) { + outputEvent.mMCCellLabels = getCellMCLabelForEvent(eventID); + } return outputEvent; } diff --git a/DataFormats/Detectors/TOF/CMakeLists.txt b/DataFormats/Detectors/TOF/CMakeLists.txt index 03dbd9275edf9..4d41167f7bf1d 100644 --- a/DataFormats/Detectors/TOF/CMakeLists.txt +++ b/DataFormats/Detectors/TOF/CMakeLists.txt @@ -9,6 +9,14 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +o2_add_library(DataFormatsParamTOF + SOURCES src/ParameterContainers.cxx + PUBLIC_LINK_LIBRARIES O2::FrameworkLogger) + + +o2_target_root_dictionary(DataFormatsParamTOF + HEADERS include/DataFormatsTOF/ParameterContainers.h) + o2_add_library(DataFormatsTOF SOURCES src/Cluster.cxx src/CalibInfoTOFshort.cxx @@ -16,13 +24,13 @@ o2_add_library(DataFormatsTOF src/CalibLHCphaseTOF.cxx src/CalibTimeSlewingParamTOF.cxx src/CTF.cxx - src/ParameterContainers.cxx src/CalibInfoCluster.cxx src/CosmicInfo.cxx src/Diagnostic.cxx src/TOFFEElightInfo.cxx PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats O2::GPUCommon + O2::DataFormatsParamTOF Boost::serialization) o2_target_root_dictionary(DataFormatsTOF @@ -34,7 +42,6 @@ o2_target_root_dictionary(DataFormatsTOF include/DataFormatsTOF/RawDataFormat.h include/DataFormatsTOF/CompressedDataFormat.h include/DataFormatsTOF/CTF.h - include/DataFormatsTOF/ParameterContainers.h include/DataFormatsTOF/CalibInfoCluster.h include/DataFormatsTOF/CosmicInfo.h include/DataFormatsTOF/TOFFEElightInfo.h diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h index 57d5784dc3b66..881f87ff88971 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h @@ -56,14 +56,14 @@ struct CompressedInfos { */ // ROF header data - std::vector bcIncROF; /// increment of ROF BC wrt BC of previous ROF - std::vector orbitIncROF; /// increment of ROF orbit wrt orbit of previous ROF + std::vector bcIncROF; /// increment of ROF BC wrt BC of previous ROF + std::vector orbitIncROF; /// increment of ROF orbit wrt orbit of previous ROF std::vector ndigROF; /// number of digits in ROF std::vector ndiaROF; /// number of diagnostic/pattern words in ROF std::vector ndiaCrate; /// number of diagnostic/pattern words per crate in ROF // Hit data - std::vector timeFrameInc; /// time increment with respect of previous digit in TimeFrame units + std::vector timeFrameInc; /// time increment with respect of previous digit in TimeFrame units std::vector timeTDCInc; /// time increment with respect of previous digit in TDC channel (about 24.4 ps) within timeframe std::vector stripID; /// increment of stripID wrt that of prev. strip std::vector chanInStrip; /// channel in strip 0-95 (ordered in time) diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h index 589afc8a2cde9..37d3ca23ddb35 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h @@ -49,14 +49,18 @@ class Cluster : public o2::BaseCluster kDownRight = 4, // 2^4, 5th bit kDown = 5, // 2^5, 6th bit kDownLeft = 6, // 2^6, 7th bit - kLeft = 7 }; // 2^7, 8th bit + kLeft = 7 // 2^7, 8th bit + }; Cluster() = default; - Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1latency, int deltaBC); + Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1latency, int deltaBC, float geanttime = 0.0, double t0 = 0.0); ~Cluster() = default; + bool isInNominalSector() const { return mInNominalSector; } + void setInNominalSector(bool v = true) { mInNominalSector = v; } + std::int8_t getSector() const { return getCount(); } void setSector(std::int8_t value) { setCount(value); } @@ -134,6 +138,10 @@ class Cluster : public o2::BaseCluster int getDigitInfoCH(int idig) const { return mDigitInfoCh[idig]; } double getDigitInfoT(int idig) const { return mDigitInfoT[idig]; } float getDigitInfoTOT(int idig) const { return mDigitInfoTOT[idig]; } + float getTgeant() const { return mTgeant; } + void setTgeant(float val) { mTgeant = val; } + double getT0true() const { return mT0true; } + void setT0true(double val) { mT0true = val; } private: #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) @@ -153,8 +161,11 @@ class Cluster : public o2::BaseCluster int mDigitInfoCh[6] = {0, 0, 0, 0, 0, 0}; double mDigitInfoT[6] = {0., 0., 0., 0., 0., 0.}; float mDigitInfoTOT[6] = {0., 0., 0., 0., 0., 0.}; + float mTgeant = 0.0; + bool mInNominalSector = false; + double mT0true = 0.0; - ClassDefNV(Cluster, 4); + ClassDefNV(Cluster, 6); }; #ifndef GPUCA_GPUCODE diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h index 8adcdb63e9d21..028da04b3ef70 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h @@ -44,6 +44,13 @@ class Diagnostic uint32_t fillEmptyTOF(uint32_t frequency = 1) { return fill(1, frequency); } static ULong64_t getEmptyCrateKey(int crate); static ULong64_t getNoisyChannelKey(int channel); + static ULong64_t getDRMKey(int crate) { return 1000000 + crate * 1000; } + static ULong64_t getDRMerrorKey(int crate, int error) { return getDRMKey(crate) + error; } + uint32_t getFrequencyDRM(int crate) const { return getFrequency(getDRMKey(crate)); } + uint32_t getFrequencyDRMerror(int crate, int error) const { return getFrequency(getDRMerrorKey(crate, error)); } + uint32_t fillDRM(int crate, uint32_t frequency) { return fill(getDRMKey(crate), frequency); } + uint32_t fillDRMerror(int crate, int error, uint32_t frequency) { return fill(getDRMerrorKey(crate, error), frequency); } + static ULong64_t getTRMKey(int crate, int trm); void print(bool longFormat = false) const; void clear() { mVector.clear(); } diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h index 9815bd734b458..224906e43b8c6 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h @@ -18,10 +18,10 @@ #ifndef O2_TOF_PARAMCONTAINER_H #define O2_TOF_PARAMCONTAINER_H -#include "TNamed.h" -#include "TFile.h" -#include "Framework/Logger.h" -#include "map" +#include +#include +#include +#include namespace o2 { @@ -70,7 +70,7 @@ class Parameters { LOG(info) << "Parameters '" << mName << "'"; for (int i = 0; i < nPar; i++) { - LOG(info) << "Parameter " << i << "/" << nPar - 1 << " is " << mPar[i]; + LOG(info) << "Parameter " << i << "/" << nPar - 1 << " " << mParNames[i] << " is " << mPar[i]; } } @@ -88,7 +88,11 @@ class Parameters void loadParamFromFile(const TString FileName, const TString ParamName) { TFile f(FileName, "READ"); + if (!f.IsOpen()) { + LOG(fatal) << "Could not open file " << FileName; + } if (!f.Get(ParamName)) { + f.ls(); LOG(fatal) << "Did not find parameters " << ParamName << " in file " << FileName; } LOG(info) << "Loading parameters " << ParamName << " from TFile " << FileName; @@ -169,7 +173,7 @@ class ParameterCollection : public TNamed LOG(debug) << "Did not find parameter '" << name << "' in collection, keeping preexisting"; continue; } - LOG(debug) << "Found parameter '" << name << "' in collection, keeping preexisting"; + LOG(debug) << "Found parameter '" << name << "' in collection, updating from " << p[i] << " to " << toGet.at(name); p.setParameter(i, toGet.at(name)); } return true; @@ -206,7 +210,13 @@ class ParameterCollection : public TNamed } /// @brief getter for the parameters stored in the container matching to a pass - const auto& getPars(const std::string& pass) const { return mParameters.at(pass); } + const std::unordered_map& getPars(const std::string& pass) const + { + if (!hasKey(pass)) { + LOG(fatal) << "Parameters for pass " << pass << " not found!"; + } + return mParameters.at(pass); + } /// @brief printing function for the content of the pass /// @param pass pass to print @@ -217,7 +227,7 @@ class ParameterCollection : public TNamed /// @brief Getter of the full map of parameters stored in the container /// @return returns the full map of parameters - const auto& getFullMap() { return mParameters; } + const std::unordered_map>& getFullMap() const { return mParameters; } /// Loader from file /// \param FileName name of the input file @@ -225,7 +235,11 @@ class ParameterCollection : public TNamed void loadParamFromFile(const TString FileName, const TString ParamName) { TFile f(FileName, "READ"); + if (!f.IsOpen()) { + LOG(fatal) << "Could not open file " << FileName; + } if (!f.Get(ParamName)) { + f.ls(); LOG(fatal) << "Did not find parameters " << ParamName << " in file " << FileName; } LOG(info) << "Loading parameters " << ParamName << " from TFile " << FileName; diff --git a/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx b/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx index 350af55dfc0ee..01df4f2323edd 100644 --- a/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx +++ b/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx @@ -160,6 +160,8 @@ float CalibTimeSlewingParamTOF::evalTimeSlewing(int channel, float totIn) const } int n = (*(mChannelStart[sector]))[channel]; + int nFirst = n; + if (n < 0) { return (*(mGlobalOffset[sector]))[channel]; } @@ -197,10 +199,14 @@ float CalibTimeSlewingParamTOF::evalTimeSlewing(int channel, float totIn) const return (float)((*(mTimeSlewing[sector]))[n].second + (*(mGlobalOffset[sector]))[channel]); // use the last value stored for that channel } + if (n < nFirst) { // this is before the first point (probably tot = 0 calibration is missing) + return (*(mGlobalOffset[sector]))[channel]; + } + float w1 = (float)(tot - (*(mTimeSlewing[sector]))[n].first); float w2 = (float)((*(mTimeSlewing[sector]))[n + 1].first - tot); - return (float)((*(mGlobalOffset[sector]))[channel] + (((*(mTimeSlewing[sector]))[n].second * w2 + (*(mTimeSlewing[sector]))[n + 1].second * w1) / ((*(mTimeSlewing[sector]))[n + 1].first - (*(mTimeSlewing[sector]))[n].first))); + return (float)((*(mGlobalOffset[sector]))[channel] + (((*(mTimeSlewing[sector]))[n].second * w2 + (*(mTimeSlewing[sector]))[n + 1].second * w1) / (w1 + w2))); } //______________________________________________ void CalibTimeSlewingParamTOF::setTimeSlewingInfo(int channel, float offsetold, int nold, const unsigned short* oldtot, const short* olddt, int nnew, const unsigned short* newtot, const short* newdt) @@ -212,56 +218,76 @@ void CalibTimeSlewingParamTOF::setTimeSlewingInfo(int channel, float offsetold, // work with float to avoid to loose precision due to short (in case offset will be adjust before to move deltat to short) std::vector deltat; - float minVal = 0; - float maxVal = 0; - - if (nnew >= nold) { // use new binning - int j = 0; - for (int i = 0; i < nnew; i++) { - while (j < nold && oldtot[j + 1] < newtot[i]) { - j++; + float minVal = 100000; + float maxVal = -100000; + + // new scheme + int k1 = 0; + int k2 = 0; + + int ntotFin = 0; + std::vector totFin; + + while (k1 < nnew && k2 < nold) { + if (newtot[k1] == oldtot[k2]) { + k1++; + } else if (newtot[k1] < oldtot[k2]) { + totFin.push_back(newtot[k1]); + if (k2 > 0) { + deltat.push_back(float(newdt[k1]) + olddt[k2 - 1]); + } else { + deltat.push_back(float(newdt[k1] + olddt[k2])); } - float val = float(newdt[i]) + olddt[j]; - // printf("%d %d -> %f\n",i,j,val); - deltat.push_back(val); - if (val < minVal) { - minVal = val; + k1++; + } else { + totFin.push_back(oldtot[k2]); + if (k1 > 0) { + deltat.push_back(float(olddt[k2]) + newdt[k1 - 1]); + } else { + deltat.push_back(float(olddt[k2] + newdt[k1])); } - if (val > maxVal) { - maxVal = val; - } - } - float recentering = (minVal + maxVal) * 0.5; - (*(mGlobalOffset[sector]))[channel] = offsetold + recentering; - // printf("update channel offset for sec=%d/ch=%d -> %f\n",sector,channel,(*(mGlobalOffset[sector]))[channel]); - (*(mChannelStart[sector]))[channel] = mTimeSlewing[sector]->size(); - - for (int i = 0; i < nnew; i++) { - (*(mTimeSlewing[sector])).emplace_back(newtot[i], (short)(deltat[i] - recentering)); - // printf("%d) %d\n",i,(*(mTimeSlewing[sector]))[i]); + k2++; } - } else { // use old binning - int j = 0; - for (int i = 0; i < nold; i++) { - while (j < nnew && newtot[j + 1] < oldtot[i]) { - j++; - } - float val = float(olddt[i]) + newdt[j]; - deltat.push_back(val); - if (val < minVal) { - minVal = val; + } + + if (k1 < nnew) { + while (k1 < nnew) { + totFin.push_back(newtot[k1]); + if (nold) { + deltat.push_back(float(newdt[k1]) + olddt[nold - 1]); + } else { + deltat.push_back(float(newdt[k1])); } - if (val > maxVal) { - maxVal = val; + k1++; + } + } else if (k2 < nold) { + while (k2 < nold) { + totFin.push_back(oldtot[k2]); + if (nnew) { + deltat.push_back(float(olddt[k2]) + newdt[nnew - 1]); + } else { + deltat.push_back(float(olddt[k2])); } + k2++; + } + } + + for (int i = 0; i < deltat.size(); i++) { + if (deltat[i] < minVal) { + minVal = deltat[i]; } - float recentering = (minVal + maxVal) * 0.5; - (*(mGlobalOffset[sector]))[channel] = offsetold + recentering; - (*(mChannelStart[sector]))[channel] = mTimeSlewing[sector]->size(); - for (int i = 0; i < nold; i++) { - (*(mTimeSlewing[sector])).emplace_back(oldtot[i], (short)(deltat[i] - recentering)); + if (deltat[i] > maxVal) { + maxVal = deltat[i]; } } + + float recentering = (minVal + maxVal) * 0.5; + (*(mGlobalOffset[sector]))[channel] = offsetold + recentering; + (*(mChannelStart[sector]))[channel] = mTimeSlewing[sector]->size(); + + for (int i = 0; i < deltat.size(); i++) { + (*(mTimeSlewing[sector])).emplace_back(totFin[i], (short)(deltat[i] - recentering)); + } } //______________________________________________ void CalibTimeSlewingParamTOF::addTimeSlewingInfo(int channel, float tot, float time) diff --git a/DataFormats/Detectors/TOF/src/Cluster.cxx b/DataFormats/Detectors/TOF/src/Cluster.cxx index 2ca3edeb19f0a..a7f3473e0b61c 100644 --- a/DataFormats/Detectors/TOF/src/Cluster.cxx +++ b/DataFormats/Detectors/TOF/src/Cluster.cxx @@ -23,7 +23,7 @@ using namespace o2::tof; ClassImp(o2::tof::Cluster); -Cluster::Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1Latency, int deltaBC) : o2::BaseCluster(sensid, x, y, z, sy2, sz2, syz), mTimeRaw(timeRaw), mTime(time), mTot(tot), mL0L1Latency(L0L1Latency), mDeltaBC(deltaBC) +Cluster::Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1Latency, int deltaBC, float geanttime, double t0) : o2::BaseCluster(sensid, x, y, z, sy2, sz2, syz), mTimeRaw(timeRaw), mTime(time), mTot(tot), mL0L1Latency(L0L1Latency), mDeltaBC(deltaBC), mTgeant(geanttime), mT0true(t0) { // caching R and phi diff --git a/DataFormats/Detectors/TOF/src/DataFormatsParamTOFLinkDef.h b/DataFormats/Detectors/TOF/src/DataFormatsParamTOFLinkDef.h new file mode 100644 index 0000000000000..2d6ee84bedb92 --- /dev/null +++ b/DataFormats/Detectors/TOF/src/DataFormatsParamTOFLinkDef.h @@ -0,0 +1,17 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link C++ class o2::tof::Parameters < 5> + ; +#pragma link C++ class o2::tof::ParameterCollection + ; + +#endif diff --git a/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h b/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h index 55d1fd3973e70..03004e4c22afa 100644 --- a/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h +++ b/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h @@ -33,8 +33,6 @@ #pragma link C++ class std::vector < o2::dataformats::CalibInfoTOFshort> + ; #pragma link C++ class std::vector < o2::dataformats::CalibInfoTOF> + ; -#pragma link C++ class o2::tof::Parameters < 5> + ; -#pragma link C++ class o2::tof::ParameterCollection + ; #pragma link C++ class o2::tof::CTFHeader + ; #pragma link C++ class o2::tof::CompressedInfos + ; diff --git a/DataFormats/Detectors/TOF/src/Diagnostic.cxx b/DataFormats/Detectors/TOF/src/Diagnostic.cxx index 464013afe566a..27ca65d124dd7 100644 --- a/DataFormats/Detectors/TOF/src/Diagnostic.cxx +++ b/DataFormats/Detectors/TOF/src/Diagnostic.cxx @@ -104,7 +104,7 @@ void Diagnostic::merge(const Diagnostic* prev) { LOG(debug) << "Merging diagnostic words"; for (auto const& el : prev->mVector) { - fill(el.first, el.second + getFrequency(el.first)); + fill(el.first, el.second); } } diff --git a/DataFormats/Detectors/TPC/CMakeLists.txt b/DataFormats/Detectors/TPC/CMakeLists.txt index 28c0bc4672561..2cc69e16001a6 100644 --- a/DataFormats/Detectors/TPC/CMakeLists.txt +++ b/DataFormats/Detectors/TPC/CMakeLists.txt @@ -34,7 +34,9 @@ o2_add_library( O2::ReconstructionDataFormats O2::CommonDataFormat O2::Headers - O2::Algorithm) + O2::DataSampling + O2::Algorithm + ROOT::Minuit) o2_target_root_dictionary( DataFormatsTPC @@ -62,7 +64,9 @@ o2_target_root_dictionary( include/DataFormatsTPC/LtrCalibData.h include/DataFormatsTPC/VDriftCorrFact.h include/DataFormatsTPC/CalibdEdxCorrection.h - include/DataFormatsTPC/BetheBlochAleph.h) + include/DataFormatsTPC/BetheBlochAleph.h + include/DataFormatsTPC/PIDResponse.h + include/DataFormatsTPC/AltroSyncSignal.h) o2_add_test( ClusterNative diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h new file mode 100644 index 0000000000000..6dee49e4ed6c6 --- /dev/null +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h @@ -0,0 +1,30 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AltroSyncSignal.h +/// \brief Definition of the timebin from which syncronization starts + +#include "GPUCommonRtypes.h" + +namespace o2::tpc +{ +struct AltroSyncSignal { + int periodTF = 10; // signal repeats every period-th TF + int timebin = 14192; // every 10 TF, orbit 31, Time bin 384, BC 4 -> 14195, but clusters can be affected before that + + int getTB2Cut(uint32_t tfCounter) const + { + return periodTF > 0 && (tfCounter % periodTF) == 1 && tfCounter > periodTF ? timebin : -1; + } + + ClassDefNV(AltroSyncSignal, 1); +}; +} // namespace o2::tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/BetheBlochAleph.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/BetheBlochAleph.h index 3fff89b44ba0f..28b224298f36f 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/BetheBlochAleph.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/BetheBlochAleph.h @@ -12,26 +12,17 @@ #ifndef AliceO2_TPC_BETHEBLOCH_H_ #define AliceO2_TPC_BETHEBLOCH_H_ -#include +#include "MathUtils/BetheBlochAleph.h" -namespace o2 -{ -namespace tpc +namespace o2::tpc { template -inline T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5) +GPUdi() T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5) { - T beta = bg / std::sqrt(static_cast(1.) + bg * bg); - - T aa = std::pow(beta, kp4); - T bb = std::pow(static_cast(1.) / bg, kp5); - bb = std::log(kp3 + bb); - - return (kp2 - aa - bb) * kp1 / aa; + return o2::common::BetheBlochAleph(bg, kp1, kp2, kp3, kp4, kp5); } -} // namespace tpc -} // namespace o2 +} // namespace o2::tpc #endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h new file mode 100644 index 0000000000000..8195b3e39c689 --- /dev/null +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h @@ -0,0 +1,124 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file CMV.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Common mode values data format definition + +/// The data is sent by the CRU as 256+16 bit words. The CMV data layout is as follows: +/// - 256-bit Header: [version:8][packetID:8][errorCode:8][magicWord:8][heartbeatOrbit:32][heartbeatBC:16][padding:176] +/// - 16-bit CMV value: [sign:1][I8F7:15] where bit 15 is the sign (1=positive, 0=negative) and the lower 15 bits are a fixed point I8F7 value (8 integer bits, 7 fractional bits) +/// Float conversion: sign ? (value & 0x7FFF) / 128.0 : -(value & 0x7FFF) / 128.0 + +#ifndef ALICEO2_DATAFORMATSTPC_CMV_H +#define ALICEO2_DATAFORMATSTPC_CMV_H + +#include +#include + +namespace o2::tpc::cmv +{ + +static constexpr uint32_t NTimeBinsPerPacket = 3564; ///< number of time bins (covering 8 heartbeats) +static constexpr uint32_t NPacketsPerTFPerCRU = 4; ///< 4 packets per timeframe +static constexpr uint32_t NTimeBinsPerTF = NTimeBinsPerPacket * NPacketsPerTFPerCRU; ///< maximum number of timebins per timeframe (14256) + +/// Data padding: NTimeBinsPerPacket * sizeof(Data) = 3564 * 2 = 7128 bytes +static constexpr uint32_t DataSizeBytes = NTimeBinsPerPacket * sizeof(uint16_t); ///< 7128 bytes +static constexpr uint32_t DataPaddingBytes = (32 - (DataSizeBytes % 32)) % 32; ///< 8 bytes + +/// Header definition of the CMVs +struct Header { + static constexpr uint8_t MagicWord = 0xDC; + union { + uint64_t word0 = 0; ///< bits 0 - 63 + struct { + uint8_t version : 8; ///< version + uint8_t packetID : 8; ///< packet id + uint8_t errorCode : 8; ///< errors + uint8_t magicWord : 8; ///< magic word + uint32_t heartbeatOrbit : 32; ///< first heart beat timing of the package + }; + }; + union { + uint64_t word1 = 0; ///< bits 64 - 127 + struct { + uint16_t heartbeatBC : 16; ///< first BC id of the package + uint16_t unused1 : 16; ///< reserved + uint32_t unused2 : 32; ///< reserved + }; + }; + union { + uint64_t word3 = 0; ///< bits 128 - 191 + struct { + uint64_t unused3 : 64; ///< reserved + }; + }; + union { + uint64_t word4 = 0; ///< bits 192 - 255 + struct { + uint64_t unused4 : 64; ///< reserved + }; + }; +}; + +/// CMV single data container +struct Data { + uint16_t cmv{0}; ///< 16-bit signed fixed point value: bit 15 = sign (1=positive, 0=negative), bits 14-0 = I8F7 magnitude + + uint16_t getCMV() const { return cmv; } ///< raw 16-bit integer representation + void setCMV(uint16_t value) { cmv = value; } ///< set raw 16-bit integer representation + + // Decode to float: sign-magnitude with 7 fractional bits, range ±255.992 + float getCMVFloat() const + { + const bool positive = (cmv >> 15) & 1; // bit 15: sign (1=positive, 0=negative) + const float magnitude = (cmv & 0x7FFF) / 128.f; // lower 15 bits, shift right by 7 (divide by 2^7) + return positive ? magnitude : -magnitude; + } + + // Encode from float: truncates magnitude to 15 bits, range ±255.992 + void setCMVFloat(float value) + { + const bool positive = (value >= 0.f); + const uint16_t magnitude = static_cast( + std::lround(std::abs(value) * 128.f)) & + 0x7FFF; + cmv = (positive ? 0x8000 : 0x0000) | magnitude; + } +}; + +/// CMV full data container: one packet carries NTimeBinsPerPacket CMV values followed by padding +/// Layout: Header (32 bytes) + Data[NTimeBinsPerPacket] (7128 bytes) + padding (8 bytes) = 7168 bytes total (224 * 32 = 7168) +/// The padding bytes at the end of the data array are rubbish/unused and must not be interpreted as CMV values +struct Container { + Header header; ///< CMV data header + Data data[NTimeBinsPerPacket]; ///< data values + uint8_t padding[DataPaddingBytes]{}; ///< trailing padding to align data to 32-byte boundary + + // Header and data accessors + const Header& getHeader() const { return header; } + Header& getHeader() { return header; } + + const Data* getData() const { return data; } + Data* getData() { return data; } + + // Per timebin CMV accessors + uint16_t getCMV(uint32_t timeBin) const { return data[timeBin].getCMV(); } + void setCMV(uint32_t timeBin, uint16_t value) { data[timeBin].setCMV(value); } + + float getCMVFloat(uint32_t timeBin) const { return data[timeBin].getCMVFloat(); } + void setCMVFloat(uint32_t timeBin, float value) { data[timeBin].setCMVFloat(value); } +}; + +} // namespace o2::tpc::cmv + +#endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h index 1e57c9654932c..1f4f08db9d1b9 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h @@ -27,14 +27,15 @@ namespace tpc struct CTFHeader : public ctf::CTFDictHeader, public CompressedClustersCounters { enum : uint32_t { CombinedColumns = 0x1 }; uint32_t flags = 0; - - ClassDefNV(CTFHeader, 2); + uint32_t firstOrbitTrig = 0; /// orbit of 1st trigger + uint16_t nTriggers = 0; /// number of triggers + ClassDefNV(CTFHeader, 3); }; /// wrapper for the Entropy-encoded clusters of the TF -struct CTF : public o2::ctf::EncodedBlocks { +struct CTF : public o2::ctf::EncodedBlocks { - using container_t = o2::ctf::EncodedBlocks; + using container_t = o2::ctf::EncodedBlocks; static constexpr size_t N = getNBlocks(); static constexpr int NBitsQTot = 16; @@ -66,9 +67,14 @@ struct CTF : public o2::ctf::EncodedBlocks { BLCsigmaPadU, BLCsigmaTimeU, // can be combined with BLCsigmaPadU BLCnTrackClusters, - BLCnSliceRowClusters }; + BLCnSliceRowClusters, + // trigger info + BLCTrigOrbitInc, + BLCTrigBCInc, + BLCTrigType + }; - ClassDefNV(CTF, 2); + ClassDefNV(CTF, 4); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h index eb9fd33c27476..f5088959edcf8 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h @@ -22,6 +22,7 @@ #ifndef GPUCA_GPUCODE_DEVICE #include #include +#include #endif // o2 includes @@ -30,22 +31,25 @@ namespace o2::tpc { +namespace conf_dedx_corr +{ +GPUconstexpr() float TglScale[4] = {1.9, 1.5, 1.22, 1.02}; ///< Max Tgl values for each ROC type +} + class CalibdEdxCorrection { public: static constexpr int FitSize = 288; ///< Number of fitted corrections static constexpr int ParamSize = 8; ///< Number of params per fit -#if !defined(GPUCA_ALIGPUCODE) +#if !defined(GPUCA_GPUCODE) CalibdEdxCorrection() { clear(); } CalibdEdxCorrection(std::string_view fileName) { loadFromFile(fileName); } -#else - CalibdEdxCorrection() CON_DEFAULT; #endif - ~CalibdEdxCorrection() CON_DEFAULT; + ~CalibdEdxCorrection() = default; GPUd() float getCorrection(const StackID& stack, ChargeType charge, float tgl = 0, float snp = 0) const { @@ -54,7 +58,8 @@ class CalibdEdxCorrection return 1; } - tgl = o2::gpu::CAMath::Abs(tgl); + // limit to the fit range in the respective region + tgl = o2::gpu::CAMath::Min(conf_dedx_corr::TglScale[stack.type], o2::gpu::CAMath::Abs(tgl)); auto p = mParams[stackIndex(stack, charge)]; float result = p[0]; // Tgl part @@ -84,12 +89,33 @@ class CalibdEdxCorrection void clear(); - void writeToFile(std::string_view fileName) const; - void loadFromFile(std::string_view fileName); + void writeToFile(std::string_view fileName, std::string_view objName = "ccdb_object") const; + void loadFromFile(std::string_view fileName, std::string_view objName = "ccdb_object"); /// \param outFileName name of the output file void dumpToTree(const char* outFileName = "calib_dedx.root") const; + /// Parameters averaged over all stacks + const std::array getMeanParams(ChargeType charge) const; + + /// Parameters averaged over all sectors for a stack type + const std::array getMeanParams(const GEMstack stack, ChargeType charge) const; + + /// Single fit parameters averaged over all sectors for a stack type + float getMeanParam(ChargeType charge, uint32_t param) const; + + /// Single fit parameters averaged over all sectors for a stack type + float getMeanParam(const GEMstack stack, ChargeType charge, uint32_t param) const; + + /// Single fit parameters averaged over all sectors for a stack type + float getMeanEntries(ChargeType charge) const; + + /// Single fit parameters averaged over all sectors for a stack type + float getMeanEntries(const GEMstack stack, ChargeType charge) const; + + /// set all corrections to 1, used for default initialization and to reset corrections + void setUnity(); + #endif private: diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h index 49d61007092a8..fb81afdf67587 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h @@ -15,6 +15,7 @@ #ifndef ALICEO2_DATAFORMATSTPC_CLUSTERNATIVE_H #define ALICEO2_DATAFORMATSTPC_CLUSTERNATIVE_H #ifndef GPUCA_GPUCODE_DEVICE +#include #include #include // for size_t #include @@ -31,7 +32,7 @@ template class ConstMCTruthContainer; template class ConstMCTruthContainerView; -} +} // namespace dataformats } // namespace o2 namespace o2 @@ -62,6 +63,9 @@ struct ClusterNative { static constexpr int scalePadPacked = 64; //< ~60 is needed for 0.1mm precision, but power of two avoids rounding static constexpr int scaleSigmaTimePacked = 32; // 1/32nd of pad/timebin precision for cluster size static constexpr int scaleSigmaPadPacked = 32; + static constexpr int scaleSaturatedQtot = 8; + static constexpr int maxRegularQtot = 25 * 1024; + static constexpr int maxSaturatedQtot = (USHRT_MAX - maxRegularQtot) * scaleSaturatedQtot; uint32_t timeFlagsPacked; //< Contains the time in the lower 24 bits in a packed format, contains the flags in the // upper 8 bits @@ -69,21 +73,21 @@ struct ClusterNative { uint8_t sigmaTimePacked; //< Sigma of the time in packed format uint8_t sigmaPadPacked; //< Sigma of the pad in packed format uint16_t qMax; //< QMax of the cluster - uint16_t qTot; //< Total charge of the cluster + uint16_t qTotPacked; //< Total charge of the cluster GPUd() static uint16_t packPad(float pad) { return (uint16_t)(pad * scalePadPacked + 0.5); } GPUd() static uint32_t packTime(float time) { return (uint32_t)(time * scaleTimePacked + 0.5); } GPUd() static float unpackPad(uint16_t pad) { return float(pad) * (1.f / scalePadPacked); } GPUd() static float unpackTime(uint32_t time) { return float(time) * (1.f / scaleTimePacked); } - GPUdDefault() ClusterNative() CON_DEFAULT; - GPUd() ClusterNative(uint32_t time, uint8_t flags, uint16_t pad, uint8_t sigmaTime, uint8_t sigmaPad, uint16_t qmax, uint16_t qtot) : padPacked(pad), sigmaTimePacked(sigmaTime), sigmaPadPacked(sigmaPad), qMax(qmax), qTot(qtot) + GPUdDefault() ClusterNative() = default; + GPUd() ClusterNative(uint32_t time, uint8_t flags, uint16_t pad, uint8_t sigmaTime, uint8_t sigmaPad, uint16_t qmax, uint16_t qtotPacked) : padPacked(pad), sigmaTimePacked(sigmaTime), sigmaPadPacked(sigmaPad), qMax(qmax), qTotPacked(qtotPacked) { setTimePackedFlags(time, flags); } GPUd() uint16_t getQmax() const { return qMax; } - GPUd() uint16_t getQtot() const { return qTot; } + GPUd() uint32_t getQtot() const { return isSaturated() ? getSaturatedQtot() : (uint32_t)qTotPacked; } GPUd() uint8_t getFlags() const { return timeFlagsPacked >> 24; } GPUd() uint32_t getTimePacked() const { return timeFlagsPacked & 0xFFFFFF; } GPUd() void setTimePackedFlags(uint32_t timePacked, uint8_t flags) @@ -119,7 +123,13 @@ struct ClusterNative { /// Y = (12.4 - 0.5 * (66 - 1)) * 4.16mm = -83.616mm GPUd() float getPad() const { return unpackPad(padPacked); } GPUd() void setPad(float pad) { padPacked = packPad(pad); } - GPUd() float getSigmaTime() const { return float(sigmaTimePacked) * (1.f / scaleSigmaTimePacked); } + GPUd() float getSigmaTime() const + { + if (isSaturated()) [[unlikely]] { + return 0; + } + return float(sigmaTimePacked) * (1.f / scaleSigmaTimePacked); + } GPUd() void setSigmaTime(float sigmaTime) { uint32_t tmp = sigmaTime * scaleSigmaTimePacked + 0.5; @@ -138,6 +148,31 @@ struct ClusterNative { sigmaPadPacked = tmp; } + GPUd() bool isSaturated() const { return qTotPacked > maxRegularQtot; } + + GPUd() void setSaturatedQtot(uint32_t qtot) + { + this->qTotPacked = USHRT_MAX; + if (qtot < maxSaturatedQtot) { + this->qTotPacked = ((qtot + scaleSaturatedQtot / 2) / scaleSaturatedQtot) + maxRegularQtot; + } + } + + GPUd() uint32_t getSaturatedQtot() const + { + return uint32_t(qTotPacked - maxRegularQtot) * scaleSaturatedQtot; + } + + GPUd() void setSaturatedTailLength(uint32_t tail) + { + sigmaTimePacked = encodeTailLength(tail); + } + + GPUd() uint32_t getSaturatedTailLength() const + { + return decodeTailLength(sigmaTimePacked); + } + GPUd() bool operator<(const ClusterNative& rhs) const { if (this->getTimePacked() != rhs.getTimePacked()) { @@ -150,12 +185,110 @@ struct ClusterNative { return (this->sigmaPadPacked < rhs.sigmaPadPacked); } else if (this->qMax != rhs.qMax) { return (this->qMax < rhs.qMax); - } else if (this->qTot != rhs.qTot) { - return (this->qTot < rhs.qTot); + } else if (this->qTotPacked != rhs.qTotPacked) { + return (this->getQtot() < rhs.getQtot()); } else { return (this->getFlags() < rhs.getFlags()); } } + + GPUd() bool operator==(const ClusterNative& rhs) const + { + return this->getTimePacked() == rhs.getTimePacked() && + this->padPacked == rhs.padPacked && + this->sigmaTimePacked == rhs.sigmaTimePacked && + this->sigmaPadPacked == rhs.sigmaPadPacked && + this->qMax == rhs.qMax && + this->qTotPacked == rhs.qTotPacked && + this->getFlags() == rhs.getFlags(); + } + + private: + static constexpr GPUd() uint32_t decodeTailLength(uint8_t code) + { + // Quantize tail length into 8bits. + // Max expected length is 1500 tbs. + // But allow outliers up to 8000 tbs. + // + // Full code layout is: + // + // | Code range | Decoded values | Step | Codes | + // | ---------: | -------------: | ----: | ----: | + // | `0..63` | `0..63` | `1` | `64` | + // | `64..95` | `64..126` | `2` | `32` | + // | `96..127` | `128..252` | `4` | `32` | + // | `128..159` | `256..504` | `8` | `32` | + // | `160..223` | `512..1520` | `16` | `64` | + // | `224..239` | `1552..2032` | `32` | `16` | + // | `240..255` | `2048..8048` | `400` | `16` | + // + + if (code < 64) { + return code; + } + + if (code < 160) { + uint32_t q = (uint32_t)code - 64u; + uint32_t exponent = (q >> 5) + 1u; // 1, 2, 3 + uint32_t mantissa = q & 31u; // 0..31 + + return (32u + mantissa) << exponent; + } + + if (code < 224) { + return 512u + 16u * ((uint32_t)code - 160u); + } + + if (code < 240) { + return 1552u + 32u * ((uint32_t)code - 224u); + } + + return 2048u + 400u * ((uint32_t)code - 240u); + } + + static constexpr GPUd() uint8_t encodeTailLength(uint32_t value) + { + // Saturate above representable range. + if (value >= decodeTailLength(255)) [[unlikely]] { + return 255; + } + + // Binary search for the first code whose decoded value >= value. + uint8_t lo = 0; + uint8_t hi = 255; + + while (lo < hi) { + uint8_t mid = lo + ((hi - lo) >> 1); + uint32_t decoded = decodeTailLength(mid); + + if (decoded < value) { + lo = mid + 1; + } else { + hi = mid; + } + } + + // lo is now the first code with decoded >= value. + if (lo == 0) [[unlikely]] { + return 0; + } + + uint8_t above_code = lo; + uint8_t below_code = lo - 1; + + uint32_t above_value = decodeTailLength(above_code); + uint32_t below_value = decodeTailLength(below_code); + + uint32_t above_error = above_value - value; + uint32_t below_error = value - below_value; + + // Tie-break downward. + if (below_error <= above_error) { + return below_code; + } else { + return above_code; + } + } }; // This is an index struct to access TPC clusters inside sectors and rows. It shall not own the data, but just point to diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNativeHelper.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNativeHelper.h index b8d6a3e7a9428..c8f071c7cd416 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNativeHelper.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNativeHelper.h @@ -312,7 +312,7 @@ class ClusterNativeHelper sigmaTime = rhs.getSigmaTime(); sigmaPad = rhs.getSigmaPad(); qMax = rhs.qMax; - qTot = rhs.qTot; + qTot = rhs.qTotPacked; flags = rhs.getFlags(); return *this; } diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h index 9f49884035b7e..18ad5c6819344 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h @@ -29,7 +29,7 @@ struct CompressedClustersCounters { unsigned int nUnattachedClusters = 0; unsigned int nAttachedClustersReduced = 0; unsigned int nSliceRows = 36 * 152; - unsigned char nComppressionModes = 0; + unsigned char nComppressionModes = 0; // Don't fix this name due to ROOT dictionaries! float solenoidBz = -1e6f; int maxTimeBin = -1e6; @@ -77,8 +77,8 @@ struct CompressedClustersOffsets : public CompressedClustersPtrs_x #include #include +#include #include #include #include @@ -29,9 +30,13 @@ #include "Framework/Logger.h" #include "DataFormatsTPC/Defs.h" +#include "MathUtils/fit.h" using namespace o2::tpc; +class TLinearFitter; +class TTree; + namespace o2::tpc::dcs { @@ -50,6 +55,8 @@ struct DataPoint { bool equalTime(const DataPoint& other) const { return time == other.time; } bool operator<(const DataPoint& other) const { return time < other.time; } bool operator<(const TimeStampType timeStamp) const { return time < timeStamp; } + DataPoint operator+(const DataPoint& other) const { return DataPoint{(time + other.time) / TimeStampType{2}, value + other.value}; } + DataPoint operator/(const DataType denom) const { return DataPoint{time, value / denom}; } ClassDefNV(DataPoint, 1); }; @@ -63,6 +70,19 @@ struct DataPointVector { uint32_t sensorNumber{}; std::vector data; + /// \brief convert data points to a vector of pairs: pair.first -> data and pair.second -> time + auto getPairOfVector() const + { + std::pair, std::vector> pairs; + pairs.first.reserve(data.size()); + pairs.second.reserve(data.size()); + for (const auto& dp : data) { + pairs.first.emplace_back(dp.value); + pairs.second.emplace_back(dp.time); + } + return pairs; + } + void fill(const TimeStampType time, const T& value) { data.emplace_back(DPType{time, value}); } void fill(const DPType& dataPoint) { data.emplace_back(dataPoint); } @@ -83,6 +103,11 @@ struct DataPointVector { void clear() { data.clear(); } + void append(const DataPointVector& other) + { + data.insert(data.end(), other.data.begin(), other.data.end()); + } + /// return value at the last valid time stamp /// /// values are valid unitl the next time stamp @@ -90,7 +115,32 @@ struct DataPointVector { { const auto i = std::upper_bound(data.begin(), data.end(), DPType{timeStamp, {}}); return (i == data.begin()) ? (*i).value : (*(i - 1)).value; - }; + } + + /// calculate average value for `timeStamp`, extending the range by +- range elements + const T getAverageValueForTime(const TimeStampType timeStamp, const long range) const + { + return getAverageValueForTime(timeStamp, timeStamp, range); + } + + /// calculate average value between `from` and `unil`, extending the range by +- `range` elements + const std::pair getSumAndPoints(const TimeStampType from, const TimeStampType until, const long range) const + { + const auto iFrom = std::upper_bound(data.begin(), data.end(), DPType{from, {}}); + const auto iUntil = (from == until) ? iFrom : std::upper_bound(data.begin(), data.end(), DPType{until, {}}); + const auto distFrom = std::distance(data.begin(), iFrom); + const auto distUntil = std::distance(iUntil, data.end()); + const auto nFrom = std::min(distFrom, range + 1); + const auto nUntil = std::min(distUntil, range); + const auto nPoints = std::distance(iFrom - nFrom, iUntil + nUntil); + return {std::accumulate(iFrom - nFrom, iUntil + nUntil, DPType{(*(iFrom - nFrom)).time, T{}}).value, nPoints}; + } + + const T getAverageValueForTime(const TimeStampType from, const TimeStampType until, const long range) const + { + const auto sumAndPoints = getSumAndPoints(from, until, range); + return sumAndPoints.first / static_cast(sumAndPoints.second); + } ClassDefNV(DataPointVector, 1); }; @@ -111,6 +161,70 @@ void doClear(std::vector>& dataVector) } } +template +void doAppend(std::vector>& a, const std::vector>& b) +{ + if (a.size() != b.size()) { + LOGP(warning, "Trying to append std::vector>s of different size: {} != {}", a.size(), b.size()); + } + for (size_t i = 0; i < a.size(); ++i) { + a[i].append(b[i]); + } +} + +template +const T getAverageValueForTime(const std::vector>& dpVec, const TimeStampType from, const TimeStampType until, const long range) +{ + T ret{}; + long nPoints{}; + + for (const auto& dps : dpVec) { + const auto sumAndPoints = dps.getSumAndPoints(from, until, range); + ret += sumAndPoints.first; + nPoints += sumAndPoints.second; + } + return (nPoints > 0) ? ret / static_cast(nPoints) : T{}; +} + +template +dcs::TimeStampType getMinTime(const std::vector>& data, const bool roundToInterval, dcs::TimeStampType fitInterval) +{ + constexpr auto max = std::numeric_limits::max(); + dcs::TimeStampType firstTime = std::numeric_limits::max(); + for (const auto& sensor : data) { + const auto time = sensor.data.size() ? sensor.data.front().time : max; + firstTime = std::min(firstTime, time); + } + + // mFitInterval is is seconds. Round to full amount. + // if e.g. mFitInterval = 5min, then round 10:07:20.510 to 10:05:00.000 + if (roundToInterval) { + firstTime -= (firstTime % fitInterval); + } + + return firstTime; +} + +template +dcs::TimeStampType getMaxTime(const std::vector>& data) +{ + constexpr auto min = 0; + dcs::TimeStampType lastTime = 0; + for (const auto& sensor : data) { + const auto time = sensor.data.size() ? sensor.data.back().time : 0; + lastTime = std::max(lastTime, time); + } + + // mFitInterval is is seconds. Round to full amount. + // if e.g. mFitInterval = 5min, then round 10:07:20.510 to 10:05:00.000 + // TODO: fix this + // if (mRoundToInterval) { + // lastTime -= (lastTime % mFitInterval); + //} + + return lastTime; +} + using RawDPsF = DataPointVector; // using RawDPsI = DataPointVector; @@ -152,11 +266,20 @@ struct Temperature { static constexpr auto& getSensorPosition(const size_t sensor) { return SensorPosition[sensor]; } + /// \brief make fit of the mean temperature and gradients in time intervals + /// \param Side TPC side for which to make the fit + /// \param fitInterval time interval for the fits + /// \param roundToInterval round min time + void fitTemperature(Side side, dcs::TimeStampType fitInterval = 5 * 60 * 1000, const bool roundToInterval = false); + struct Stats { DataType mean{}; ///< average temperature in K DataType gradX{}; ///< horizontal temperature gradient in K/cm DataType gradY{}; ///< vertical temperature gradient in K/cm + Stats operator+(const Stats& other) const { return Stats{mean + other.mean, gradX + other.gradX, gradY + other.gradY}; } + Stats operator/(const DataType val) const { return Stats{mean / val, gradX / val, gradY / val}; } + ClassDefNV(Stats, 1); }; using StatsDPs = DataPointVector; @@ -170,6 +293,11 @@ struct Temperature { return (s == Side::A) ? statsA.getValueForTime(timeStamp) : statsC.getValueForTime(timeStamp); } + DataType getMeanTempRaw() + { + return getAverageValueForTime(raw, 0, 9999999999999, 0); + } + void fill(std::string_view sensor, const TimeStampType time, const DataType temperature) { raw[SensorNameMap.at(sensor.data())].fill(time, temperature); @@ -177,6 +305,8 @@ struct Temperature { void sortAndClean() { + statsA.sortAndClean(); + statsC.sortAndClean(); doSortAndClean(raw); } @@ -187,6 +317,16 @@ struct Temperature { statsC.clear(); } + void append(const Temperature& other) + { + statsA.append(other.statsA); + statsC.append(other.statsC); + doAppend(raw, other.raw); + } + + private: + bool makeFit(TLinearFitter& fitter, const int nDim, std::vector& xVals, std::vector& temperatures); + ClassDefNV(Temperature, 1); }; @@ -195,8 +335,7 @@ struct Temperature { /// struct HV { - HV() - noexcept; + HV() noexcept; // Exmple strings // TPC_HV_A03_I_G1B_I @@ -295,6 +434,13 @@ struct HV { doClear(states); } + void append(const HV& other) + { + doAppend(voltages, other.voltages); + doAppend(currents, other.currents); + doAppend(states, other.states); + } + ClassDefNV(HV, 1); }; @@ -382,6 +528,18 @@ struct Gas { o2Sensor.clear(); } + void append(const Gas& other) + { + neon.append(other.neon); + co2.append(other.co2); + n2.append(other.n2); + argon.append(other.argon); + h2o.append(other.h2o); + o2.append(other.o2); + h2oSensor.append(other.h2oSensor); + o2Sensor.append(other.o2Sensor); + } + TimeStampType getMinTime() const; TimeStampType getMaxTime() const; @@ -389,5 +547,77 @@ struct Gas { ClassDefNV(Gas, 1); }; +struct RobustPressure { + using Stats = o2::math_utils::RollingStats; + Stats surfaceAtmosPressure; ///< rolling statistics of surface sensor + Stats cavernAtmosPressure; ///< rolling statistics of cavern sensor 1 + Stats cavernAtmosPressure2; ///< rolling statistics of cavern sensor 2 + Stats cavernAtmosPressure12; ///< rolling statistics of cavernAtmosPressure/cavernAtmosPressure2 + Stats cavernAtmosPressure1S; ///< rolling statistics of cavernAtmosPressure/surfaceAtmosPressure + Stats cavernAtmosPressure2S; ///< rolling statistics of cavernAtmosPressure2/surfaceAtmosPressure + std::vector isOk; ///< bit mask of valid sensors: cavernBit 0, cavern2Bit = 1, surfaceBit = 2 + std::vector robustPressure; ///< combined robust pressure value that should be used + std::vector time; ///< time stamps of all pressure values + TimeStampType timeInterval; ///< time interval used for rolling statistics + TimeStampType timeIntervalRef; ///< reference time interval used for normalization of pressure sensors + float maxDist{}; ///< maximum allowed time distance between sensors to be accepted for robust pressure calculation + float maxDiff{0.2f}; ///< maximum allowed pressure difference between sensors to be accepted for robust pressure calculation + + ClassDefNV(RobustPressure, 2); +}; + +struct Pressure { + + /// \brief fill pressure data + /// \param sensor name of the sensor from DCS data stream + /// \param time measurement time + /// \param value pressure value + void fill(std::string_view sensor, const TimeStampType time, const DataType value); + + /// sort pressure values and remove obvious outliers + /// \param pMin min accepted pressure + /// \param pMax max accepted pressure + void sortAndClean(float pMin = 800, float pMax = 1100); + + /// \clear all stored data except the buffer + void clear(); + + /// append other pressure values + void append(const Pressure& other); + + /// \return get minimum time of stored data + TimeStampType getMinTime() const; + + /// \return get maximum time of stored data + TimeStampType getMaxTime() const; + + /// \brief average pressure values for given time interval + /// \param timeInterval time interval for which the pressure values are averaged + /// \param timeIntervalRef time interval used to calculate the normalization values for the pressure + /// \param tStart min time of the data + /// \param tEnd max time of the data + /// \param nthreads numbe rof threads used for some calculations + void makeRobustPressure(TimeStampType timeInterval = 100 * 1000, TimeStampType timeIntervalRef = 24 * 60 * 1000, TimeStampType tStart = 1, TimeStampType tEnd = 0, const int nthreads = 1); + + /// set aliases for the cuts used in the calculation of the robust pressure + static void setAliases(TTree* tree); + + RawDPsF cavernAtmosPressure{}; ///< raw pressure in the cavern from sensor 1 + RawDPsF cavernAtmosPressure2{}; ///< raw pressure in the cavern from sensor 2 + RawDPsF surfaceAtmosPressure{}; ///< raw pressure at the surface + RobustPressure robustPressure{}; ///< combined robust pressure estimator from all three sensors + + std::pair, std::vector> mCavernAtmosPressure1Buff{}; ///, std::vector> mCavernAtmosPressure2Buff{}; ///, std::vector> mSurfaceAtmosPressureBuff{}; ///, std::vector> mPressure12Buff{}; ///, std::vector> mPressure1SBuff{}; ///, std::vector> mPressure2SBuff{}; ///, std::vector> mRobPressureBuff{}; /// matchedLtrIDs; ///< list of matched laser track IDs + std::vector matchedLtrIDs; ///< matched laser track IDs + std::vector nTrackTF; ///< number of laser tracks per TF + std::vector dEdx; ///< dE/dx of each track + float tp{0.f}; ///< temperature over pressure ratio - float getDriftVCorrection() const { return 0.5f * (dvCorrectionA + dvCorrectionC); } + bool isValid() const + { + return (std::abs(dvCorrectionA - 1.f) < 0.2) || (std::abs(dvCorrectionC - 1.f) < 0.2); + } + + float getDriftVCorrection() const + { + float correction = 0; + int nCorr = 0; + // only allow +- 20% around reference correction + if (std::abs(dvCorrectionA - 1.f) < 0.2) { + correction += dvCorrectionA; + ++nCorr; + } else { + LOGP(warning, "abs(dvCorrectionA ({}) - 1) >= 0.2, not using for combined estimate", dvCorrectionA); + } + + if (std::abs(dvCorrectionC - 1.f) < 0.2) { + correction += dvCorrectionC; + ++nCorr; + } else { + LOGP(warning, "abs(dvCorrectionC ({}) - 1) >= 0.2, not using for combined estimate", dvCorrectionC); + } + + if (nCorr == 0) { + LOGP(error, "no valid drift velocity correction"); + return 1.f; + } + + return correction / nCorr; + } + + float getVDrift() const { return refVDrift / getDriftVCorrection(); } + + float getTimeOffset() const { return refTimeOffset + timeOffsetCorr; } // renormalize reference and correction either to provided new reference (if >0) or to correction 1 wrt current reference void normalize(float newVRef = 0.f) @@ -61,6 +100,20 @@ struct LtrCalibData { dvCorrectionC *= fact; } + // similarly, the time offset reference is set to provided newRefTimeOffset (if > -998) or modified to have timeOffsetCorr to + // be 0 otherwise + + void normalizeOffset(float newRefTimeOffset = -999.) + { + if (newRefTimeOffset > -999.) { + timeOffsetCorr = getTimeOffset() - newRefTimeOffset; + refTimeOffset = newRefTimeOffset; + } else { + refTimeOffset = getTimeOffset(); + timeOffsetCorr = 0.; + } + } + float getT0A() const { return (250.f * (1.f - dvCorrectionA) - dvOffsetA) / refVDrift; } float getT0C() const { return (250.f * (1.f - dvCorrectionC) + dvOffsetC) / refVDrift; } float getZOffsetA() const { return (250.f * (1.f - dvCorrectionA) - dvOffsetA) / dvCorrectionA; } @@ -79,10 +132,14 @@ struct LtrCalibData { nTracksA = 0; nTracksC = 0; refVDrift = 0; + refTimeOffset = 0; + timeOffsetCorr = 0; matchedLtrIDs.clear(); + nTrackTF.clear(); + dEdx.clear(); } - ClassDefNV(LtrCalibData, 2); + ClassDefNV(LtrCalibData, 5); }; } // namespace o2::tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/PIDResponse.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/PIDResponse.h new file mode 100644 index 0000000000000..277011a260631 --- /dev/null +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/PIDResponse.h @@ -0,0 +1,145 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// @file PIDResponse.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// + +#ifndef AliceO2_TPC_PIDResponse_H +#define AliceO2_TPC_PIDResponse_H + +// o2 includes +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonMath.h" +#include "ReconstructionDataFormats/PID.h" +#include "DataFormatsTPC/PIDResponse.h" +#include "DataFormatsTPC/BetheBlochAleph.h" +#include "DataFormatsTPC/TrackTPC.h" + +namespace o2::tpc +{ +class TrackTPC; + +/// \brief PID response class +/// +/// This class is used to handle the TPC PID response. +/// + +class PIDResponse +{ + public: + /// default constructor + PIDResponse() = default; + + /// default destructor + ~PIDResponse() = default; + + /// setters + GPUd() void setBetheBlochParams(const float betheBlochParams[5]); + GPUd() void setMIP(float mip) { mMIP = mip; } + GPUd() void setChargeFactor(float chargeFactor) { mChargeFactor = chargeFactor; } + + /// getters + GPUd() const float* getBetheBlochParams() const { return mBetheBlochParams; } + GPUd() float getMIP() const { return mMIP; } + GPUd() float getChargeFactor() const { return mChargeFactor; } + + /// get expected signal of the track + GPUd() float getExpectedSignal(const TrackTPC& track, const o2::track::PID::ID id) const; + + /// get most probable PID of the track + GPUd() o2::track::PID::ID getMostProbablePID(const TrackTPC& track, float PID_EKrangeMin, float PID_EKrangeMax, float PID_EPrangeMin, float PID_EPrangeMax, float PID_EDrangeMin, float PID_EDrangeMax, float PID_ETrangeMin, float PID_ETrangeMax, char PID_useNsigma, float PID_sigma) const; + + private: + float mBetheBlochParams[5] = {0.19310481, 4.26696118, 0.00522579, 2.38124907, 0.98055396}; // BBAleph average fit parameters + float mMIP = 50.f; + float mChargeFactor = 2.299999952316284f; + + ClassDefNV(PIDResponse, 1); +}; + +GPUd() void PIDResponse::setBetheBlochParams(const float betheBlochParams[5]) +{ + for (int i = 0; i < 5; i++) { + mBetheBlochParams[i] = betheBlochParams[i]; + } +} + +GPUd() float PIDResponse::getExpectedSignal(const TrackTPC& track, const o2::track::PID::ID id) const +{ + const float bg = static_cast(track.getP() / o2::track::pid_constants::sMasses[id]); + if (bg < 0.05) { + return -999.; + } + const float bethe = mMIP * o2::tpc::BetheBlochAleph(bg, mBetheBlochParams[0], mBetheBlochParams[1], mBetheBlochParams[2], mBetheBlochParams[3], mBetheBlochParams[4]) * o2::gpu::GPUCommonMath::Pow(static_cast(o2::track::pid_constants::sCharges[id]), mChargeFactor); + return bethe >= 0. ? bethe : -999.; +} + +// get most probable PID +GPUd() o2::track::PID::ID PIDResponse::getMostProbablePID(const TrackTPC& track, float PID_EKrangeMin, float PID_EKrangeMax, float PID_EPrangeMin, float PID_EPrangeMax, float PID_EDrangeMin, float PID_EDrangeMax, float PID_ETrangeMin, float PID_ETrangeMax, char PID_useNsigma, float PID_sigma) const +{ + const float dEdx = track.getdEdx().dEdxTotTPC; + + if (dEdx < 10.) { + return o2::track::PID::Pion; + } + + auto id = o2::track::PID::Electron; + float distanceMin = 0.; + float dEdxExpected = getExpectedSignal(track, id); + if (PID_useNsigma) { + // using nSigma + distanceMin = o2::gpu::GPUCommonMath::Abs((dEdx - dEdxExpected) / (PID_sigma * dEdxExpected)); + } else { + // using absolute distance + distanceMin = o2::gpu::GPUCommonMath::Abs(dEdx - dEdxExpected); + } + + // calculate the distance to the expected dEdx signals + // start from Pion to exlude Muons + for (o2::track::PID::ID i = o2::track::PID::Pion; i < o2::track::PID::NIDs; i++) { + float distance = 0.; + dEdxExpected = getExpectedSignal(track, i); + if (PID_useNsigma) { + // using nSigma + distance = o2::gpu::GPUCommonMath::Abs((dEdx - dEdxExpected) / (PID_sigma * dEdxExpected)); + } else { + // using absolute distance + distance = o2::gpu::GPUCommonMath::Abs(dEdx - dEdxExpected); + } + if (distance < distanceMin) { + id = i; + distanceMin = distance; + } + } + + // override the electrons to the closest alternative PID in the bands crossing regions + if (id == o2::track::PID::Electron) { + const float p = track.getP(); + if ((p > PID_EKrangeMin) && (p < PID_EKrangeMax)) { + id = o2::track::PID::Kaon; + } else if ((p > PID_EPrangeMin) && (p < PID_EPrangeMax)) { + id = o2::track::PID::Proton; + } else if ((p > PID_EDrangeMin) && (p < PID_EDrangeMax)) { + id = o2::track::PID::Deuteron; + } else if ((p > PID_ETrangeMin) && (p < PID_ETrangeMax)) { + id = o2::track::PID::Triton; + } + } + + return id; +} + +} // namespace o2::tpc + +#endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/RawDataTypes.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/RawDataTypes.h index 481a7739e54fb..db96280bde534 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/RawDataTypes.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/RawDataTypes.h @@ -25,9 +25,10 @@ namespace o2::tpc::raw_data_types enum Type : char { RAWDATA = 0, ///< GBT raw data LinkZS = 1, ///< Link-based Zero Suppression - ZS = 2, ///< final Zero Suppression + ZS = 2, ///< final Zero Suppression (can be ILBZS, DLBZS) IDC = 3, ///< Integrated Digitial Currents, with priority bit to end up in separate buffer SAC = 4, ///< Sampled Analogue Currents from the current monitor + CMV = 5, ///< Common mode values }; const std::unordered_map TypeNameMap{ @@ -36,6 +37,7 @@ const std::unordered_map TypeNameMap{ {Type::ZS, "ZS"}, {Type::IDC, "IDC"}, {Type::SAC, "SAC"}, + {Type::CMV, "CMV"}, }; } // namespace o2::tpc::raw_data_types diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/SAC.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/SAC.h index 53cbb66435ee6..416522185dd7d 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/SAC.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/SAC.h @@ -81,7 +81,7 @@ struct dataDef { }; /// /// uint32_t pktNumber = 0; ///< packet number of this front end card. Should always increase by 1 - uint32_t timeStamp = 0; ///< tims stamp + uint32_t timeStamp = 0; ///< time stamp char dataWords[DataSize]; ///< ASCI encoded SAC data uint32_t crc32 = 0; ///< CRC32 checksum uint32_t trailer = 0; ///< trailer magic word, should always be TrailerWord diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackCuts.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackCuts.h index f8bcc601cf659..451acfe9d1fa0 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackCuts.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackCuts.h @@ -46,13 +46,16 @@ class TrackCuts void setNClusMin(float NClusMin) { mNClusMin = NClusMin; } void setdEdxMin(float dEdxMin) { mdEdxMin = dEdxMin; } void setdEdxMax(float dEdxMax) { mdEdxMax = dEdxMax; } + /// try to remove looper cutting on (abs(z_out) - abs(z_in)) < -10), not very precise + void setCutLooper(bool cut) { mCutLooper = cut; } private: - float mPMin{0}; ///< min momentum allowed - float mPMax{1e10}; ///< max momentum allowed - float mNClusMin{0}; ///< min number of clusters in track allowed - float mdEdxMin{0}; ///< min dEdx - float mdEdxMax{1e10}; ///< max dEdx + float mPMin{0}; ///< min momentum allowed + float mPMax{1e10}; ///< max momentum allowed + float mNClusMin{0}; ///< min number of clusters in track allowed + float mdEdxMin{0}; ///< min dEdx + float mdEdxMax{1e10}; ///< max dEdx + bool mCutLooper{false}; ///< cut looper comparing zout-zin ClassDefNV(TrackCuts, 1) }; diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h index 42c098b7bdd54..e7e23dea91e88 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h @@ -79,8 +79,8 @@ class TrackTPC : public o2::track::TrackParCov GPUd() void setClusterRef(uint32_t entry, uint16_t ncl) { mClustersReference.set(entry, ncl); } template - GPUd() static inline void getClusterReference(T& clinfo, int nCluster, - uint8_t& sectorIndex, uint8_t& rowIndex, uint32_t& clusterIndex, const ClusRef& ref) + GPUdi() static void getClusterReference(T& clinfo, int nCluster, + uint8_t& sectorIndex, uint8_t& rowIndex, uint32_t& clusterIndex, const ClusRef& ref) { // data for given tracks starts at clinfo[ ref.getFirstEntry() ], // 1st ref.getEntries() cluster indices are stored as uint32_t @@ -95,15 +95,15 @@ class TrackTPC : public o2::track::TrackParCov } template - GPUd() inline void getClusterReference(T& clinfo, int nCluster, - uint8_t& sectorIndex, uint8_t& rowIndex, uint32_t& clusterIndex) const + GPUdi() void getClusterReference(T& clinfo, int nCluster, + uint8_t& sectorIndex, uint8_t& rowIndex, uint32_t& clusterIndex) const { getClusterReference(clinfo, nCluster, sectorIndex, rowIndex, clusterIndex, mClustersReference); } template - GPUd() static inline const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, - const o2::tpc::ClusterNativeAccess& clusters, uint8_t& sectorIndex, uint8_t& rowIndex, const ClusRef& ref) + GPUdi() static const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, + const o2::tpc::ClusterNativeAccess& clusters, uint8_t& sectorIndex, uint8_t& rowIndex, const ClusRef& ref) { uint32_t clusterIndex; getClusterReference(clinfo, nCluster, sectorIndex, rowIndex, clusterIndex, ref); @@ -111,15 +111,15 @@ class TrackTPC : public o2::track::TrackParCov } template - GPUd() inline const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, - const o2::tpc::ClusterNativeAccess& clusters, uint8_t& sectorIndex, uint8_t& rowIndex) const + GPUdi() const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, + const o2::tpc::ClusterNativeAccess& clusters, uint8_t& sectorIndex, uint8_t& rowIndex) const { return getCluster(clinfo, nCluster, clusters, sectorIndex, rowIndex, mClustersReference); } template - GPUd() inline const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, - const o2::tpc::ClusterNativeAccess& clusters) const + GPUdi() const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, + const o2::tpc::ClusterNativeAccess& clusters) const { uint8_t sectorIndex, rowIndex; return (getCluster(clinfo, nCluster, clusters, sectorIndex, rowIndex)); @@ -128,6 +128,9 @@ class TrackTPC : public o2::track::TrackParCov GPUd() const dEdxInfo& getdEdx() const { return mdEdx; } GPUd() void setdEdx(const dEdxInfo& v) { mdEdx = v; } + GPUd() const dEdxInfo& getdEdxAlt() const { return mdEdxAlt; } + GPUd() void setdEdxAlt(const dEdxInfo& v) { mdEdxAlt = v; } + private: float mTime0 = 0.f; ///< Assumed time of the vertex that created the track in TPC time bins, 0 for triggered data float mDeltaTFwd = 0; ///< max possible increment to mTime0 @@ -136,9 +139,10 @@ class TrackTPC : public o2::track::TrackParCov float mChi2 = 0.f; // Chi2 of the track o2::track::TrackParCov mOuterParam; // Track parameters at outer end of TPC. dEdxInfo mdEdx; // dEdx Information + dEdxInfo mdEdxAlt; // dEdx alternative Information ClusRef mClustersReference; // reference to externale cluster indices - ClassDefNV(TrackTPC, 4); + ClassDefNV(TrackTPC, 5); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h index 03ad9755fedae..c3feccecc28b8 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h @@ -26,14 +26,15 @@ namespace o2::tpc { struct VDriftCorrFact { - long firstTime{}; ///< first time stamp of processed TFs - long lastTime{}; ///< last time stamp of processed TFs - long creationTime{}; ///< time of creation - float corrFact{1.0}; ///< drift velocity correction factor (multiplicative) - float corrFactErr{0.0}; ///< stat error of correction factor - float refVDrift{0.}; ///< reference vdrift for which factor was extracted + long firstTime{}; ///< first time stamp of processed TFs + long lastTime{}; ///< last time stamp of processed TFs + long creationTime{}; ///< time of creation + float corrFact{1.0}; ///< drift velocity correction factor (multiplicative) + float corrFactErr{0.0}; ///< stat error of correction factor + float refVDrift{0.}; ///< reference vdrift for which factor was extracted float refTimeOffset{0.}; ///< additive time offset reference (\mus) float timeOffsetCorr{0.}; ///< additive time offset correction (\mus) + float refTP{0.}; ///< reference temperature / pressure for which refVDrift was extracted float getVDrift() const { return refVDrift * corrFact; } float getVDriftError() const { return refVDrift * corrFactErr; } @@ -43,10 +44,12 @@ struct VDriftCorrFact { // renormalize VDrift reference and correction either to provided new reference (if >0) or to correction 1 wrt current reference void normalize(float newVRef = 0.f) { + float normVDrift = newVRef; if (newVRef == 0.f) { - newVRef = refVDrift * corrFact; + normVDrift = refVDrift * corrFact; + newVRef = normVDrift; } - float fact = refVDrift / newVRef; + float fact = refVDrift / normVDrift; refVDrift = newVRef; corrFactErr *= fact; corrFact *= fact; @@ -66,7 +69,19 @@ struct VDriftCorrFact { } } - ClassDefNV(VDriftCorrFact, 2); + // scale the drift velocity with the relative change of T/P wrt the reference T/P + // The scaling is folded into the correction factor, keeping refVDrift constant + void normalizeTP(float tp) + { + if ((tp > 0) && (refTP > 0)) { + const float scale = tp / refTP; + corrFact *= scale; + corrFactErr *= scale; + refTP = tp; + } + } + + ClassDefNV(VDriftCorrFact, 3); }; } // namespace o2::tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/WorkflowHelper.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/WorkflowHelper.h index 30b40ed70b9c7..f4a318bc30101 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/WorkflowHelper.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/WorkflowHelper.h @@ -21,6 +21,7 @@ #include "Framework/DataRefUtils.h" #include #include "Framework/InputRecordWalker.h" +#include "DataSampling/DataSamplingHeader.h" #include "DataFormatsTPC/TrackTPC.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -80,7 +81,7 @@ static auto getWorkflowTPCInput(o2::framework::ProcessingContext& pc, int verbos if (do_digits) { std::fill(inputDigitsMCIndex.begin(), inputDigitsMCIndex.end(), -1); } - for (auto const& ref : o2::framework::InputRecordWalker(pc.inputs(), filter)) { + for (auto const& ref : o2::framework::InputRecordWalker(pc.inputs(), filter)) { auto const* sectorHeader = o2::framework::DataRefUtils::getHeader(ref); if (sectorHeader == nullptr) { // FIXME: think about error policy @@ -127,7 +128,7 @@ static auto getWorkflowTPCInput(o2::framework::ProcessingContext& pc, int verbos {"check", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "CLUSTERNATIVE"}, o2::framework::Lifetime::Timeframe}, }; unsigned long recvMask = 0; - for (auto const& ref : o2::framework::InputRecordWalker(pc.inputs(), filter)) { + for (auto const& ref : o2::framework::InputRecordWalker(pc.inputs(), filter)) { auto const* sectorHeader = o2::framework::DataRefUtils::getHeader(ref); if (sectorHeader == nullptr) { throw std::runtime_error("sector header missing on header stack"); diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h index b83a894c1aa6f..b1df9445bcf42 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h @@ -19,6 +19,7 @@ #include // for size_t #endif #include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" namespace o2 { @@ -84,6 +85,35 @@ struct ZeroSuppressedContainer { // Struct for the TPC zero suppressed data form TPCZSHDR hdr; // ZS header }; +/// Trigger word for dense link-base ZS +/// +/// Trigger word is always 128bit and occurs always in the last page of a HBF before the meta header +struct TriggerWordDLBZS { + static constexpr uint16_t MaxTriggerEntries = 8; ///< Maximum number of trigger information + + /// trigger types as in the ttype bits + enum TriggerType : uint8_t { + PhT = 1, ///< Physics Trigger + PP = 2, ///< Pre Pulse for calibration + Cal = 4, ///< Laser (Calibration trigger) + }; + uint16_t triggerEntries[MaxTriggerEntries] = {}; + + uint16_t getTriggerBC(int entry = 0) const { return triggerEntries[entry] & 0xFFF; } + uint16_t getTriggerType(int entry = 0) const { return (triggerEntries[entry] >> 12) & 0x7; } + bool isValid(int entry = 0) const { return triggerEntries[entry] & 0x8000; } + + ClassDefNV(TriggerWordDLBZS, 1); +}; + +/// Trigger info including the orbit +struct TriggerInfoDLBZS { + TriggerWordDLBZS triggerWord{}; ///< trigger Word information + uint32_t orbit{}; ///< orbit of the trigger word + + ClassDefNV(TriggerInfoDLBZS, 1); +}; + } // namespace tpc } // namespace o2 #endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h index e8c9dd2317d51..a753f24aec11f 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h @@ -19,6 +19,7 @@ #include "GPUCommonDef.h" #ifndef GPUCA_GPUCODE +#include #include #endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h index 63b64a50b63dc..bdf9c41aae421 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h @@ -22,24 +22,24 @@ namespace o2 namespace tpc { struct dEdxInfo { - float dEdxTotIROC; - float dEdxTotOROC1; - float dEdxTotOROC2; - float dEdxTotOROC3; - float dEdxTotTPC; - float dEdxMaxIROC; - float dEdxMaxOROC1; - float dEdxMaxOROC2; - float dEdxMaxOROC3; - float dEdxMaxTPC; - unsigned char NHitsIROC; - unsigned char NHitsSubThresholdIROC; - unsigned char NHitsOROC1; - unsigned char NHitsSubThresholdOROC1; - unsigned char NHitsOROC2; - unsigned char NHitsSubThresholdOROC2; - unsigned char NHitsOROC3; - unsigned char NHitsSubThresholdOROC3; + float dEdxTotIROC = 0.f; + float dEdxTotOROC1 = 0.f; + float dEdxTotOROC2 = 0.f; + float dEdxTotOROC3 = 0.f; + float dEdxTotTPC = 0.f; + float dEdxMaxIROC = 0.f; + float dEdxMaxOROC1 = 0.f; + float dEdxMaxOROC2 = 0.f; + float dEdxMaxOROC3 = 0.f; + float dEdxMaxTPC = 0.f; + unsigned char NHitsIROC = 0; + unsigned char NHitsSubThresholdIROC = 0; + unsigned char NHitsOROC1 = 0; + unsigned char NHitsSubThresholdOROC1 = 0; + unsigned char NHitsOROC2 = 0; + unsigned char NHitsSubThresholdOROC2 = 0; + unsigned char NHitsOROC3 = 0; + unsigned char NHitsSubThresholdOROC3 = 0; ClassDefNV(dEdxInfo, 1); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx index b843577f1976a..635b0e0817d6e 100644 --- a/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx +++ b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx @@ -11,14 +11,18 @@ #include "DataFormatsTPC/CalibdEdxCorrection.h" +#include #include +#ifndef GPUCA_STANDALONE // o2 includes +#include "Framework/Logger.h" #include "DataFormatsTPC/Defs.h" #include "CommonUtils/TreeStreamRedirector.h" // root includes #include "TFile.h" +#endif using namespace o2::tpc; @@ -35,18 +39,32 @@ void CalibdEdxCorrection::clear() mDims = -1; } -void CalibdEdxCorrection::writeToFile(std::string_view fileName) const +#ifndef GPUCA_STANDALONE + +void CalibdEdxCorrection::writeToFile(std::string_view fileName, std::string_view objName) const { std::unique_ptr file(TFile::Open(fileName.data(), "recreate")); - file->WriteObject(this, "CalibdEdxCorrection"); + if (!file) { + LOGP(error, "Failed to open file {} for writing", fileName.data()); + return; + } + + file->WriteObject(this, objName.data()); } -void CalibdEdxCorrection::loadFromFile(std::string_view fileName) +void CalibdEdxCorrection::loadFromFile(std::string_view fileName, std::string_view objName) { std::unique_ptr file(TFile::Open(fileName.data())); - auto tmp = file->Get("CalibdEdxCorrection"); + if (!file || file->IsZombie()) { + LOGP(error, "Failed to open file {}", fileName.data()); + return; + } + + auto tmp = file->Get(objName.data()); if (tmp != nullptr) { *this = *tmp; + } else { + LOGP(error, "Failed to load object with name {} from file {}", objName.data(), fileName.data()); } } @@ -86,3 +104,86 @@ void CalibdEdxCorrection::dumpToTree(const char* outFileName) const } } } + +const std::array CalibdEdxCorrection::getMeanParams(ChargeType charge) const +{ + std::array params{}; + + for (int index = 0; index < FitSize / 2; ++index) { + std::transform(params.begin(), params.end(), mParams[index + charge * FitSize / 2], params.begin(), std::plus<>()); + } + std::for_each(params.begin(), params.end(), [](auto& val) { val /= (0.5f * FitSize); }); + return params; +} + +const std::array CalibdEdxCorrection::getMeanParams(const GEMstack stack, ChargeType charge) const +{ + std::array params{}; + + for (int index = 0; index < SECTORSPERSIDE * SIDES; ++index) { + std::transform(params.begin(), params.end(), getParams(StackID{index, stack}, charge), params.begin(), std::plus<>()); + } + std::for_each(params.begin(), params.end(), [](auto& val) { val /= (SECTORSPERSIDE * SIDES); }); + return params; +} + +float CalibdEdxCorrection::getMeanParam(ChargeType charge, uint32_t param) const +{ + if (param >= ParamSize) { + return 0.f; + } + float mean{}; + for (int index = 0; index < FitSize / 2; ++index) { + mean += mParams[index + charge * FitSize / 2][param]; + } + + return mean / (0.5f * FitSize); +} + +float CalibdEdxCorrection::getMeanParam(const GEMstack stack, ChargeType charge, uint32_t param) const +{ + if (param >= ParamSize) { + return 0.f; + } + float mean{}; + for (int index = 0; index < SECTORSPERSIDE * SIDES; ++index) { + mean += getParams(StackID{index, stack}, charge)[param]; + } + + return mean / (SECTORSPERSIDE * SIDES); +} + +float CalibdEdxCorrection::getMeanEntries(ChargeType charge) const +{ + float mean{}; + for (int index = 0; index < FitSize / 2; ++index) { + mean += mEntries[index + charge * FitSize / 2]; + } + + return mean / (0.5f * FitSize); +} + +float CalibdEdxCorrection::getMeanEntries(const GEMstack stack, ChargeType charge) const +{ + float mean{}; + for (int index = 0; index < SECTORSPERSIDE * SIDES; ++index) { + mean += getEntries(StackID{index, stack}, charge); + } + + return mean / (SECTORSPERSIDE * SIDES); +} + +void CalibdEdxCorrection::setUnity() +{ + for (int i = 0; i < FitSize; ++i) { + for (int j = 0; j < ParamSize; ++j) { + mParams[i][j] = 0.f; + } + mParams[i][0] = 1.f; // constant term = 1 + mChi2[i] = 0.f; + mEntries[i] = 0; + } + mDims = 0; +} + +#endif // GPUCA_STANDALONE diff --git a/DataFormats/Detectors/TPC/src/ClusterNativeHelper.cxx b/DataFormats/Detectors/TPC/src/ClusterNativeHelper.cxx index a1268c02a2740..0e4432f970230 100644 --- a/DataFormats/Detectors/TPC/src/ClusterNativeHelper.cxx +++ b/DataFormats/Detectors/TPC/src/ClusterNativeHelper.cxx @@ -15,7 +15,6 @@ /// @author Matthias Richter #include "DataFormatsTPC/ClusterNativeHelper.h" -#include "Algorithm/Parser.h" #include #include #include diff --git a/DataFormats/Detectors/TPC/src/DCS.cxx b/DataFormats/Detectors/TPC/src/DCS.cxx index 131f6c9526647..8a082a8d21901 100644 --- a/DataFormats/Detectors/TPC/src/DCS.cxx +++ b/DataFormats/Detectors/TPC/src/DCS.cxx @@ -16,6 +16,8 @@ #include #include "DataFormatsTPC/DCS.h" +#include "TLinearFitter.h" +#include "TTree.h" using namespace o2::tpc::dcs; @@ -117,3 +119,463 @@ TimeStampType Gas::getMaxTime() const return *std::max_element(times.begin(), times.end()); } + +TimeStampType Pressure::getMinTime() const +{ + constexpr auto max = std::numeric_limits::max(); + const std::vector times{ + cavernAtmosPressure.data.size() ? cavernAtmosPressure.data.front().time : max, + cavernAtmosPressure2.data.size() ? cavernAtmosPressure2.data.front().time : max, + surfaceAtmosPressure.data.size() ? surfaceAtmosPressure.data.front().time : max, + }; + + return *std::min_element(times.begin(), times.end()); +} + +TimeStampType Pressure::getMaxTime() const +{ + constexpr auto min = 0; + const std::vector times{ + cavernAtmosPressure.data.size() ? cavernAtmosPressure.data.back().time : min, + cavernAtmosPressure2.data.size() ? cavernAtmosPressure2.data.back().time : min, + surfaceAtmosPressure.data.size() ? surfaceAtmosPressure.data.back().time : min, + }; + + return *std::max_element(times.begin(), times.end()); +} + +bool Temperature::makeFit(TLinearFitter& fitter, const int nDim, std::vector& xVals, std::vector& temperatures) +{ + const int minPointsForFit = 5; + if (temperatures.empty() || (temperatures.size() < minPointsForFit)) { + LOGP(warning, "Number of points {} for fit smaller than minimum of {}!", temperatures.size(), minPointsForFit); + return false; + } + + fitter.ClearPoints(); + fitter.AssignData(temperatures.size(), nDim, xVals.data(), temperatures.data()); + int status = fitter.Eval(); + if (status == 1) { + LOGP(warning, "Fit failed!"); + return false; + } + return true; +} + +void Temperature::fitTemperature(Side side, dcs::TimeStampType fitInterval, const bool roundToInterval) +{ + // clear old data + auto& stats = (side == Side::A) ? statsA : statsC; + stats.clear(); + + // temperature fits in x-y + const int nDim = 2; + TLinearFitter fitter(nDim, "1 ++ x0 ++ x1", ""); + std::array startPos{}; + const size_t sensorOffset = (side == Side::C) ? dcs::Temperature::SensorsPerSide : 0; + + const dcs::TimeStampType refTime = getMinTime(raw, fitInterval, roundToInterval); + const dcs::TimeStampType refTimeMax = getMaxTime(raw); + + // calculate number of intervals and see if the last interval should be merged into the previous one + const int lastIntervalDuration = (refTimeMax - refTime) % fitInterval; + + // process the last interval only if it contains more than 50% of the interval duration + const bool procLastInt = (lastIntervalDuration / fitInterval > 0.5); + int numIntervals = (refTimeMax - refTime) / fitInterval + procLastInt; + if (numIntervals == 0) { + numIntervals = 1; + } + + // buffer for fit values + std::vector xVals; + std::vector temperatures; + xVals.reserve(2 * 1000); + temperatures.reserve(1000); + + for (int interval = 0; interval < numIntervals; ++interval) { + const dcs::TimeStampType timeStart = refTime + interval * fitInterval; + + // clear buffer + xVals.clear(); + temperatures.clear(); + + // TODO: check if we should use refTime + dcs::TimeStampType firstTime = std::numeric_limits::max(); + dcs::TimeStampType LastTime = 0; + + for (size_t iSensor = 0; iSensor < dcs::Temperature::SensorsPerSide; ++iSensor) { + const auto& sensor = raw[iSensor + sensorOffset]; + + LOGP(debug, "sensor {}, start {}, size {}", sensor.sensorNumber, startPos[iSensor], sensor.data.size()); + while (startPos[iSensor] < sensor.data.size()) { + const auto& dataPoint = sensor.data[startPos[iSensor]]; + if (((dataPoint.time - timeStart) >= fitInterval) && (interval != numIntervals - 1)) { + LOGP(debug, "sensor {}, {} - {} >= {}", sensor.sensorNumber, dataPoint.time, timeStart, fitInterval); + break; + } + firstTime = std::min(firstTime, dataPoint.time); + LastTime = std::max(LastTime, dataPoint.time); + const auto temperature = dataPoint.value; + // sanity check + ++startPos[iSensor]; + if (temperature < 15 || temperature > 25) { + continue; + } + const auto& pos = dcs::Temperature::SensorPosition[iSensor + sensorOffset]; + xVals.emplace_back(pos.x); + xVals.emplace_back(pos.y); + temperatures.emplace_back(temperature); + } + } + if (firstTime < std::numeric_limits::max() && !temperatures.empty()) { + const bool fitOk = makeFit(fitter, nDim, xVals, temperatures); + if (!fitOk) { + continue; + } + auto& stat = stats.data.emplace_back(); + stat.time = (firstTime + LastTime) / 2; + stat.value.mean = fitter.GetParameter(0); + stat.value.gradX = fitter.GetParameter(1); + stat.value.gradY = fitter.GetParameter(2); + + // check if data contains outliers + const float maxDeltaT = 1; + const float meanTemp = fitter.GetParameter(0); + const bool isDataGood = std::all_of(temperatures.begin(), temperatures.end(), [meanTemp, maxDeltaT](double t) { return std::abs(t - meanTemp) < maxDeltaT; }); + + // do second iteration only in case of outliers + if (!isDataGood) { + std::vector xVals2; + std::vector temperatures2; + xVals2.reserve(xVals.size()); + temperatures2.reserve(temperatures.size()); + for (int i = 0; i < temperatures.size(); ++i) { + if (std::abs(temperatures[i] - meanTemp) < maxDeltaT) { + const int idx = 2 * i; + xVals2.emplace_back(xVals[idx]); + xVals2.emplace_back(xVals[idx + 1]); + temperatures2.emplace_back(temperatures[i]); + } + } + const bool fitOk2 = makeFit(fitter, nDim, xVals2, temperatures2); + if (fitOk2) { + stat.value.mean = fitter.GetParameter(0); + stat.value.gradX = fitter.GetParameter(1); + stat.value.gradY = fitter.GetParameter(2); + } + } + } + } +} + +void Pressure::fill(std::string_view sensor, const TimeStampType time, const DataType value) +{ + if (sensor == "CavernAtmosPressure") { + cavernAtmosPressure.fill(time, value); + } else if (sensor == "CavernAtmosPressure2") { + cavernAtmosPressure2.fill(time, value); + } else if (sensor == "SurfaceAtmosPressure") { + surfaceAtmosPressure.fill(time, value); + } else { + LOGP(warning, "Unknown pressure sensor {}", sensor); + } +} + +void Pressure::sortAndClean(float pMin, float pMax) +{ + cavernAtmosPressure.sortAndClean(); + cavernAtmosPressure2.sortAndClean(); + surfaceAtmosPressure.sortAndClean(); + + auto removeOutliers = [](auto& dataVec, auto minVal, auto maxVal) { + dataVec.erase( + std::remove_if(dataVec.begin(), dataVec.end(), + [minVal, maxVal](const auto& dp) { + return (dp.value < minVal || dp.value > maxVal); + }), + dataVec.end()); + }; + + removeOutliers(cavernAtmosPressure.data, pMin, pMax); + removeOutliers(cavernAtmosPressure2.data, pMin, pMax); + removeOutliers(surfaceAtmosPressure.data, pMin, pMax); +} + +void Pressure::clear() +{ + cavernAtmosPressure.clear(); + cavernAtmosPressure2.clear(); + surfaceAtmosPressure.clear(); + robustPressure = RobustPressure(); +} + +void Pressure::append(const Pressure& other) +{ + cavernAtmosPressure.append(other.cavernAtmosPressure); + cavernAtmosPressure2.append(other.cavernAtmosPressure2); + surfaceAtmosPressure.append(other.surfaceAtmosPressure); +} + +void fillBuffer(std::pair, std::vector>& buffer, const std::pair, std::vector>& values, TimeStampType tStart, const int minPoints) +{ + const auto itStartBuff = std::lower_bound(buffer.second.begin(), buffer.second.end(), tStart); + size_t idxStartBuffer = std::distance(buffer.second.begin(), itStartBuff); + if (buffer.first.size() - idxStartBuffer < minPoints) { + if (buffer.first.size() < minPoints) { + idxStartBuffer = 0; + } else { + idxStartBuffer = buffer.first.size() - minPoints; + } + } + + std::pair, std::vector> buffTmp; + auto& [buffVals, buffTimes] = buffTmp; + + // Preallocate enough capacity to avoid reallocations + buffVals.reserve(buffer.first.size() - idxStartBuffer + values.first.size()); + buffTimes.reserve(buffer.second.size() - idxStartBuffer + values.second.size()); + // Insert the kept part of the old buffer + buffVals.insert(buffVals.end(), buffer.first.begin() + idxStartBuffer, buffer.first.end()); + buffTimes.insert(buffTimes.end(), buffer.second.begin() + idxStartBuffer, buffer.second.end()); + // Insert the new values + buffVals.insert(buffVals.end(), values.first.begin(), values.first.end()); + buffTimes.insert(buffTimes.end(), values.second.begin(), values.second.end()); + + // this should not happen + if (!std::is_sorted(buffTimes.begin(), buffTimes.end())) { + LOGP(info, "Pressure buffer not sorted after filling - sorting it"); + std::vector idx(buffTimes.size()); + o2::math_utils::SortData(buffTimes, idx); + o2::math_utils::Reorder(buffVals, idx); + o2::math_utils::Reorder(buffTimes, idx); + } + + buffer = std::move(buffTmp); +} + +/// truncate all parallel vectors of a RollingStats to size n, keeping the leading n entries. +/// Used to keep RobustPressure's members aligned with a `times` vector that got trimmed. +void trimStats(o2::math_utils::RollingStats& stats, size_t n) +{ + stats.median.resize(n); + stats.std.resize(n); + stats.nPoints.resize(n); + stats.closestDistanceL.resize(n); + stats.closestDistanceR.resize(n); +} + +void Pressure::makeRobustPressure(TimeStampType timeInterval, TimeStampType timeIntervalRef, TimeStampType tStart, TimeStampType tEnd, const int nthreads) +{ + const auto surfaceAtmosPressurePair = surfaceAtmosPressure.getPairOfVector(); + const auto cavernAtmosPressurePair = cavernAtmosPressure.getPairOfVector(); + const auto cavernAtmosPressure2Pair = cavernAtmosPressure2.getPairOfVector(); + + // round to second + tStart = tStart / 1000 * 1000; + const TimeStampType tStartRef = (tStart - timeIntervalRef); + const int minPointsRef = 50; + fillBuffer(mCavernAtmosPressure1Buff, cavernAtmosPressurePair, tStartRef, minPointsRef); + fillBuffer(mCavernAtmosPressure2Buff, cavernAtmosPressure2Pair, tStartRef, minPointsRef); + fillBuffer(mSurfaceAtmosPressureBuff, surfaceAtmosPressurePair, tStartRef, minPointsRef); + + int nIntervals = std::round((tEnd - tStart) / timeInterval); + if (nIntervals == 0) { + nIntervals = 1; // at least one interval + } + std::vector times; + times.reserve(nIntervals); + for (int i = 0; i < nIntervals; ++i) { + times.emplace_back(tStart + (i + 0.5) * timeInterval); + } + + /// minimum number of points in the interval - otherwise use the n closest points + const int minPoints = 4; + auto cavernAtmosPressureStats = o2::math_utils::getRollingStatistics(mCavernAtmosPressure1Buff.second, mCavernAtmosPressure1Buff.first, times, timeInterval, nthreads, minPoints, minPoints); + auto cavernAtmosPressure2Stats = o2::math_utils::getRollingStatistics(mCavernAtmosPressure2Buff.second, mCavernAtmosPressure2Buff.first, times, timeInterval, nthreads, minPoints, minPoints); + auto surfaceAtmosPressureStats = o2::math_utils::getRollingStatistics(mSurfaceAtmosPressureBuff.second, mSurfaceAtmosPressureBuff.first, times, timeInterval, nthreads, minPoints, minPoints); + + // subtract the moving median values from the different sensors if they are ok + std::pair, std::vector> cavernAtmosPressure12; + std::pair, std::vector> cavernAtmosPressure1S; + std::pair, std::vector> cavernAtmosPressure2S; + cavernAtmosPressure12.first.reserve(nIntervals); + cavernAtmosPressure1S.first.reserve(nIntervals); + cavernAtmosPressure2S.first.reserve(nIntervals); + cavernAtmosPressure12.second.reserve(nIntervals); + cavernAtmosPressure1S.second.reserve(nIntervals); + cavernAtmosPressure2S.second.reserve(nIntervals); + + for (int i = 0; i < nIntervals; i++) { + // coarse check if data is close by + const int maxDist = 600 * 1000; + const bool cavernOk = (cavernAtmosPressureStats.median[i] > 0) && (cavernAtmosPressureStats.closestDistanceL[i] < maxDist) && (cavernAtmosPressureStats.closestDistanceR[i] < maxDist); + const bool cavern2Ok = (cavernAtmosPressure2Stats.median[i] > 0) && (cavernAtmosPressure2Stats.closestDistanceL[i] < maxDist) && (cavernAtmosPressure2Stats.closestDistanceR[i] < maxDist); + const bool surfaceOk = (surfaceAtmosPressureStats.median[i] > 0) && (surfaceAtmosPressureStats.closestDistanceL[i] < maxDist) && (surfaceAtmosPressureStats.closestDistanceR[i] < maxDist); + + if (cavernOk && cavern2Ok) { + cavernAtmosPressure12.first.emplace_back(cavernAtmosPressureStats.median[i] - cavernAtmosPressure2Stats.median[i]); + cavernAtmosPressure12.second.emplace_back(times[i]); + } + if (cavernOk && surfaceOk) { + cavernAtmosPressure1S.first.emplace_back(cavernAtmosPressureStats.median[i] - surfaceAtmosPressureStats.median[i]); + cavernAtmosPressure1S.second.emplace_back(times[i]); + } + if (cavern2Ok && surfaceOk) { + cavernAtmosPressure2S.first.emplace_back(cavernAtmosPressure2Stats.median[i] - surfaceAtmosPressureStats.median[i]); + cavernAtmosPressure2S.second.emplace_back(times[i]); + } + } + + fillBuffer(mPressure12Buff, cavernAtmosPressure12, tStartRef, minPointsRef); + fillBuffer(mPressure1SBuff, cavernAtmosPressure1S, tStartRef, minPointsRef); + fillBuffer(mPressure2SBuff, cavernAtmosPressure2S, tStartRef, minPointsRef); + + // get long term median of diffs - this is used for normalization of the pressure values - + auto cavernAtmosPressure12Stats = o2::math_utils::getRollingStatistics(mPressure12Buff.second, mPressure12Buff.first, times, timeIntervalRef, nthreads, 3, minPointsRef); + auto cavernAtmosPressure1SStats = o2::math_utils::getRollingStatistics(mPressure1SBuff.second, mPressure1SBuff.first, times, timeIntervalRef, nthreads, 3, minPointsRef); + auto cavernAtmosPressure2SStats = o2::math_utils::getRollingStatistics(mPressure2SBuff.second, mPressure2SBuff.first, times, timeIntervalRef, nthreads, 3, minPointsRef); + + // calculate diffs of median values + const float maxDist = 20 * timeInterval; + const float maxDiff = 0.2; + std::pair, std::vector> robustPressureTmp; + robustPressureTmp.first.reserve(nIntervals); + robustPressureTmp.second.reserve(nIntervals); + std::vector isOk(nIntervals); + + for (int i = 0; i < nIntervals; ++i) { + // difference beween pressure values corrected for the long term median + const float delta12 = cavernAtmosPressureStats.median[i] - cavernAtmosPressure2Stats.median[i] - cavernAtmosPressure12Stats.median[i]; + const float delta1S = cavernAtmosPressureStats.median[i] - surfaceAtmosPressureStats.median[i] - cavernAtmosPressure1SStats.median[i]; + const float delta2S = cavernAtmosPressure2Stats.median[i] - surfaceAtmosPressureStats.median[i] - cavernAtmosPressure2SStats.median[i]; + + const auto distCavernAtmosPressureL = cavernAtmosPressureStats.closestDistanceL[i]; + const auto distCavernAtmosPressure2L = cavernAtmosPressure2Stats.closestDistanceL[i]; + const auto distSurfaceAtmosPressureL = surfaceAtmosPressureStats.closestDistanceL[i]; + const auto distCavernAtmosPressureR = cavernAtmosPressureStats.closestDistanceR[i]; + const auto distCavernAtmosPressure2R = cavernAtmosPressure2Stats.closestDistanceR[i]; + const auto distSurfaceAtmosPressureR = surfaceAtmosPressureStats.closestDistanceR[i]; + + // check if data is ok + const bool cavernDistOk = (cavernAtmosPressureStats.median[i] > 0) && ((distCavernAtmosPressureL < maxDist) || (distCavernAtmosPressureR < maxDist)); + const bool cavern2DistOk = (cavernAtmosPressure2Stats.median[i] > 0) && ((distCavernAtmosPressure2L < maxDist) || (distCavernAtmosPressure2R < maxDist)); + const bool surfaceDistOk = (surfaceAtmosPressureStats.median[i] > 0) && ((distSurfaceAtmosPressureL < maxDist) || (distSurfaceAtmosPressureR < maxDist)); + const bool onlyOneSensor = (cavernDistOk + cavern2DistOk + surfaceDistOk) == 1; // check if only 1 sensor exists, if so use that sensor + + uint8_t maskIsOkTmp = 0; + const int cavernBit = 0; // val 1 + const int cavern2Bit = 1; // val 2 + const int surfaceBit = 2; // val 4 + + // check if ratio sensor 1 and 2 are good + // maskIsOkTmp = 3 + if (((std::abs(delta12) < maxDiff) && (cavernDistOk && cavern2DistOk)) || onlyOneSensor) { + if (cavernDistOk) { + maskIsOkTmp |= (1 << cavernBit); + } + if (cavern2DistOk) { + maskIsOkTmp |= (1 << cavern2Bit); + } + } + + // check if ratio sensor 1 and surface are good + // maskIsOkTmp = 5 + if ((std::abs(delta1S) < maxDiff) && ((cavernDistOk && surfaceDistOk)) || onlyOneSensor) { + if (cavernDistOk) { + maskIsOkTmp |= (1 << cavernBit); + } + if (surfaceDistOk) { + maskIsOkTmp |= (1 << surfaceBit); + } + } + + // check if ratio sensor 2 and surface are good + // maskIsOkTmp = 6 + if ((std::abs(delta2S) < maxDiff) && ((cavern2DistOk && surfaceDistOk)) || onlyOneSensor) { + if (cavern2DistOk) { + maskIsOkTmp |= (1 << cavern2Bit); + } + if (surfaceDistOk) { + maskIsOkTmp |= (1 << surfaceBit); + } + } + + // calculate robust pressure + float pressure = 0; + int pressureCount = 0; + if ((maskIsOkTmp >> cavernBit) & 1) { + pressure += cavernAtmosPressureStats.median[i]; + pressureCount++; + } + + if ((maskIsOkTmp >> cavern2Bit) & 1) { + pressure += cavernAtmosPressure2Stats.median[i] + cavernAtmosPressure12Stats.median[i]; + pressureCount++; + } + + if ((maskIsOkTmp >> surfaceBit) & 1) { + pressure += surfaceAtmosPressureStats.median[i] + cavernAtmosPressure1SStats.median[i]; + pressureCount++; + } + + isOk[i] = maskIsOkTmp; + if (pressureCount > 0) { + pressure /= pressureCount; + robustPressureTmp.first.emplace_back(pressure); + robustPressureTmp.second.emplace_back(times[i]); + } + } + + fillBuffer(mRobPressureBuff, robustPressureTmp, tStartRef, minPointsRef); + + // drop trailing query times that don't yet have a full look-ahead margin of data + // to their right in the buffer: the smoothing window is ±timeInterval, so without + // it those points would be smoothed with a partially or fully one-sided (past-only) + // window, biasing them low/high and causing a jump at the slot boundary. + const auto& robBuffTimes = mRobPressureBuff.second; + const TimeStampType lookaheadMargin = 2 * timeInterval; + while (times.size() > 1 && !robBuffTimes.empty() && times.back() + lookaheadMargin > robBuffTimes.back()) { + times.pop_back(); + } + isOk.resize(times.size()); + + // the *Stats above were computed for the untrimmed query grid; truncate them to + // match so all vectors stored in RobustPressure stay parallel/same length as time. + // The dropped tail is simply recomputed (with a proper symmetric window) next slot. + trimStats(cavernAtmosPressureStats, times.size()); + trimStats(cavernAtmosPressure2Stats, times.size()); + trimStats(surfaceAtmosPressureStats, times.size()); + trimStats(cavernAtmosPressure12Stats, times.size()); + trimStats(cavernAtmosPressure1SStats, times.size()); + trimStats(cavernAtmosPressure2SStats, times.size()); + + RobustPressure& pOut = robustPressure; + pOut.surfaceAtmosPressure = std::move(surfaceAtmosPressureStats); + pOut.cavernAtmosPressure2 = std::move(cavernAtmosPressure2Stats); + pOut.cavernAtmosPressure = std::move(cavernAtmosPressureStats); + pOut.cavernAtmosPressure12 = std::move(cavernAtmosPressure12Stats); + pOut.cavernAtmosPressure1S = std::move(cavernAtmosPressure1SStats); + pOut.cavernAtmosPressure2S = std::move(cavernAtmosPressure2SStats); + pOut.isOk = std::move(isOk); + pOut.robustPressure = o2::math_utils::getRollingStatistics(mRobPressureBuff.second, mRobPressureBuff.first, times, timeInterval, nthreads, 1, 5).median; + pOut.time = std::move(times); + pOut.timeInterval = timeInterval; + pOut.timeIntervalRef = timeIntervalRef; + pOut.maxDist = maxDist; + pOut.maxDiff = maxDiff; +} + +void Pressure::setAliases(TTree* tree) +{ + tree->SetAlias("cavernDistOk", "robustPressure.cavernAtmosPressure.median>0 && (robustPressure.cavernAtmosPressure.closestDistanceRSetAlias("cavern2DistOk", "robustPressure.cavernAtmosPressure2.median>0 && (robustPressure.cavernAtmosPressure2.closestDistanceRSetAlias("surfaceDistOk", "robustPressure.surfaceAtmosPressure.median>0 && (robustPressure.surfaceAtmosPressure.closestDistanceRSetAlias("onlyOneSensor", "(cavernDistOk + cavern2DistOk + surfaceDistOk) == 1"); + tree->SetAlias("delta12", "robustPressure.cavernAtmosPressure.median - robustPressure.cavernAtmosPressure2.median - robustPressure.cavernAtmosPressure12.median"); + tree->SetAlias("delta1S", "robustPressure.cavernAtmosPressure.median - robustPressure.surfaceAtmosPressure.median - robustPressure.cavernAtmosPressure1S.median"); + tree->SetAlias("delta2S", "robustPressure.surfaceAtmosPressure.median - robustPressure.cavernAtmosPressure2.median - robustPressure.cavernAtmosPressure2S.median"); + tree->SetAlias("delta12_Ok", "abs(delta12)SetAlias("delta1S_Ok", "abs(delta1S)SetAlias("delta2S_Ok", "abs(delta2S) + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::tpc::CTFHeader, 26, uint32_t> + ; #pragma link C++ enum o2::tpc::StatisticsType; #pragma link C++ class o2::tpc::TrackCuts + ; #pragma link C++ class o2::tpc::KrCluster + ; @@ -69,5 +69,12 @@ #pragma link C++ class o2::tpc::dcs::DataPoint < o2::tpc::dcs::HV::StackState> + ; #pragma link C++ class o2::tpc::dcs::DataPointVector < o2::tpc::dcs::HV::StackState> + ; #pragma link C++ class o2::tpc::dcs::Gas + ; +#pragma link C++ class o2::tpc::dcs::Pressure + ; +#pragma link C++ class o2::tpc::dcs::RobustPressure + ; +#pragma link C++ class o2::tpc::PIDResponse + ; +#pragma link C++ class o2::tpc::TriggerWordDLBZS + ; +#pragma link C++ class o2::tpc::TriggerInfoDLBZS + ; +#pragma link C++ class std::vector < o2::tpc::TriggerInfoDLBZS> + ; +#pragma link C++ class o2::tpc::AltroSyncSignal + ; #endif diff --git a/DataFormats/Detectors/TPC/src/TrackCuts.cxx b/DataFormats/Detectors/TPC/src/TrackCuts.cxx index 43039e25734b0..111f3c3da8920 100644 --- a/DataFormats/Detectors/TPC/src/TrackCuts.cxx +++ b/DataFormats/Detectors/TPC/src/TrackCuts.cxx @@ -49,5 +49,8 @@ bool TrackCuts::goodTrack(o2::tpc::TrackTPC const& track) if (dEdx < mdEdxMin) { return false; } + if ((std::abs(track.getOuterParam().getZ()) - std::abs(track.getZ())) < -10) { + return false; + } return true; } diff --git a/DataFormats/Detectors/TRD/CMakeLists.txt b/DataFormats/Detectors/TRD/CMakeLists.txt index fa5d9121527be..35712caf5bfb9 100644 --- a/DataFormats/Detectors/TRD/CMakeLists.txt +++ b/DataFormats/Detectors/TRD/CMakeLists.txt @@ -13,6 +13,8 @@ o2_add_library(DataFormatsTRD SOURCES src/TriggerRecord.cxx src/LinkRecord.cxx src/AngularResidHistos.cxx + src/GainCalibHistos.cxx + src/T0FitHistos.cxx src/DcsCcdbObjects.cxx src/Tracklet64.cxx src/RawData.cxx @@ -35,14 +37,19 @@ o2_target_root_dictionary(DataFormatsTRD include/DataFormatsTRD/CalibratedTracklet.h include/DataFormatsTRD/DcsCcdbObjects.h include/DataFormatsTRD/AngularResidHistos.h + include/DataFormatsTRD/GainCalibHistos.h + include/DataFormatsTRD/T0FitHistos.h include/DataFormatsTRD/HelperMethods.h include/DataFormatsTRD/Hit.h include/DataFormatsTRD/Digit.h include/DataFormatsTRD/KrCluster.h include/DataFormatsTRD/KrClusterTriggerRecord.h include/DataFormatsTRD/NoiseCalibration.h + include/DataFormatsTRD/PHData.h + include/DataFormatsTRD/RawDataStats.h include/DataFormatsTRD/CTF.h include/DataFormatsTRD/CalVdriftExB.h + include/DataFormatsTRD/CalGain.h include/DataFormatsTRD/CalT0.h include/DataFormatsTRD/SignalArray.h include/DataFormatsTRD/CompressedDigit.h @@ -63,4 +70,3 @@ o2_add_test(RawData ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage LABELS trd ) - diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/AngularResidHistos.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/AngularResidHistos.h index bf935866564c0..4380c79cc48e8 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/AngularResidHistos.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/AngularResidHistos.h @@ -38,7 +38,6 @@ class AngularResidHistos size_t getNEntries() const { return mNEntriesTotal; } void fill(const AngularResidHistos& input); - void fill(const gsl::span input); // dummy! void merge(const AngularResidHistos* prev); void print(); diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h new file mode 100644 index 0000000000000..97b3a73a86c03 --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h @@ -0,0 +1,87 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CalGain.h +/// \brief Object with MPV dEdx values per chamber to be written into the CCDB + +#ifndef ALICEO2_CALGAIN_H +#define ALICEO2_CALGAIN_H + +#include "DataFormatsTRD/Constants.h" +#include "Rtypes.h" +#include + +namespace o2 +{ +namespace trd +{ + +class CalGain +{ + public: + CalGain() = default; + CalGain(const CalGain&) = default; + ~CalGain() = default; + + void setMPVdEdx(int iDet, float mpv) { mMPVdEdx[iDet] = mpv; } + + float getMPVdEdx(int iDet, bool defaultAvg = true) const + { + // if defaultAvg = false, we take the value stored whatever it is + // if defaultAvg = true and we have default value or bad value stored, we take the average on all chambers instead + if (!defaultAvg || isGoodGain(iDet)) + return mMPVdEdx[iDet]; + else { + if (std::fabs(mMeanGain + 999.) < 1e-6) + mMeanGain = getAverageGain(); + return mMeanGain; + } + } + + float getAverageGain() const + { + float averageGain = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodGain(iDet)) { + // The chamber has correct calibration + ngood++; + averageGain += mMPVdEdx[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::MPVDEDXDEFAULT; + } + averageGain /= ngood; + return averageGain; + } + + bool isGoodGain(int iDet) const + { + if (std::fabs(mMPVdEdx[iDet] - constants::MPVDEDXDEFAULT) > 1e-6) + return true; + else + return false; + } + + private: + std::array mMPVdEdx{}; ///< Most probable value of dEdx distribution per TRD chamber + mutable float mMeanGain{-999.}; ///! average gain, calculated only once + + ClassDefNV(CalGain, 2); +}; + +} // namespace trd +} // namespace o2 + +#endif // ALICEO2_CALGAIN_H diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h index 30474888a7424..69fc23646c912 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h @@ -32,13 +32,38 @@ class CalT0 ~CalT0() = default; void setT0(int iDet, float t0) { mT0[iDet] = t0; } + void setT0av(float t0) { mT0av = t0; } float getT0(int iDet) const { return mT0[iDet]; } + // getT0av() returns the average T0 obtained by fitting the data from all chambers combined + float getT0av() const { return mT0av; } + // calcT0av() returns the average T0 from all individual chambers + float calcT0av() const + { + if (mT0.size() == 0) { + return -1; + } + float sum = 0; + int counts = 0; + for (int iDet = 0; iDet < constants::MAXCHAMBER; ++iDet) { + if (mT0[iDet] > -5) { + sum += mT0[iDet]; + ++counts; + } + } + + if (counts > 0) { + return sum / counts; + } else { + return -2; + } + } private: std::array mT0{}; ///< calibrated T0 per TRD chamber + float mT0av{-1}; ///< average T0 obtained from fitting the PH data from all chambers combined - ClassDefNV(CalT0, 1); + ClassDefNV(CalT0, 2); }; } // namespace trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h index bad9dcfef4e37..280f9d0d4b8a9 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h @@ -34,14 +34,102 @@ class CalVdriftExB void setVdrift(int iDet, float vd) { mVdrift[iDet] = vd; } void setExB(int iDet, float exb) { mExB[iDet] = exb; } - float getVdrift(int iDet) const { return mVdrift[iDet]; } - float getExB(int iDet) const { return mExB[iDet]; } + float getVdrift(int iDet, bool defaultAvg = true) const + { + // if defaultAvg = false, we take the value stored whatever it is + // if defaultAvg = true and we have default value or bad value stored, we take the average on all chambers instead + if (!defaultAvg || (isGoodExB(iDet) && isGoodVdrift(iDet))) + return mVdrift[iDet]; + else { + if (std::fabs(mMeanVdrift + 999.) < 1e-6) + mMeanVdrift = getAverageVdrift(); + return mMeanVdrift; + } + } + float getExB(int iDet, bool defaultAvg = true) const + { + if (!defaultAvg || (isGoodExB(iDet) && isGoodVdrift(iDet))) + return mExB[iDet]; + else { + if (std::fabs(mMeanExB + 999.) < 1e-6) + mMeanExB = getAverageExB(); + return mMeanExB; + } + } + + float getAverageVdrift() const + { + float averageVdrift = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodExB(iDet) && isGoodVdrift(iDet)) { + // Both values need to be correct to declare a chamber as well calibrated + ngood++; + averageVdrift += mVdrift[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::VDRIFTDEFAULT; + } + averageVdrift /= ngood; + return averageVdrift; + } + + float getAverageExB() const + { + float averageExB = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodExB(iDet) && isGoodVdrift(iDet)) { + // Both values need to be correct to declare a chamber as well calibrated + ngood++; + averageExB += mExB[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::EXBDEFAULT; + } + averageExB /= ngood; + return averageExB; + } + + bool isGoodExB(int iDet) const + { + // check if value is well calibrated or not + // default calibration if not enough entries + // close to boundaries indicate a failed fit + if (std::fabs(mExB[iDet] - constants::EXBDEFAULT) > 1e-6 && + std::fabs(mExB[iDet] - constants::EXBMIN) > 0.01 && + std::fabs(mExB[iDet] - constants::EXBMAX) > 0.01) + return true; + else + return false; + } + + bool isGoodVdrift(int iDet) const + { + // check if value is well calibrated or not + // default calibration if not enough entries + // close to boundaries indicate a failed fit + if (std::fabs(mVdrift[iDet] - constants::VDRIFTDEFAULT) > 1e-6 && + std::fabs(mVdrift[iDet] - constants::VDRIFTMIN) > 0.1 && + std::fabs(mVdrift[iDet] - constants::VDRIFTMAX) > 0.1) + return true; + else + return false; + } private: std::array mVdrift{}; ///< calibrated drift velocity per TRD chamber std::array mExB{}; ///< calibrated Lorentz angle per TRD chamber + mutable float mMeanVdrift{-999.}; ///! average drift velocity, calculated only once + mutable float mMeanExB{-999.}; ///! average lorentz angle, calculated only once - ClassDefNV(CalVdriftExB, 1); + ClassDefNV(CalVdriftExB, 2); }; } // namespace trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h index 8185bf2718629..9a4da1024e251 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h @@ -41,6 +41,7 @@ constexpr int TRDLINKID = 15; ///< hard coded link id, specific to TRD constexpr int NCOLUMN = 144; ///< the number of pad columns for each chamber constexpr int NROWC0 = 12; ///< the number of pad rows for chambers of type C0 (installed in stack 2) constexpr int NROWC1 = 16; ///< the number of pad rows for chambers of type C1 (installed in stacks 0, 1, 3 and 4) +constexpr int FIRSTROW[NSTACK] = {0, 16, 32, 44, 60}; ///< first pad row for each stack constexpr int NMCMROB = 16; ///< the number of MCMs per ROB constexpr int NMCMHCMAX = 64; ///< the maximum number of MCMs for one half chamber (C1 type) @@ -74,7 +75,14 @@ constexpr int TIMEBINS = 30; ///< the number of time bins constexpr float MAXIMPACTANGLE = 25.f; ///< the maximum impact angle for tracks relative to the TRD detector plane to be considered for vDrift and ExB calibration constexpr int NBINSANGLEDIFF = 25; ///< the number of bins for the track angle used for the vDrift and ExB calibration based on the tracking constexpr double VDRIFTDEFAULT = 1.546; ///< default value for vDrift +constexpr double VDRIFTMIN = 0.4; ///< min value for vDrift +constexpr double VDRIFTMAX = 2.0; ///< max value for vDrift constexpr double EXBDEFAULT = 0.0; ///< default value for LorentzAngle +constexpr double EXBMIN = -0.4; ///< min value for LorentzAngle +constexpr double EXBMAX = 0.4; ///< max value for LorentzAngle +constexpr int NBINSGAINCALIB = 320; ///< number of bins in the charge (Q0+Q1+Q2) histogram for gain calibration +constexpr float MPVDEDXDEFAULT = 42.; ///< default Most Probable Value of TRD dEdx +constexpr float T0DEFAULT = 1.2; ///< default value for t0 // array size to store incoming half cru payload. constexpr int HBFBUFFERMAX = 1048576; ///< max buffer size for data read from a half cru, (all events) diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h index b76732f80c5d4..9eba0318a5a13 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h @@ -59,7 +59,7 @@ class Digit Digit(int det, int row, int pad, ArrayADC adc, int phase = 0); Digit(int det, int row, int pad); // add adc data and pretrigger phase in a separate step Digit(int det, int rob, int mcm, int channel, ArrayADC adc, int phase = 0); - Digit(int det, int rob, int mcm, int channel); // add adc data in a seperate step + Digit(int det, int rob, int mcm, int channel, int phase = 0); // add adc data // Copy Digit(const Digit&) = default; @@ -74,22 +74,27 @@ class Digit void setDetector(int det) { mDetector = ((mDetector & 0xf000) | (det & 0xfff)); } void setADC(ArrayADC const& adc) { mADC = adc; } void setADC(const gsl::span& adc) { std::copy(adc.begin(), adc.end(), mADC.begin()); } - void setPreTrigPhase(int phase) { mDetector = (((phase & 0xf) << 12) | (mDetector & 0xfff)); } + // set the trigger phase make sure it is mapped to 2 bits as it can only have 4 valid numbers shifted 0,3,6,9 or 1,4,7,10 etc. + void setPreTrigPhase(int phase); // Get methods int getDetector() const { return mDetector & 0xfff; } + int getDetectorInFull() const { return mDetector; } // return the entire mDetector 16 bits, so far only for CTF encoding. int getHCId() const { return (mDetector & 0xfff) * 2 + (mROB % 2); } int getPadRow() const { return HelperMethods::getPadRowFromMCM(mROB, mMCM); } int getPadCol() const { return HelperMethods::getPadColFromADC(mROB, mMCM, mChannel); } int getROB() const { return mROB; } int getMCM() const { return mMCM; } + int getMCMCol() const { return (getMCM() % constants::NMCMROBINCOL) + constants::NMCMROBINCOL * (getROB() % 2); } int getChannel() const { return mChannel; } int getPreTrigPhase() const { return ((mDetector >> 12) & 0xf); } bool isSharedDigit() const; + bool isNeighbour(const Digit& other) const; ArrayADC const& getADC() const { return mADC; } ADC_t getADCsum() const { return std::accumulate(mADC.begin(), mADC.end(), (ADC_t)0); } // returns the max ADC value and sets idx to the time bin with the largest ADC value ADC_t getADCmax(int& idx) const; + ADC_t getADCval(int tb) const { return mADC[tb]; } bool operator==(const Digit& o) const { diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/GainCalibHistos.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/GainCalibHistos.h new file mode 100644 index 0000000000000..6142646950969 --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/GainCalibHistos.h @@ -0,0 +1,53 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GainCalibHistos.h +/// \brief Class to store the TRD dEdx distribution for each TRD chamber + +#ifndef ALICEO2_GAINCALIBHISTOS_H +#define ALICEO2_GAINCALIBHISTOS_H + +#include "DataFormatsTRD/Constants.h" +#include "Rtypes.h" +#include + +namespace o2 +{ +namespace trd +{ + +class GainCalibHistos +{ + public: + GainCalibHistos() = default; + GainCalibHistos(const GainCalibHistos&) = default; + ~GainCalibHistos() = default; + void reset(); + void init(); + auto getHistogramEntry(int index) const { return mdEdxEntries[index]; } + auto getNEntries() const { return mNEntriesTot; } + + void fill(const std::vector& input); + void merge(const GainCalibHistos* prev); + void print(); + + private: + std::vector mdEdxEntries{}; ///< dEdx histograms + size_t mNEntriesTot{0}; + bool mInitialized{false}; + + ClassDefNV(GainCalibHistos, 1); +}; + +} // namespace trd +} // namespace o2 + +#endif // ALICEO2_GAINCALIBHISTOS_H diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h index 4468528865d3b..ecab7f49b92a2 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h @@ -14,6 +14,8 @@ #include "DataFormatsTRD/Constants.h" #include +#include +#include namespace o2 { @@ -54,12 +56,17 @@ struct HelperMethods { printf("%02i_%i_%i\n", det / constants::NCHAMBERPERSEC, (det % constants::NCHAMBERPERSEC) / constants::NLAYER, det % constants::NLAYER); } - static void printSectorStackLayerSide(int hcid) + static std::string getSectorStackLayerSide(int hcid) { - // for a given half-chamber number prints SECTOR_STACK_LAYER_side int det = hcid / 2; std::string side = (hcid % 2 == 0) ? "A" : "B"; - printf("%02i_%i_%i%s\n", det / constants::NCHAMBERPERSEC, (det % constants::NCHAMBERPERSEC) / constants::NLAYER, det % constants::NLAYER, side.c_str()); + return fmt::format("{}_{}_{}{}", getSector(det), getStack(det), getLayer(det), side); + } + + static void printSectorStackLayerSide(int hcid) + { + // for a given half-chamber number prints SECTOR_STACK_LAYER_side + printf("%s\n", getSectorStackLayerSide(hcid).c_str()); } static int getPadColFromADC(int irob, int imcm, int iadc) diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h new file mode 100644 index 0000000000000..fc46ca0207993 --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h @@ -0,0 +1,126 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_TRD_PHDATA_H_ +#define ALICEO2_TRD_PHDATA_H_ + +#include +#include "Rtypes.h" + +namespace o2::trd +{ + +/* + This data type is used to send around the information required to fill PH plots per chamber + + |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| + ------------------------------------------------------------------------------------------------- + |type |nNeighb | time bin | detector number | ADC sum for all neigbours | + ------------------------------------------------------------------------------------------------- +*/ + +class PHData +{ + public: + enum Origin : uint8_t { + ITSTPCTRD, + TPCTRD, + TRACKLET, + OTHER + }; + + PHData() = default; + PHData(int adc, int det, int tb, int nb, int type) { set(adc, det, tb, nb, type); } + + void set(int adc, int det, int tb, int nb, int type) + { + mData = ((type & 0x3) << 30) | ((nb & 0x7) << 27) | ((tb & 0x1f) << 22) | ((det & 0x3ff) << 12) | (adc & 0xfff); + } + + // the ADC sum for given time bin for up to three neighbours + int getADC() const { return mData & 0xfff; } + // the TRD detector number + int getDetector() const { return (mData >> 12) & 0x3ff; } + // the given time bin + int getTimebin() const { return (mData >> 22) & 0x1f; } + // number of neighbouring digits for which the ADC is accumulated + int getNneighbours() const { return (mData >> 27) & 0x7; } + // the origin of this point: digit on ITS-TPC-TRD track, ... (see enum Origin above) + int getType() const { return (mData >> 30) & 0x3; } + + private: + uint32_t mData{0}; // see comment above for data content + + ClassDefNV(PHData, 1); +}; + +/* + This data type is used to send around the information required to fill PH plots per chamber + + |19|18|17|16|15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| + ------------------------------------------------------------- + |type |nNeighb | time bin | detector number | + ------------------------------------------------------------- +*/ +/* + This data type is used to send around the information required to fill PH plots per chamber + + |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| + ------------------------------------------------ + | ADC sum for all neigbours | + ------------------------------------------------ +*/ + +class PHDataHD +{ + public: + enum Origin : uint8_t { + ITSTPCTRD, + TPCTRD, + TRACKLET, + OTHER + }; + + PHDataHD() = default; + PHDataHD(int adc, int det, int tb, int nb, int type) { set(adc, det, tb, nb, type); } + + void set(int adc, int det, int tb, int nb, int type) + { + mDetector = det; + mTimeBin = tb; + mType = type; + mNNeighbours = nb; + mADC = adc; + } + + // the ADC sum for given time bin for up to three neighbours + int getADC() const { return mADC; } + // the TRD detector number + int getDetector() const { return mDetector; } + // the given time bin + int getTimebin() const { return mTimeBin; } + // number of neighbouring digits for which the ADC is accumulated + int getNNeighbours() const { return mNNeighbours; } + // the origin of this point: digit on ITS-TPC-TRD track, ... (see enum Origin above) + int getType() const { return mType; } + + private: + uint16_t mDetector{0}; + uint8_t mTimeBin{0}; + uint8_t mType{0}; + uint8_t mNNeighbours{0}; + uint16_t mADC{0}; + + ClassDefNV(PHDataHD, 1); +}; +} // namespace o2::trd + +#endif // ALICEO2_TRD_PHDATA_H_ diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/PID.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PID.h index cbb62d8f1e164..d99a5be14db4a 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/PID.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PID.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace o2 { @@ -28,46 +29,71 @@ namespace trd enum class PIDPolicy : unsigned int { // Classical Algorithms LQ1D = 0, ///< 1-Dimensional Likelihood model + LQ2D, ///< 2-Dimensional Likelihood model LQ3D, ///< 3-Dimensional Likelihood model +#ifdef TRDPID_WITH_ONNX // ML models XGB, ///< XGBOOST + PY, ///< Pytorch +#endif // Do not add anything after this! NMODELS, ///< Count of all models - Test, ///< Load object for testing Dummy, ///< Dummy object outputting -1.f DEFAULT = Dummy, ///< The default option }; +inline std::ostream& operator<<(std::ostream& os, const PIDPolicy& policy) +{ + std::string name; + switch (policy) { + case PIDPolicy::LQ1D: + name = "LQ1D"; + break; + case PIDPolicy::LQ2D: + name = "LQ2D"; + break; + case PIDPolicy::LQ3D: + name = "LQ3D"; + break; +#ifdef TRDPID_WITH_ONNX + case PIDPolicy::XGB: + name = "XGBoost"; + break; + case PIDPolicy::PY: + name = "PyTorch"; + break; +#endif + case PIDPolicy::Dummy: + name = "Dummy"; + break; + default: + name = "Default"; + } + os << name; + return os; +} + /// Transform PID policy from string to enum. static const std::unordered_map PIDPolicyString{ // Classical Algorithms {"LQ1D", PIDPolicy::LQ1D}, + {"LQ2D", PIDPolicy::LQ2D}, {"LQ3D", PIDPolicy::LQ3D}, +#ifdef TRDPID_WITH_ONNX // ML models {"XGB", PIDPolicy::XGB}, + {"PY", PIDPolicy::PY}, +#endif // General - {"TEST", PIDPolicy::Test}, {"DUMMY", PIDPolicy::Dummy}, // Default {"default", PIDPolicy::DEFAULT}, }; -/// Transform PID policy from string to enum. -static const char* PIDPolicyEnum[] = { - "LQ1D", - "LQ3D", - "XGBoost", - "NMODELS", - "Test", - "Dummy", - "default(=TODO)"}; - -using PIDValue = float; - } // namespace trd } // namespace o2 diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h index 0bc9ec474529e..f7f5f96208191 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h @@ -447,10 +447,10 @@ void setHalfCRUHeaderLinkSizeAndFlags(HalfCRUHeader& cruhead, int link, int size DigitMCMADCMask constructBlankADCMask(); uint32_t getHalfCRULinkInfo(const HalfCRUHeader& cruhead, const uint32_t link, const bool data); -uint32_t getHalfCRULinkErrorFlag(const HalfCRUHeader& cruhead, const uint32_t link); -uint32_t getHalfCRULinkDataSize(const HalfCRUHeader& cruhead, const uint32_t link); -void getHalfCRULinkErrorFlags(const HalfCRUHeader& cruheader, std::array& linkerrorflags); -void getHalfCRULinkDataSizes(const HalfCRUHeader& cruheader, std::array& linksizes); +uint8_t getHalfCRULinkErrorFlag(const HalfCRUHeader& cruhead, const uint32_t link); +uint16_t getHalfCRULinkDataSize(const HalfCRUHeader& cruhead, const uint32_t link); +void getHalfCRULinkErrorFlags(const HalfCRUHeader& cruheader, std::array& linkerrorflags); +void getHalfCRULinkDataSizes(const HalfCRUHeader& cruheader, std::array& linksizes); std::ostream& operator<<(std::ostream& stream, const TrackletHCHeader& halfchamberheader); std::ostream& operator<<(std::ostream& stream, const TrackletMCMHeader& tracklmcmhead); std::ostream& operator<<(std::ostream& stream, const TrackletMCMData& trackletmcmdata); diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h index f75c684a37fd8..04ebd9c7eea3a 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h @@ -16,14 +16,13 @@ #ifndef O2_TRD_RAWDATASTATS #define O2_TRD_RAWDATASTATS -#include "TObject.h" +#include "Rtypes.h" #include #include #include -#include #include -#include #include "DataFormatsTRD/Constants.h" +#include "CommonDataFormat/TFIDInfo.h" namespace o2::trd { @@ -120,22 +119,14 @@ enum OptionBits { TRDIgnore2StageTrigger, TRDGenerateStats, TRDOnlyCalibrationTriggerBit, - TRDDisableRootOutputBit + TRDSortDigits, + TRDLinkStats }; // this is currently 16 options, the array is 16, if you add here you need to change the 16; -//Data to be stored and accumulated on an event basis. -//events are spread out with in the data coming in with a halfcruheader per event, per ... half cru. -//this is looked up via the interaction record (orbit and bunchcrossing). -//this permits averaging in the data that gets senton per timeframe -struct TRDDataCountersPerEvent { - TRDDataCountersPerEvent() = default; - double mTimeTaken = 0.; // time take to process an event (summed trackletparsing and digitparsing) parts not accounted for. - double mTimeTakenForDigits = 0.; // time take to process tracklet data blocks [us]. - double mTimeTakenForTracklets = 0.; // time take to process digit data blocks [us]. - uint64_t mWordsRead = 0; // words read in - uint64_t mWordsRejected = 0; // words skipped for various reasons. - uint16_t mTrackletsFound = 0; // tracklets found in the event - uint16_t mDigitsFound = 0; // digits found in the event +struct DataCountersPerTrigger { + std::array mLinkWords{}; + std::array mLinkErrorFlag{}; + ClassDefNV(DataCountersPerTrigger, 1); }; //Data to be stored on a timeframe basis to then be sent as a message to be ultimately picked up by qc. @@ -143,39 +134,42 @@ struct TRDDataCountersPerEvent { class TRDDataCountersPerTimeFrame { public: - std::array mLinkErrorFlag{}; // status of the error flags for this timeframe, 8bit values from cru halfchamber header. - std::array mLinkNoData; // Link had no data or was not present. - std::array mLinkWords{}; // units of 256bits, read from the cru half chamber header - std::array mLinkWordsRead{}; // units of 32 bits the data words read before dumping or finishing - std::array mLinkWordsRejected{}; // units of 32 bits the data dumped due to some or other error + std::array mLinkErrorFlag{}; // status of the error flags for this timeframe, 8bit values from cru halfchamber header. + std::array mLinkNoData{}; // Link had no data or was not present. + std::array mLinkWords{}; // units of 256bits, read from the cru half chamber header + std::array mLinkWordsRead{}; // units of 32 bits the data words read before dumping or finishing + std::array mLinkWordsRejected{}; // units of 32 bits the data dumped due to some or other error + std::array mParsingOK{}; // count how often given link could be parsed without any errors std::array mParsingErrors{}; // errors in parsing, indexed by enum above of ParsingErrors - std::array mParsingErrorsByLink{}; // errors in parsing, indexed by enum above of ParsingErrors - uint16_t mDigitsPerEvent; // average digits found per event in this timeframe, ignoring the no digit events where there is no calibration trigger. - uint16_t mTrackletsPerEvent; // average tracklets found per event in this timeframe - double mTimeTaken; // time taken to process the entire timeframe [ms]. - double mTimeTakenForDigits; // time take to process tracklet data blocks [ms]. - double mTimeTakenForTracklets; // time take to process digit data blocks [ms]. + std::vector mParsingErrorsByLink{}; // each entry is for a single parsing error on a given link (HCID * number of Errors + error index) + float mTimeTaken; // time taken to process all half-CRU data blocks combined [us]. + float mTimeTakenForDigits; // time take to process tracklet data blocks [us]. + float mTimeTakenForTracklets; // time take to process digit data blocks [us]. uint32_t mDigitsFound; // digits found in the time frame. uint32_t mTrackletsFound; // tracklets found in the time frame. - std::array mDataFormatRead{}; // We just keep the major version number + uint16_t mNTriggersCalib; // number of triggers with digit readout + uint16_t mNTriggersTotal; // total number of triggers + std::array mDataFormatRead{}; // We just keep the major version number + o2::dataformats::TFIDInfo mTFIDInfo; // keep track of TF ID void clear() { mLinkNoData.fill(0); mLinkWords.fill(0); mLinkWordsRead.fill(0); mLinkWordsRejected.fill(0); + mParsingOK.fill(0); mParsingErrors.fill(0); - mParsingErrorsByLink.fill(0); - mDigitsPerEvent = 0; - mTrackletsPerEvent = 0; + mParsingErrorsByLink.clear(); mTimeTaken = 0; mTimeTakenForDigits = 0; mTimeTakenForTracklets = 0; mDigitsFound = 0; - mTrackletsFound = 0; //tracklets found in timeframe. + mTrackletsFound = 0; + mNTriggersCalib = 0; + mNTriggersTotal = 0; mDataFormatRead.fill(0); }; - ClassDefNV(TRDDataCountersPerTimeFrame, 1); // primarily for serialisation so we can send this as a message in o2 + ClassDefNV(TRDDataCountersPerTimeFrame, 3); // primarily for serialisation so we can send this as a message in o2 }; } // namespace o2::trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RecoInputContainer.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RecoInputContainer.h index 21a079ec0df77..032dd4162a785 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RecoInputContainer.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RecoInputContainer.h @@ -27,7 +27,7 @@ #include "Framework/InputRecord.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include #include @@ -88,7 +88,7 @@ inline void RecoInputContainer::fillGPUIOPtr(o2::gpu::GPUTrackingInOutPointers* ptrs->nTRDTriggerRecords = mNTriggerRecords; ptrs->trdTriggerTimes = &(trdTriggerTimes[0]); ptrs->trdTrackletIdxFirst = &(trdTriggerIndices[0]); - ptrs->trdTrigRecMask = reinterpret_cast(mTrigRecMask.data()); + ptrs->trdTrigRecMask = reinterpret_cast(mTrigRecMask.data()); ptrs->nTRDTracklets = mNTracklets; ptrs->trdTracklets = reinterpret_cast(mTracklets.data()); ptrs->trdSpacePoints = reinterpret_cast(mSpacePoints.data()); diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/T0FitHistos.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/T0FitHistos.h new file mode 100644 index 0000000000000..93448696025c9 --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/T0FitHistos.h @@ -0,0 +1,56 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file T0FitHistos.h +/// \brief Class to store the TRD PH values for each TRD chamber + +#ifndef ALICEO2_T0FITHISTOS_H +#define ALICEO2_T0FITHISTOS_H + +#include "DataFormatsTRD/Constants.h" +#include "DataFormatsTRD/PHData.h" +#include "Rtypes.h" +#include +#include + +namespace o2 +{ +namespace trd +{ + +class T0FitHistos +{ + public: + T0FitHistos() = default; + T0FitHistos(const T0FitHistos&) = default; + ~T0FitHistos() = default; + auto getDetector(int index) const { return mDet[index]; } + auto getTimeBin(int index) const { return mTB[index]; } + auto getADC(int index) const { return mADC[index]; } + auto getNEntries() const { return mNEntriesTot; } + + void fill(const std::vector& data); + void merge(const T0FitHistos* prev); + void print(); + + private: + std::vector mDet{}; + std::vector mTB{}; + std::vector mADC{}; + size_t mNEntriesTot{0}; + + ClassDefNV(T0FitHistos, 1); +}; + +} // namespace trd +} // namespace o2 + +#endif // ALICEO2_T0FITHISTOS_H diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/TrackTRD.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/TrackTRD.h index 9dcbde05fb743..b2182636d4591 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/TrackTRD.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/TrackTRD.h @@ -16,6 +16,7 @@ #define O2_DATAFORMATS_TRACK_TRD_H #include "GPUTRDTrack.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" namespace o2 { diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h index 463effd19106f..e63d8fbb5f277 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h @@ -9,9 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -//#include "TRDBase/GeometryBase.h" -//#include "DetectorsCommonDataFormats/DetMatrixCache.h" -//#include "DetectorsCommonDataFormats/DetID.h" +// #include "TRDBase/GeometryBase.h" +// #include "DetectorsCommonDataFormats/DetMatrixCache.h" +// #include "DetectorsCommonDataFormats/DetID.h" #ifndef O2_TRD_TRACKLET64_H #define O2_TRD_TRACKLET64_H @@ -27,8 +27,12 @@ // //////////////////////////////////////////////////////////////////////////// +#ifndef GPUCA_GPUCODE_DEVICE +#include +#endif #include "GPUCommonDef.h" #include "GPUCommonRtypes.h" +#include "GPUCommonMath.h" #include "DataFormatsTRD/Constants.h" namespace o2 @@ -84,16 +88,73 @@ class Tracklet64 GPUdDefault() ~Tracklet64() = default; GPUdDefault() Tracklet64& operator=(const Tracklet64& rhs) = default; + GPUd() bool operator==(const Tracklet64& o) const { return mtrackletWord == o.mtrackletWord; } + + // ----- Getters ----- + GPUd() uint64_t getTrackletWord() const { return mtrackletWord; } + + // position / direction information + + // half-chamber number [0..1079] + GPUd() int getHCID() const { return ((mtrackletWord & hcidmask) >> hcidbs); }; + + // chamber number [0..539] + GPUd() int getDetector() const { return getHCID() / 2; } + + // pad row number on ROB [0..15] + GPUd() int getPadRow() const { return ((mtrackletWord & padrowmask) >> padrowbs); }; + + // MCM position in column direction on ROB [0..3] + GPUd() int getColumn() const { return ((mtrackletWord & colmask) >> colbs); }; + + // in units of 1/40 pads, 11 bit granularity (MSP is used to indicate sign) + GPUd() int getPosition() const { return ((mtrackletWord & posmask) >> posbs); }; + + // applies two's complement to getPosition() to obtain signed value + GPUd() int getPositionBinSigned() const; - // ----- Getters for contents of tracklet word ----- - GPUd() int getFormat() const { return ((mtrackletWord & formatmask) >> formatbs); }; // no units 0..15 - GPUd() int getHCID() const { return ((mtrackletWord & hcidmask) >> hcidbs); }; // no units 0..1079 - GPUd() int getPadRow() const { return ((mtrackletWord & padrowmask) >> padrowbs); }; // pad row number [0..15] - GPUd() int getPadCol() const; // estimate the pad column number from the tracklet offset (can be off by +-1 pad) - GPUd() int getColumn() const { return ((mtrackletWord & colmask) >> colbs); }; // column refers to MCM position in column direction on readout board [0..3] - GPUd() int getPosition() const { return ((mtrackletWord & posmask) >> posbs); }; // in units of 1/40 pads, 11 bit granularity - GPUd() int getSlope() const { return ((mtrackletWord & slopemask) >> slopebs); }; // in units of 1/1000 pads/timebin, 8 bit granularity - GPUd() int getPID() const { return ((mtrackletWord & PIDmask)); }; // no unit, all 3 charge windows combined + // the position as float in unit of pads relative to MCM center + GPUd() float getPositionFloat() const { return getPositionBinSigned() * constants::GRANULARITYTRKLPOS; } + + // in units of 1/128 pads/timebin, 8 bit granularity (MSP is used to indicate sign) + GPUd() int getSlope() const { return ((mtrackletWord & slopemask) >> slopebs); }; + + // applies two's complement to getSlope() to obtain signed value + GPUd() int getSlopeBinSigned() const; + + // as float in units of pads per time bin + GPUd() float getSlopeFloat() const { return getSlopeBinSigned() * constants::GRANULARITYTRKLSLOPE / constants::ADDBITSHIFTSLOPE; } + + // MCM position on ROB [0..15] + GPUd() int getMCM() const { return constants::NMCMROBINROW * (getPadRow() % constants::NMCMROBINCOL) + getColumn(); } + + // ROB number [0..5] for C0 chamber and [0..7] for C1 chamber + GPUd() int getROB() const { return (getHCID() % 2) ? (getPadRow() / constants::NMCMROBINROW) * 2 + 1 : (getPadRow() / constants::NMCMROBINROW) * 2; } + + // MCM number in pad column direction [0..7] + GPUd() int getMCMCol() const { return (getMCM() % constants::NMCMROBINCOL) + constants::NMCMROBINCOL * (getROB() % 2); } + + // pad width in cm required for transformation from pad coordinates to tracking coordinates + GPUd() float getPadWidth() const { return 0.635f + 0.03f * (getDetector() % constants::NLAYER); } + + // pad column number inside pad row as float + // FIXME: understand why the offset seems to be 8 pads and not nChannels / 2 = 10.5 + // Due to wrong pad shift included in alignment we need to optionally shift the tracklets by one pad + // in case we are not using the ideal alignment + GPUd() float getPadColFloat(bool applyShift) const { return getPositionFloat() + getMCMCol() * constants::NCOLMCM + 8.f + (applyShift ? 1.f : 0.f); } + + // pad column number inside pad row as int can be off by +-1 pad (same function name as for TRD digit) + GPUd() int getPadCol(bool applyShift = false) const { return o2::gpu::CAMath::Float2IntRn(getPadColFloat(applyShift)); } + + // translate local position into global y (in cm) not taking into account calibrations (ExB, vDrift, t0) + GPUd() float getUncalibratedY(bool applyShift = false) const { return (getPadColFloat(applyShift) - (constants::NCOLUMN / 2.f)) * getPadWidth(); } + + // translate local slope into dy/dx with dx=3m (drift length) and default drift time in time bins (19.4 timebins / 3cm) + GPUd() float getUncalibratedDy(float nTbDrift = 19.4f) const { return getSlopeFloat() * getPadWidth() * nTbDrift; } + + // PID related getters + GPUd() int getFormat() const { return ((mtrackletWord & formatmask) >> formatbs); }; + GPUd() int getPID() const { return ((mtrackletWord & PIDmask)); }; GPUd() int getDynamicCharge(unsigned int charge) const { int shift = (charge >> 6) & 0x3; @@ -130,54 +191,14 @@ class Tracklet64 } }; // no unit + // ----- Setters for tracklet word manipulation ----- GPUd() void setTrackletWord(uint64_t trackletword) { mtrackletWord = trackletword; } - - // ----- Getters for tracklet information ----- - GPUd() int getMCM() const { return 4 * (getPadRow() % 4) + getColumn(); } // returns MCM position on ROB [0..15] - GPUd() int getROB() const { return (getHCID() % 2) ? (getPadRow() / 4) * 2 + 1 : (getPadRow() / 4) * 2; } // returns ROB number [0..5] for C0 chamber and [0..7] for C1 chamber - GPUd() int getPositionBinSigned() const; - GPUd() int getSlopeBinSigned() const; - GPUd() float getUncalibratedY() const; // translate local position into global y (in cm) not taking into account calibrations (ExB, vDrift, t0) - GPUd() float getUncalibratedDy(float nTbDrift = 19.4f) const; // translate local slope into dy/dx with dx=3m (drift length) and default drift time in time bins (19.4 timebins / 3cm) - - // ----- Getters for offline corresponding values ----- - GPUd() int getDetector() const { return getHCID() / 2; } - - GPUd() uint64_t getTrackletWord() const { return mtrackletWord; } - - GPUd() void setQ0(int charge) - { - mtrackletWord &= ~Q0mask; - mtrackletWord |= ((charge << Q0bs) & Q0mask); - } - GPUd() void setQ1(int charge) - { - mtrackletWord &= ~Q1mask; - mtrackletWord |= ((charge << Q1bs) & Q1mask); - } - GPUd() void setQ2(int charge) - { - mtrackletWord &= ~Q2mask; - mtrackletWord |= ((charge << Q2bs) & Q2mask); - } - GPUd() void setPID(uint64_t pid) - { - // set the entire pid area of the trackletword, all the 3 Q's - mtrackletWord &= ~PIDmask; - mtrackletWord |= ((pid << PIDbs) & PIDmask); - } - GPUd() void setPosition(uint64_t position) - { - mtrackletWord &= ~posmask; - mtrackletWord |= ((position << posbs) & posmask); - } - GPUd() void setSlope(uint64_t slope) - { - mtrackletWord &= ~slopemask; - mtrackletWord |= ((slope << slopebs) & slopemask); - } - - GPUd() bool operator==(const Tracklet64& o) const { return mtrackletWord == o.mtrackletWord; } + GPUd() void setQ0(int charge) { mtrackletWord = (mtrackletWord & ~Q0mask) | ((charge & Q0mask) << Q0bs); } + GPUd() void setQ1(int charge) { mtrackletWord = (mtrackletWord & ~Q1mask) | ((charge & Q1mask) << Q1bs); } + GPUd() void setQ2(int charge) { mtrackletWord = (mtrackletWord & ~Q2mask) | ((charge & Q2mask) << Q2bs); } + GPUd() void setPID(int pid) { mtrackletWord = (mtrackletWord & ~PIDmask) | ((pid & PIDmask) << PIDbs); } + GPUd() void setPosition(int position) { mtrackletWord = (mtrackletWord & ~posmask) | ((position & posmask) << posbs); } + GPUd() void setSlope(int slope) { mtrackletWord = (mtrackletWord & ~slopemask) | ((slope & slopemask) << slopebs); } GPUd() void print() const; #ifndef GPUCA_GPUCODE_DEVICE @@ -195,7 +216,7 @@ class Tracklet64 static constexpr uint64_t Q1mask = 0x000000000000ff00; static constexpr uint64_t Q0mask = 0x00000000000000ff; static constexpr uint64_t PIDmask = 0x0000000000ffffff; - //bit shifts for the above raw data + // bit shifts for the above raw data static constexpr uint64_t formatbs = 60; static constexpr uint64_t hcidbs = 49; static constexpr uint64_t padrowbs = 45; @@ -225,17 +246,6 @@ GPUdi() int Tracklet64::getPositionBinSigned() const return padLocal; } -GPUdi() float Tracklet64::getUncalibratedY() const -{ - int padLocal = getPositionBinSigned(); - int mcmCol = (getMCM() % constants::NMCMROBINCOL) + constants::NMCMROBINCOL * (getROB() % 2); - // one pad column has 144 pads, the offset of -63 is the center of the first MCM in that column - // which is connected to the pads -63 - 9 = -72 to -63 + 9 = -54 - float offset = -63.f + ((float)constants::NCOLMCM) * mcmCol; - float padWidth = 0.635f + 0.03f * (getDetector() % constants::NLAYER); - return (offset + padLocal * constants::GRANULARITYTRKLPOS) * padWidth; -} - GPUdi() int Tracklet64::getSlopeBinSigned() const { int slopeBin = getSlope(); @@ -248,18 +258,11 @@ GPUdi() int Tracklet64::getSlopeBinSigned() const return -slope; } -GPUdi() float Tracklet64::getUncalibratedDy(float nTbDrift) const -{ - int slope = getSlopeBinSigned(); - float padWidth = 0.635f + 0.03f * (getDetector() % constants::NLAYER); - return slope * constants::GRANULARITYTRKLSLOPE * padWidth * nTbDrift / constants::ADDBITSHIFTSLOPE; -} - #ifndef GPUCA_GPUCODE_DEVICE std::ostream& operator<<(std::ostream& stream, const Tracklet64& trg); #endif // GPUCA_GPUCODE_DEVICE -} //namespace trd -} //namespace o2 +} // namespace trd +} // namespace o2 #endif diff --git a/DataFormats/Detectors/TRD/src/AngularResidHistos.cxx b/DataFormats/Detectors/TRD/src/AngularResidHistos.cxx index 5253fdfa9b359..dfee36f944723 100644 --- a/DataFormats/Detectors/TRD/src/AngularResidHistos.cxx +++ b/DataFormats/Detectors/TRD/src/AngularResidHistos.cxx @@ -52,12 +52,6 @@ void AngularResidHistos::fill(const AngularResidHistos& input) } } -void AngularResidHistos::fill(const gsl::span input) -{ - (void)input; - LOG(fatal) << "This function must not be called. But it must be available for the compilation to work"; -} - void AngularResidHistos::merge(const AngularResidHistos* prev) { for (int i = 0; i < MAXCHAMBER * NBINSANGLEDIFF; ++i) { diff --git a/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h b/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h index 35a8ed26a4b30..c6d36a7aee495 100644 --- a/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h +++ b/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h @@ -32,13 +32,20 @@ #pragma link C++ class o2::trd::KrClusterTriggerRecord + ; #pragma link C++ class o2::trd::NoiseStatusMCM + ; #pragma link C++ class o2::trd::AngularResidHistos + ; +#pragma link C++ class o2::trd::GainCalibHistos + ; +#pragma link C++ class o2::trd::T0FitHistos + ; #pragma link C++ class o2::trd::CalVdriftExB + ; +#pragma link C++ class o2::trd::CalGain + ; #pragma link C++ class o2::trd::CalT0 + ; #pragma link C++ class o2::trd::CompressedDigit + ; #pragma link C++ class o2::trd::HelperMethods + ; #pragma link C++ class o2::trd::LinkToHCIDMapping + ; #pragma link C++ class o2::trd::ChannelInfo + ; #pragma link C++ class o2::trd::ChannelInfoContainer + ; +#pragma link C++ struct o2::trd::PHData + ; +#pragma link C++ struct o2::trd::PHDataHD + ; +#pragma link C++ class o2::trd::TRDDataCountersPerTimeFrame + ; +#pragma link C++ class o2::trd::DataCountersPerTrigger + ; #pragma link C++ class std::vector < o2::trd::Tracklet64> + ; #pragma link C++ class std::vector < o2::trd::CalibratedTracklet> + ; #pragma link C++ class std::vector < o2::trd::TrackTriggerRecord> + ; @@ -47,8 +54,13 @@ #pragma link C++ class std::vector < o2::trd::Hit > +; #pragma link C++ class std::vector < o2::trd::Digit> + ; #pragma link C++ class std::vector < o2::trd::AngularResidHistos> + ; +#pragma link C++ class std::vector < o2::trd::GainCalibHistos> + ; +#pragma link C++ class std::vector < o2::trd::T0FitHistos> + ; +#pragma link C++ class std::vector < o2::trd::PHData> + ; +#pragma link C++ class std::vector < o2::trd::PHDataHD> + ; #pragma link C++ class std::vector < o2::trd::KrCluster> + ; #pragma link C++ class std::vector < o2::trd::KrClusterTriggerRecord> + ; +#pragma link C++ class std::vector < o2::trd::DataCountersPerTrigger> + ; #pragma link C++ struct o2::trd::CTFHeader + ; #pragma link C++ struct o2::trd::CTF + ; diff --git a/DataFormats/Detectors/TRD/src/Digit.cxx b/DataFormats/Detectors/TRD/src/Digit.cxx index d8a7e7613d7e8..37d6638ac0996 100644 --- a/DataFormats/Detectors/TRD/src/Digit.cxx +++ b/DataFormats/Detectors/TRD/src/Digit.cxx @@ -12,6 +12,7 @@ #include "DataFormatsTRD/Digit.h" #include #include +#include "fairlogger/Logger.h" namespace o2::trd { @@ -46,12 +47,18 @@ Digit::Digit(int det, int rob, int mcm, int channel, ArrayADC adc, int pretrigph setPreTrigPhase(pretrigphase); } -Digit::Digit(int det, int rob, int mcm, int channel) // add adc data in a seperate step +Digit::Digit(int det, int rob, int mcm, int channel, int pretrigphase) // add adc data in a seperate step { setDetector(det); setROB(rob); setMCM(mcm); setChannel(channel); + setPreTrigPhase(pretrigphase); +} + +void Digit::setPreTrigPhase(int phase) +{ + mDetector = ((((phase) & 0x3) << 12) | (mDetector & 0xfff)); } bool Digit::isSharedDigit() const @@ -63,6 +70,11 @@ bool Digit::isSharedDigit() const } } +bool Digit::isNeighbour(const Digit& other) const +{ + return (getDetector() == other.getDetector() && getROB() == other.getROB() && getMCM() == other.getMCM() && std::abs(getChannel() - other.getChannel()) == 1); +} + ADC_t Digit::getADCmax(int& idx) const { auto itMax = std::max_element(mADC.begin(), mADC.end()); diff --git a/DataFormats/Detectors/TRD/src/GainCalibHistos.cxx b/DataFormats/Detectors/TRD/src/GainCalibHistos.cxx new file mode 100644 index 0000000000000..5580c03d40c5c --- /dev/null +++ b/DataFormats/Detectors/TRD/src/GainCalibHistos.cxx @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GainCalibHistos.cxx +/// \brief Class to store the output of the global tracking based TRD calibration + +#include "DataFormatsTRD/GainCalibHistos.h" +#include +#include + +using namespace o2::trd; +using namespace o2::trd::constants; + +void GainCalibHistos::init() +{ + mdEdxEntries.resize(constants::MAXCHAMBER * constants::NBINSGAINCALIB, 0); + mInitialized = true; +} + +void GainCalibHistos::reset() +{ + if (!mInitialized) { + init(); + } + std::fill(mdEdxEntries.begin(), mdEdxEntries.end(), 0); + mNEntriesTot = 0; +} + +void GainCalibHistos::fill(const std::vector& input) +{ + if (!mInitialized) { + init(); + } + for (auto elem : input) { + ++mdEdxEntries[elem]; + } + mNEntriesTot += input.size(); +} + +void GainCalibHistos::merge(const GainCalibHistos* prev) +{ + if (!mInitialized) { + init(); + } + for (int i = 0; i < MAXCHAMBER * NBINSGAINCALIB; ++i) { + mdEdxEntries[i] += prev->getHistogramEntry(i); + mNEntriesTot += prev->getHistogramEntry(i); + } +} + +void GainCalibHistos::print() +{ + LOG(info) << "There are " << mNEntriesTot << " entries in the container"; +} diff --git a/DataFormats/Detectors/TRD/src/RawData.cxx b/DataFormats/Detectors/TRD/src/RawData.cxx index 8447f1e3c695d..8453cd55e4dc4 100644 --- a/DataFormats/Detectors/TRD/src/RawData.cxx +++ b/DataFormats/Detectors/TRD/src/RawData.cxx @@ -112,23 +112,23 @@ void setHalfCRUHeaderLinkSizeAndFlags(HalfCRUHeader& cruhead, int link, int size cruhead.errorflags[link].errorflag = errors; } -uint32_t getHalfCRULinkErrorFlag(const HalfCRUHeader& cruhead, const uint32_t link) +uint8_t getHalfCRULinkErrorFlag(const HalfCRUHeader& cruhead, const uint32_t link) { // link is the link you are requesting information on, 0-14 - uint32_t errorflag = 0; + uint8_t errorflag = 0; //dealing with word0-2 errorflag = cruhead.errorflags[link].errorflag; return errorflag; } -uint32_t getHalfCRULinkDataSize(const HalfCRUHeader& cruhead, const uint32_t link) +uint16_t getHalfCRULinkDataSize(const HalfCRUHeader& cruhead, const uint32_t link) { // link is the link you are requesting information on, 0-14 //return number 32 byte blocks for the link 3x64bit ints. return cruhead.datasizes[link].size; } -void getHalfCRULinkErrorFlags(const HalfCRUHeader& cruheader, std::array& linkerrorflags) +void getHalfCRULinkErrorFlags(const HalfCRUHeader& cruheader, std::array& linkerrorflags) { // retrieve all the link error flags for this half cru for (uint32_t link = 0; link < 15; link++) { @@ -136,7 +136,7 @@ void getHalfCRULinkErrorFlags(const HalfCRUHeader& cruheader, std::array& linksizes) +void getHalfCRULinkDataSizes(const HalfCRUHeader& cruheader, std::array& linksizes) { // retrieve all the link error flags for this half cru for (uint32_t link = 0; link < 15; link++) { @@ -229,8 +229,8 @@ void printTrackletMCMHeader(const o2::trd::TrackletMCMHeader& mcmhead) void printHalfCRUHeader(const o2::trd::HalfCRUHeader& halfcru) { - std::array sizes; - std::array errorflags; + std::array sizes; + std::array errorflags; getHalfCRULinkDataSizes(halfcru, sizes); getHalfCRULinkErrorFlags(halfcru, errorflags); LOGF(info, "V:%d BC:%d SB:%d EType:%d", halfcru.HeaderVersion, halfcru.BunchCrossing, diff --git a/DataFormats/Detectors/TRD/src/T0FitHistos.cxx b/DataFormats/Detectors/TRD/src/T0FitHistos.cxx new file mode 100644 index 0000000000000..623f61b761188 --- /dev/null +++ b/DataFormats/Detectors/TRD/src/T0FitHistos.cxx @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file T0FitHistos.cxx +/// \brief Class to store the TRD PH values for each chamber + +#include "DataFormatsTRD/T0FitHistos.h" +#include +#include + +using namespace o2::trd; +using namespace o2::trd::constants; + +void T0FitHistos::fill(const std::vector& data) +{ + for (const auto& ph : data) { + int det = ph.getDetector(); + int tb = ph.getTimebin(); + int adc = ph.getADC(); + + if (ph.getNneighbours() != 2) { + continue; + } + + mDet.push_back(det); + mTB.push_back(tb); + mADC.push_back(adc); + ++mNEntriesTot; + } +} + +void T0FitHistos::merge(const T0FitHistos* prev) +{ + auto sizePrev = (int)prev->getNEntries(); + + for (int i = 0; i < sizePrev; ++i) { + mDet.push_back(prev->getDetector(i)); + mTB.push_back(prev->getTimeBin(i)); + mADC.push_back(prev->getADC(i)); + } +} + +void T0FitHistos::print() +{ + LOG(info) << "There are " << mNEntriesTot << " entries in the container"; +} diff --git a/DataFormats/Detectors/TRD/src/Tracklet64.cxx b/DataFormats/Detectors/TRD/src/Tracklet64.cxx index 35879fcec09e3..0458dad92e678 100644 --- a/DataFormats/Detectors/TRD/src/Tracklet64.cxx +++ b/DataFormats/Detectors/TRD/src/Tracklet64.cxx @@ -10,40 +10,21 @@ // or submit itself to any jurisdiction. #include "DataFormatsTRD/Tracklet64.h" -#include "DataFormatsTRD/Constants.h" #include "DataFormatsTRD/HelperMethods.h" -#include "GPUCommonMath.h" - #include "fairlogger/Logger.h" #include -using namespace GPUCA_NAMESPACE::gpu; - namespace o2 { - namespace trd { -using namespace constants; - void Tracklet64::print() const { - LOGF(info, "%02i_%i_%i, row(%i), col(%i), position(%i), slope(%i), pid(%i), q0(%i), q1(%i), q2(%i). Format(%i)", - HelperMethods::getSector(getDetector()), HelperMethods::getStack(getDetector()), HelperMethods::getLayer(getDetector()), getPadRow(), getColumn(), getPosition(), getSlope(), getPID(), getQ0(), getQ1(), getQ2(), getFormat()); -} - -GPUd() int Tracklet64::getPadCol() const -{ - // obtain pad number relative to MCM center - int padLocal = getPositionBinSigned() * GRANULARITYTRKLPOS; - // MCM number in column direction (0..7) - int mcmCol = (getMCM() % NMCMROBINCOL) + NMCMROBINCOL * (getROB() % 2); - // FIXME: understand why the offset seems to be 6 pads and not nChannels / 2 = 10.5 - return CAMath::Nint(6.f + mcmCol * ((float)NCOLMCM) + padLocal); + LOGF(info, "%02i_%i_%i, ROB(%i), MCM(%i), row(%i), col(%i), position(%i), slope(%i), pid(%i), q0(%i), q1(%i), q2(%i). Format(%i)", + HelperMethods::getSector(getDetector()), HelperMethods::getStack(getDetector()), HelperMethods::getLayer(getDetector()), getROB(), getMCM(), getPadRow(), getPadCol(), getPosition(), getSlope(), getPID(), getQ0(), getQ1(), getQ2(), getFormat()); } -#ifndef GPUCA_GPUCODE_DEVICE void Tracklet64::printStream(std::ostream& stream) const { stream << "Tracklet64 : 0x" << std::hex << getTrackletWord(); @@ -58,7 +39,15 @@ std::ostream& operator<<(std::ostream& stream, const Tracklet64& trg) trg.printStream(stream); return stream; } -#endif // GPUCA_GPUCODE_DEVICE + +bool operator<(const Tracklet64& lhs, const Tracklet64& rhs) +{ + return (lhs.getDetector() < rhs.getDetector()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() < rhs.getROB()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() == rhs.getROB() && lhs.getMCM() < rhs.getMCM()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() == rhs.getROB() && lhs.getMCM() == rhs.getMCM() && lhs.getPadRow() < rhs.getPadRow()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() == rhs.getROB() && lhs.getMCM() == rhs.getMCM() && lhs.getPadRow() == rhs.getPadRow() && lhs.getPadCol() < rhs.getPadCol()); +} } // namespace trd } // namespace o2 diff --git a/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt new file mode 100644 index 0000000000000..3914b8c7ede8d --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright 2019-2025 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +add_subdirectory(FD3) +add_subdirectory(TRKFT3) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt new file mode 100644 index 0000000000000..e2219bb893612 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(DataFormatsFD3 + SOURCES src/Hit.cxx + PUBLIC_LINK_LIBRARIES O2::FD3Base + O2::SimulationDataFormat + O2::CommonDataFormat + Microsoft.GSL::GSL + O2::DetectorsCommonDataFormats +) + +o2_target_root_dictionary(DataFormatsFD3 + HEADERS include/DataFormatsFD3/Hit.h +) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/include/DataFormatsFD3/Hit.h b/DataFormats/Detectors/Upgrades/ALICE3/FD3/include/DataFormatsFD3/Hit.h new file mode 100644 index 0000000000000..4fde2f6cde6b4 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/include/DataFormatsFD3/Hit.h @@ -0,0 +1,125 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Hit.h +/// \brief Definition of the FD3 Hit class (based on ITSMFT and FV0) + +#ifndef ALICEO2_FVD_HIT_H_ +#define ALICEO2_FVD_HIT_H_ + +#include +#include "SimulationDataFormat/BaseHits.h" // for BasicXYZEHit +#include "Rtypes.h" // for Bool_t, Double_t, int, Double32_t, etc +#include "TVector3.h" // for TVector3 +#include "CommonUtils/ShmAllocator.h" + +namespace o2 +{ +namespace fd3 +{ + +class Hit : public o2::BasicXYZEHit +{ + public: + /// Default constructor + Hit() = default; + + /// Class Constructor + /// \param trackID Index of MCTrack + /// \param cellID Cell ID + /// \param startPos Coordinates at entrance to active volume [cm] + /// \param endPos Coordinates to active volume [cm] + /// \param startMom Momentum of track at entrance [GeV] + /// \param startE Energy of track at entrance [GeV] + /// \param endTime Final time [ns] + /// \param eLoss Energy deposit [GeV] + /// \param particlePdg PDG code of the partcile associated with the track + inline Hit(int trackID, + int cellID, + const math_utils::Point3D& startPos, + const math_utils::Point3D& endPos, + const math_utils::Vector3D& startMom, + double startE, + double endTime, + double eLoss, + int particlePdg); + + // Entrance position getters + math_utils::Point3D const& GetPosStart() const { return mPositionStart; } + float GetStartX() const { return mPositionStart.X(); } + float GetStartY() const { return mPositionStart.Y(); } + float GetStartZ() const { return mPositionStart.Z(); } + template + void GetStartPosition(F& x, F& y, F& z) const + { + x = GetStartX(); + y = GetStartY(); + z = GetStartZ(); + } + + // Momentum getters + math_utils::Vector3D const& GetMomentum() const { return mMomentumStart; } + math_utils::Vector3D& GetMomentum() { return mMomentumStart; } + float GetPx() const { return mMomentumStart.X(); } + float GetPy() const { return mMomentumStart.Y(); } + float GetPz() const { return mMomentumStart.Z(); } + float GetE() const { return mEnergyStart; } + float GetTotalEnergyAtEntrance() const { return GetE(); } + int GetParticlePdg() const { return mParticlePdg; } + + void Print(const Option_t* opt) const; + + private: + math_utils::Vector3D mMomentumStart; ///< momentum at entrance + math_utils::Point3D mPositionStart; ///< position at entrance (base mPos give position on exit) + float mEnergyStart; ///< total energy at entrance + int mParticlePdg; ///< PDG code of the particle associated with this track + + ClassDefNV(Hit, 1); +}; + +Hit::Hit(int trackID, + int detID, + const math_utils::Point3D& startPos, + const math_utils::Point3D& endPos, + const math_utils::Vector3D& startMom, + double startE, + double endTime, + double eLoss, + int particlePdg) + : BasicXYZEHit(endPos.X(), + endPos.Y(), + endPos.Z(), + endTime, + eLoss, + trackID, + detID), + mMomentumStart(startMom.X(), startMom.Y(), startMom.Z()), + mPositionStart(startPos.X(), startPos.Y(), startPos.Z()), + mEnergyStart(startE), + mParticlePdg(particlePdg) +{ +} + +} // namespace fd3 +} // namespace o2 + +#ifdef USESHM +namespace std +{ +template <> +class allocator : public o2::utils::ShmAllocator +{ +}; + +} // namespace std +#endif /* USESHM */ +#endif /* ALICEO2_FD3_HIT_H_ */ diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h new file mode 100644 index 0000000000000..1014b3d8c704e --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::fd3::Hit + ; +#pragma link C++ class vector < o2::fd3::Hit> + ; + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx new file mode 100644 index 0000000000000..403a3402bd30c --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx @@ -0,0 +1,35 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Hit.cxx +/// \brief Implementation of the Hit class + +#include "DataFormatsFD3/Hit.h" +#include + +ClassImp(o2::fd3::Hit); + +namespace o2 +{ +namespace fd3 +{ + +void Hit::Print(const Option_t* opt) const +{ + printf( + "Det: %5d Track: %6d E.loss: %.3e P: %+.3e %+.3e %+.3e\n" + "PosIn: %+.3e %+.3e %+.3e PosOut: %+.3e %+.3e %+.3e\n", + GetDetectorID(), GetTrackID(), GetEnergyLoss(), GetPx(), GetPy(), GetPz(), + GetStartX(), GetStartY(), GetStartZ(), GetX(), GetY(), GetZ()); +} + +} // namespace fd3 +} // namespace o2 diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/CMakeLists.txt new file mode 100644 index 0000000000000..fd6e02c44a6b6 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright 2019-2026 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +add_subdirectory(common) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/CMakeLists.txt new file mode 100644 index 0000000000000..d2a8b73da3455 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright 2019-2026 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(DataFormatsTRKFT3 + SOURCES src/Digit.cxx + src/Hit.cxx + src/ROFRecord.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + O2::SimulationDataFormat) + +o2_target_root_dictionary(DataFormatsTRKFT3 + HEADERS include/DataFormatsTRKFT3/Cluster.h + include/DataFormatsTRKFT3/Digit.h + include/DataFormatsTRKFT3/Hit.h + include/DataFormatsTRKFT3/ROFRecord.h + LINKDEF src/DataFormatsTRKFT3LinkDef.h) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/Cluster.h b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/Cluster.h new file mode 100644 index 0000000000000..c3517f289d505 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/Cluster.h @@ -0,0 +1,52 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_DATAFORMATSTRKFT3_CLUSTER_H +#define ALICEO2_DATAFORMATSTRKFT3_CLUSTER_H + +#include "DetectorsCommonDataFormats/DetID.h" +#include +#include +#include +#include + +namespace o2::trkft3 +{ + +template +struct Cluster { + static_assert(DetID == o2::detectors::DetID::TRK || DetID == o2::detectors::DetID::FT3, "only TRK and FT3 clusters are supported"); + + uint16_t chipID = 0; + uint16_t row = 0; + uint16_t col = 0; + uint16_t size = 1; + int16_t subDetID = -1; + int16_t layer = -1; + + std::string asString() const + { + std::ostringstream stream; + stream << o2::detectors::DetID(DetID).getName() << " cluster chip=" << chipID + << " row=" << row << " col=" << col << " size=" << size + << " subDet=" << subDetID << " layer=" << layer; + return stream.str(); + } + + ClassDefNV(Cluster, 1); +}; + +using TRKCluster = Cluster; +using FT3Cluster = Cluster; + +} // namespace o2::trkft3 + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/Digit.h b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/Digit.h new file mode 100644 index 0000000000000..41aa774a2ea06 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/Digit.h @@ -0,0 +1,60 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_DATAFORMATSTRKFT3_DIGIT_H +#define ALICEO2_DATAFORMATSTRKFT3_DIGIT_H + +#include "Rtypes.h" +#include +#include + +namespace o2::trkft3 +{ + +class Digit +{ + public: + Digit(UShort_t chipindex = 0, UShort_t row = 0, UShort_t col = 0, Int_t charge = 0); + ~Digit() = default; + + UShort_t getChipIndex() const { return mChipIndex; } + UShort_t getColumn() const { return mCol; } + UShort_t getRow() const { return mRow; } + Int_t getCharge() const { return mCharge; } + + void setChipIndex(UShort_t index) { mChipIndex = index; } + void setPixelIndex(UShort_t row, UShort_t col) + { + mRow = row; + mCol = col; + } + void setCharge(Int_t charge) { mCharge = charge < USHRT_MAX ? charge : USHRT_MAX; } + void addCharge(int charge) { setCharge(charge + int(mCharge)); } + + std::ostream& print(std::ostream& output) const; + friend std::ostream& operator<<(std::ostream& output, const Digit& digi) + { + digi.print(output); + return output; + } + + private: + UShort_t mChipIndex = 0; + UShort_t mRow = 0; + UShort_t mCol = 0; + UShort_t mCharge = 0; + + ClassDefNV(Digit, 1); +}; + +} // namespace o2::trkft3 + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/Hit.h b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/Hit.h new file mode 100644 index 0000000000000..bcc51828436f7 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/Hit.h @@ -0,0 +1,111 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_DATAFORMATSTRKFT3_HIT_H +#define ALICEO2_DATAFORMATSTRKFT3_HIT_H + +#include +#include + +#include "CommonUtils/ShmAllocator.h" +#include "SimulationDataFormat/BaseHits.h" +#include "Rtypes.h" +#include "TVector3.h" + +namespace o2::trkft3 +{ + +class Hit : public o2::BasicXYZEHit +{ + public: + enum HitStatus_t { + kTrackEntering = 0x1, + kTrackInside = 0x1 << 1, + kTrackExiting = 0x1 << 2, + kTrackOut = 0x1 << 3, + kTrackStopped = 0x1 << 4, + kTrackAlive = 0x1 << 5 + }; + + Hit() = default; + + Hit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& endPos, const TVector3& startMom, + double startE, double endTime, double eLoss, unsigned char startStatus, unsigned char endStatus); + + math_utils::Point3D GetPosStart() const { return mPosStart; } + Float_t GetStartX() const { return mPosStart.X(); } + Float_t GetStartY() const { return mPosStart.Y(); } + Float_t GetStartZ() const { return mPosStart.Z(); } + template + void GetStartPosition(F& x, F& y, F& z) const + { + x = GetStartX(); + y = GetStartY(); + z = GetStartZ(); + } + + math_utils::Vector3D GetMomentum() const { return mMomentum; } + math_utils::Vector3D& GetMomentum() { return mMomentum; } + Float_t GetPx() const { return mMomentum.X(); } + Float_t GetPy() const { return mMomentum.Y(); } + Float_t GetPz() const { return mMomentum.Z(); } + Float_t GetE() const { return mE; } + Float_t GetTotalEnergy() const { return GetE(); } + + UChar_t GetStatusEnd() const { return mTrackStatusEnd; } + UChar_t GetStatusStart() const { return mTrackStatusStart; } + + Bool_t IsEntering() const { return mTrackStatusEnd & kTrackEntering; } + Bool_t IsInside() const { return mTrackStatusEnd & kTrackInside; } + Bool_t IsExiting() const { return mTrackStatusEnd & kTrackExiting; } + Bool_t IsOut() const { return mTrackStatusEnd & kTrackOut; } + Bool_t IsStopped() const { return mTrackStatusEnd & kTrackStopped; } + Bool_t IsAlive() const { return mTrackStatusEnd & kTrackAlive; } + + Bool_t IsEnteringStart() const { return mTrackStatusStart & kTrackEntering; } + Bool_t IsInsideStart() const { return mTrackStatusStart & kTrackInside; } + Bool_t IsExitingStart() const { return mTrackStatusStart & kTrackExiting; } + Bool_t IsOutStart() const { return mTrackStatusStart & kTrackOut; } + Bool_t IsStoppedStart() const { return mTrackStatusStart & kTrackStopped; } + Bool_t IsAliveStart() const { return mTrackStatusStart & kTrackAlive; } + + void SetPosStart(const math_utils::Point3D& p) { mPosStart = p; } + + void Print(const Option_t* opt) const; + friend std::ostream& operator<<(std::ostream& of, const Hit& point) + { + of << "-I- Hit: O2 trkft3 point for track " << point.GetTrackID() << " in detector " << point.GetDetectorID() << std::endl; + return of; + } + + private: + math_utils::Vector3D mMomentum; ///< momentum at entrance + math_utils::Point3D mPosStart; ///< position at entrance, base position is at exit + Float_t mE = 0.f; ///< total energy at entrance + UChar_t mTrackStatusEnd = 0; ///< MC status flag at exit + UChar_t mTrackStatusStart = 0; ///< MC status at starting point + + ClassDefNV(Hit, 3); +}; + +} // namespace o2::trkft3 + +#ifdef USESHM +namespace std +{ +template <> +class allocator : public o2::utils::ShmAllocator +{ +}; +} // namespace std +#endif + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/ROFRecord.h b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/ROFRecord.h new file mode 100644 index 0000000000000..633ad7e4af24d --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/include/DataFormatsTRKFT3/ROFRecord.h @@ -0,0 +1,61 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_DATAFORMATSTRKFT3_ROFRECORD_H +#define ALICEO2_DATAFORMATSTRKFT3_ROFRECORD_H + +#include "CommonDataFormat/InteractionRecord.h" +#include "CommonDataFormat/RangeReference.h" +#include +#include +#include + +namespace o2::trkft3 +{ + +class ROFRecord +{ + public: + using EvIdx = o2::dataformats::RangeReference; + using BCData = o2::InteractionRecord; + using ROFtype = unsigned int; + + ROFRecord() = default; + ROFRecord(const BCData& bc, ROFtype rof, int idx, int n) + : mBCData(bc), mROFEntry(idx, n), mROFrame(rof) {} + + void setBCData(const BCData& bc) { mBCData = bc; } + void setROFrame(ROFtype rof) { mROFrame = rof; } + void setEntry(EvIdx entry) { mROFEntry = entry; } + void setFirstEntry(int idx) { mROFEntry.setFirstEntry(idx); } + void setNEntries(int n) { mROFEntry.setEntries(n); } + + const BCData& getBCData() const { return mBCData; } + BCData& getBCData() { return mBCData; } + EvIdx getEntry() const { return mROFEntry; } + EvIdx& getEntry() { return mROFEntry; } + int getNEntries() const { return mROFEntry.getEntries(); } + int getFirstEntry() const { return mROFEntry.getFirstEntry(); } + ROFtype getROFrame() const { return mROFrame; } + + std::string asString() const; + + private: + o2::InteractionRecord mBCData; + EvIdx mROFEntry; + ROFtype mROFrame = 0; + + ClassDefNV(ROFRecord, 1); +}; + +} // namespace o2::trkft3 + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/DataFormatsTRKFT3LinkDef.h b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/DataFormatsTRKFT3LinkDef.h new file mode 100644 index 0000000000000..046f680e42e8a --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/DataFormatsTRKFT3LinkDef.h @@ -0,0 +1,29 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::trkft3::Digit + ; +#pragma link C++ class std::vector < o2::trkft3::Digit> + ; +#pragma link C++ class o2::trkft3::Hit + ; +#pragma link C++ class std::vector < o2::trkft3::Hit> + ; +#pragma link C++ class o2::trkft3::Cluster < o2::detectors::DetID::TRK> + ; +#pragma link C++ class std::vector < o2::trkft3::Cluster < o2::detectors::DetID::TRK>> + ; +#pragma link C++ class o2::trkft3::Cluster < o2::detectors::DetID::FT3> + ; +#pragma link C++ class std::vector < o2::trkft3::Cluster < o2::detectors::DetID::FT3>> + ; +#pragma link C++ class o2::trkft3::ROFRecord + ; +#pragma link C++ class std::vector < o2::trkft3::ROFRecord> + ; + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/Digit.cxx b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/Digit.cxx new file mode 100644 index 0000000000000..f01d98d1e005d --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/Digit.cxx @@ -0,0 +1,29 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsTRKFT3/Digit.h" +#include + +ClassImp(o2::trkft3::Digit); + +using namespace o2::trkft3; + +Digit::Digit(UShort_t chipindex, UShort_t row, UShort_t col, Int_t charge) + : mChipIndex(chipindex), mRow(row), mCol(col) +{ + setCharge(charge); +} + +std::ostream& Digit::print(std::ostream& output) const +{ + output << "TRKFT3Digit chip [" << mChipIndex << "] R:" << mRow << " C:" << mCol << " Q: " << mCharge; + return output; +} diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/Hit.cxx b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/Hit.cxx new file mode 100644 index 0000000000000..ff99214cbd994 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/Hit.cxx @@ -0,0 +1,41 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsTRKFT3/Hit.h" + +#include + +ClassImp(o2::trkft3::Hit); + +namespace o2::trkft3 +{ + +Hit::Hit(int trackID, unsigned short detID, const TVector3& startPos, const TVector3& endPos, const TVector3& startMom, + double startE, double endTime, double eLoss, unsigned char startStatus, unsigned char endStatus) + : BasicXYZEHit(endPos.X(), endPos.Y(), endPos.Z(), endTime, eLoss, trackID, detID), + mMomentum(startMom.Px(), startMom.Py(), startMom.Pz()), + mPosStart(startPos.X(), startPos.Y(), startPos.Z()), + mE(startE), + mTrackStatusEnd(endStatus), + mTrackStatusStart(startStatus) +{ +} + +void Hit::Print(const Option_t* opt) const +{ + printf( + "Det: %5d Track: %6d E.loss: %.3e P: %+.3e %+.3e %+.3e\n" + "PosIn: %+.3e %+.3e %+.3e PosOut: %+.3e %+.3e %+.3e\n", + GetDetectorID(), GetTrackID(), GetEnergyLoss(), GetPx(), GetPy(), GetPz(), + GetStartX(), GetStartY(), GetStartZ(), GetX(), GetY(), GetZ()); +} + +} // namespace o2::trkft3 diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/ROFRecord.cxx b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/ROFRecord.cxx new file mode 100644 index 0000000000000..9b2808653cf99 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRKFT3/common/src/ROFRecord.cxx @@ -0,0 +1,28 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsTRKFT3/ROFRecord.h" +#include + +ClassImp(o2::trkft3::ROFRecord); + +namespace o2::trkft3 +{ + +std::string ROFRecord::asString() const +{ + std::ostringstream stream; + stream << "IR=" << mBCData.asString() << " ROFrame=" << mROFrame + << " first=" << mROFEntry.getFirstEntry() << " n=" << mROFEntry.getEntries(); + return stream.str(); +} + +} // namespace o2::trkft3 diff --git a/DataFormats/Detectors/Upgrades/CMakeLists.txt b/DataFormats/Detectors/Upgrades/CMakeLists.txt index a2d470b8ff6d5..0dfe07dc2827d 100644 --- a/DataFormats/Detectors/Upgrades/CMakeLists.txt +++ b/DataFormats/Detectors/Upgrades/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# Copyright 2019-2025 CERN and copyright holders of ALICE O2. # See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. # All rights not expressly granted are reserved. # @@ -10,3 +10,4 @@ # or submit itself to any jurisdiction. message(STATUS "Building dataformats for upgrades") +add_subdirectory(ALICE3) diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h index da1ca9d1d0292..916181030c583 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h @@ -55,8 +55,9 @@ struct BCData { o2::dataformats::RangeRefComp<6> ref; o2::InteractionRecord ir; std::array moduleTriggers{}; + // N.B. channels and triggers have geographical addressing (0x1 << (NChPerModule * im + ic) uint32_t channels = 0; // pattern of channels it refers to - uint32_t triggers = 0; // pattern of triggered channels (not necessarily stored) in this BC + uint32_t triggers = 0; // pattern of triggered channels (not necessarily stored) in this BC (i.e. with Hit bit on) uint8_t ext_triggers = 0; // pattern of ALICE triggers BCData() = default; diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/FEEConfig.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/FEEConfig.h deleted file mode 100644 index b084507b84519..0000000000000 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/FEEConfig.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// DataFormats/Detectors/ZDC/include/DataFormatsZDC/RawEventData.h - -#include "ZDCBase/Constants.h" - -#ifndef ALICEO2_ZDC_FEECONFIG_H -#define ALICEO2_ZDC_FEECONFIG_H - -/// \file FEEConfig.h -/// \brief ZDC FEE configuration -/// \author pietro.cortese@cern.ch - -namespace o2 -{ -namespace zdc -{ - -struct FEEFillingMap { - uint64_t filling[56]; -}; - -struct FEEConfigMap { - uint32_t address[5 * NChPerModule + 3] = {0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 12, 13, 14, 15, - 16, 17, 18, 19, - 76, 77, 78}; - uint64_t delay_sample[NChPerModule] = {6, 6, 6, 6}; // 4 bits - uint64_t delay_coarse[NChPerModule] = {200, 200, 200, 200}; // 8 bits - uint64_t threshold_level[NChPerModule] = {10, 10, 10, 10}; // 12 bits - uint64_t difference_delta[NChPerModule] = {4, 4, 4, 4}; // 3 bits - uint64_t masking_difference[NChPerModule] = {0x00ff00, 0x00ff00, 0x00ff00, 0x00ff00}; // 24 bits - uint64_t masking_alicet = 0x00000010; // 32 bits - uint64_t masking_autot = 0xf; // 4 bits - uint64_t masking_readout = 0xf; // 4 bits -}; - -} // namespace zdc -} // namespace o2 - -#endif diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/Hit.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/Hit.h index 6e0b99dca6761..3ce90a95f7248 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/Hit.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/Hit.h @@ -58,6 +58,7 @@ class Hit : public o2::BasicXYZEHit float getPMCLightYield() const { return mNphePMC; } float getPMQLightYield() const { return mNphePMQ; } int getNumContributingSteps() const { return mNoContributingSteps; } + bool getSecFlag() const { return mSecFlag; } private: Int_t mParentID; diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RawEventData.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RawEventData.h index 5d856f55c1d65..622f3116b7868 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RawEventData.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RawEventData.h @@ -33,7 +33,9 @@ constexpr unsigned short Id_w0 = 0x0; constexpr unsigned short Id_w1 = 0x1; constexpr unsigned short Id_w2 = 0x2; constexpr unsigned short Id_wn = 0x3; -constexpr int NWPerGBTW = 4; +constexpr int NWPerGBTW = 4; // 4*32bit=128 bit per GBTW +constexpr int NBPerGBTW = 4 * NWPerGBTW; // 16B=128 bit per GBTW +constexpr int PayloadPerGBTW = 10; // 80 bit per GBTW struct __attribute__((__packed__)) ChannelDataV0 { // First GBT word diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEventFlat.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEventFlat.h index 744d36d62e2dc..ea5fd10f92dcc 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEventFlat.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEventFlat.h @@ -37,6 +37,21 @@ namespace zdc using FirstEntry = int; using NElem = int; +struct RecEventScale { + static std::array fe; + static std::array fa; + static void reset(); + static void setGeV(); + static void setGeVZN(); + static void setGeVZP(); + static void setTeV(); + static void setTeVZN(); + static void setTeVZP(); + static void setNucleonEnergy(float energy); + static void setNucleonEnergyZN(float energy); + static void setNucleonEnergyZP(float energy); +}; + struct RecEventFlat { // NOLINT: false positive in clang-tidy !! o2::InteractionRecord ir; uint32_t channels = 0; /// pattern of channels acquired @@ -172,7 +187,7 @@ struct RecEventFlat { // NOLINT: false positive in clang-tidy !! { if (ich < NTDCChannels) { if (ipos < TDCAmp[ich].size()) { - return FTDCAmp * TDCAmp[ich][ipos]; + return TDCAmp[ich][ipos]; } } return -std::numeric_limits::infinity(); @@ -244,12 +259,16 @@ struct RecEventFlat { // NOLINT: false positive in clang-tidy !! // Unit: cm void centroidZNA(float& x, float& y); void centroidZNC(float& x, float& y); + void centroidZPA(float& x, float& rms); + void centroidZPC(float& x, float& rms); float xZNA(); float yZNA(); float xZNC(); float yZNC(); float xZPA(); // Positive float xZPC(); // Negative + float rmsZPA(); + float rmsZPC(); void decodeInfo(uint8_t ch, uint16_t code); void decodeMapInfo(uint32_t ch, uint16_t code); diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/ZDCTDCData.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/ZDCTDCData.h index 4e3d0bc9f9d4f..d4d6bcdf75be0 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/ZDCTDCData.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/ZDCTDCData.h @@ -30,7 +30,6 @@ namespace zdc struct ZDCTDCDataErr { static uint32_t mErrVal[NTDCChannels]; // Errors in encoding TDC values - static uint32_t mErrAmp[NTDCChannels]; // Errors in encoding TDC amplitudes static uint32_t mErrId; // Errors with TDC Id static void print() @@ -43,11 +42,6 @@ struct ZDCTDCDataErr { LOG(error) << "TDCVal itdc=" << itdc << " " << ChannelNames[TDCSignal[itdc]] << " was out of range #times = " << mErrVal[itdc]; } } - for (int itdc = 0; itdc < NTDCChannels; itdc++) { - if (mErrAmp[itdc] > 0) { - LOG(warning) << "TDCAmp itdc=" << itdc << " " << ChannelNames[TDCSignal[itdc]] << " was out of range #times = " << mErrAmp[itdc]; - } - } } }; @@ -55,10 +49,11 @@ struct ZDCTDCData { uint8_t id = 0xff; // channel ID int16_t val = 0; // tdc value - int16_t amp = 0; // tdc amplitude + float amp = 0; // tdc amplitude ZDCTDCData() = default; - ZDCTDCData(uint8_t ida, int16_t vala, int16_t ampa, bool isbeg = false, bool isend = false) + + ZDCTDCData(uint8_t ida, int16_t vala, float ampa, bool isbeg = false, bool isend = false) { // TDC value and amplitude are encoded externally id = ida < NTDCChannels ? ida : 0xf; @@ -70,7 +65,7 @@ struct ZDCTDCData { amp = ampa; } else { val = kMaxShort; - amp = kMaxShort; + amp = FInfty; #ifdef O2_ZDC_DEBUG LOG(error) << __func__ << "TDC Id = " << int(ida) << " is out of range"; #endif @@ -87,7 +82,7 @@ struct ZDCTDCData { if (ida >= NTDCChannels) { val = kMaxShort; - amp = kMaxShort; + amp = FInfty; #ifdef O2_ZDC_DEBUG LOG(error) << __func__ << "TDC Id = " << int(ida) << " is out of range"; #endif @@ -96,7 +91,6 @@ struct ZDCTDCData { } auto TDCVal = std::nearbyint(vala); - auto TDCAmp = std::nearbyint(ampa); if (TDCVal < kMinShort) { int itdc = int(id); @@ -116,46 +110,31 @@ struct ZDCTDCData { TDCVal = kMaxShort; } - if (TDCAmp < kMinShort) { - int itdc = int(ida); -#ifdef O2_ZDC_DEBUG - LOG(warning) << __func__ << "TDCAmp itdc=" << itdc << " " << ChannelNames[TDCSignal[itdc]] << " = " << TDCAmp << " is out of range"; -#endif - ZDCTDCDataErr::mErrAmp[itdc]++; - TDCAmp = kMinShort; - } - - if (TDCAmp > kMaxShort) { - int itdc = int(ida); -#ifdef O2_ZDC_DEBUG - LOG(warning) << __func__ << "TDCAmp itdc=" << itdc << " " << ChannelNames[TDCSignal[itdc]] << " = " << TDCAmp << " is out of range"; -#endif - ZDCTDCDataErr::mErrAmp[itdc]++; - TDCAmp = kMaxShort; - } - val = TDCVal; - amp = TDCAmp; + amp = ampa; } inline float amplitude() const { - // Return decoded value - return FTDCAmp * amp; + return amp; } + inline float value() const { // Return decoded value (ns) return FTDCVal * val; } + inline int ch() const { return (id & 0x0f); } + inline bool isBeg() const { return id & 0x80 ? true : false; } + inline bool isEnd() const { return id & 0x40 ? true : false; @@ -163,7 +142,7 @@ struct ZDCTDCData { void print() const; - ClassDefNV(ZDCTDCData, 1); + ClassDefNV(ZDCTDCData, 2); }; } // namespace zdc } // namespace o2 diff --git a/DataFormats/Detectors/ZDC/src/DataFormatsZDCLinkDef.h b/DataFormats/Detectors/ZDC/src/DataFormatsZDCLinkDef.h index 6e272dbc8a118..0fbc7308f39e3 100644 --- a/DataFormats/Detectors/ZDC/src/DataFormatsZDCLinkDef.h +++ b/DataFormats/Detectors/ZDC/src/DataFormatsZDCLinkDef.h @@ -51,4 +51,6 @@ #pragma link C++ struct o2::zdc::CTF + ; #pragma link C++ class o2::ctf::EncodedBlocks < o2::zdc::CTFHeader, 12, uint32_t> + ; +#pragma read sourceClass = "ZDCTDCData" targetClass = "ZDCTDCData" source = "int16_t amp" version = "[-1]" target = "amp" targetType = "float" code = "{ amp=onfile.amp / 8.; }" + #endif diff --git a/DataFormats/Detectors/ZDC/src/RecEventFlat.cxx b/DataFormats/Detectors/ZDC/src/RecEventFlat.cxx index b5632285fd55f..cb2e298eee317 100644 --- a/DataFormats/Detectors/ZDC/src/RecEventFlat.cxx +++ b/DataFormats/Detectors/ZDC/src/RecEventFlat.cxx @@ -15,6 +15,133 @@ using namespace o2::zdc; +std::array RecEventScale::fe = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; +std::array RecEventScale::fa = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +void RecEventScale::reset() +{ + for (int ich = 0; ich < NChannels; ich++) { + fe[ich] = 1; + } + for (int itdc = 0; itdc < NTDCChannels; itdc++) { + fa[itdc] = 1; + } +} + +void RecEventScale::setGeV() +{ + for (int ich = 0; ich < NChannels; ich++) { + fe[ich] = 1; + } + for (int itdc = 0; itdc < NTDCChannels; itdc++) { + fa[itdc] = 1000.; + } +} + +void RecEventScale::setGeVZN() +{ + for (int ich = IdZNAC; ich <= IdZNASum; ich++) { + fe[ich] = 1; + } + for (int ich = IdZNCC; ich <= IdZNCSum; ich++) { + fe[ich] = 1; + } + fa[TDCZNAC] = 1000.; + fa[TDCZNAS] = 1000.; + fa[TDCZNCC] = 1000.; + fa[TDCZNCS] = 1000.; +} + +void RecEventScale::setGeVZP() +{ + for (int ich = IdZPAC; ich <= IdZPASum; ich++) { + fe[ich] = 1; + } + for (int ich = IdZPCC; ich <= IdZPCSum; ich++) { + fe[ich] = 1; + } + fa[TDCZPAC] = 1000.; + fa[TDCZPAS] = 1000.; + fa[TDCZPCC] = 1000.; + fa[TDCZPCS] = 1000.; +} + +void RecEventScale::setTeV() +{ + for (int ich = 0; ich < NChannels; ich++) { + fe[ich] = 0.001; + } + for (int itdc = 0; itdc < NTDCChannels; itdc++) { + fa[itdc] = 1.; + } +}; + +void RecEventScale::setTeVZN() +{ + for (int ich = IdZNAC; ich <= IdZNASum; ich++) { + fe[ich] = 0.001; + } + for (int ich = IdZNCC; ich <= IdZNCSum; ich++) { + fe[ich] = 0.001; + } + fa[TDCZNAC] = 1.; + fa[TDCZNAS] = 1.; + fa[TDCZNCC] = 1.; + fa[TDCZNCS] = 1.; +}; + +void RecEventScale::setTeVZP() +{ + for (int ich = IdZPAC; ich <= IdZPASum; ich++) { + fe[ich] = 0.001; + } + for (int ich = IdZPCC; ich <= IdZPCSum; ich++) { + fe[ich] = 0.001; + } + fa[TDCZPAC] = 1.; + fa[TDCZPAS] = 1.; + fa[TDCZPCC] = 1.; + fa[TDCZPCS] = 1.; +}; + +void RecEventScale::setNucleonEnergy(float energy) +{ // In GeV + for (int ich = 0; ich < NChannels; ich++) { + fe[ich] = 1. / energy; + } + for (int itdc = 0; itdc < NTDCChannels; itdc++) { + fa[itdc] = 1000. / energy; + } +}; + +void RecEventScale::setNucleonEnergyZN(float energy) +{ // In GeV + for (int ich = IdZNAC; ich <= IdZNASum; ich++) { + fe[ich] = 1. / energy; + } + for (int ich = IdZNCC; ich <= IdZNCSum; ich++) { + fe[ich] = 1. / energy; + } + fa[TDCZNAC] = 1000. / energy; + fa[TDCZNAS] = 1000. / energy; + fa[TDCZNCC] = 1000. / energy; + fa[TDCZNCS] = 1000. / energy; +}; + +void RecEventScale::setNucleonEnergyZP(float energy) +{ // In GeV + for (int ich = IdZPAC; ich <= IdZPASum; ich++) { + fe[ich] = 1. / energy; + } + for (int ich = IdZPCC; ich <= IdZPCSum; ich++) { + fe[ich] = 1. / energy; + } + fa[TDCZPAC] = 1000. / energy; + fa[TDCZPAS] = 1000. / energy; + fa[TDCZPCC] = 1000. / energy; + fa[TDCZPCS] = 1000. / energy; +}; + void RecEventFlat::init(const std::vector* RecBC, const std::vector* Energy, const std::vector* TDCData, const std::vector* Info) { mRecBC = *RecBC; @@ -143,7 +270,7 @@ int RecEventFlat::at(int ientry) for (int i = mFirstE; i < mStopE; i++) { auto myenergy = mEnergy[i]; auto ch = myenergy.ch(); - ezdc[ch] = myenergy.energy(); + ezdc[ch] = myenergy.energy() * RecEventScale::fe[ch]; // Assign implicit event info if (adcPedOr[ch] == false && adcPedQC[ch] == false && adcPedMissing[ch] == false) { adcPedEv[ch] = true; @@ -163,7 +290,7 @@ int RecEventFlat::at(int ientry) isEnd[ch] = true; } TDCVal[ch].push_back(mytdc.val); - TDCAmp[ch].push_back(mytdc.amp); + TDCAmp[ch].push_back(mytdc.amp * RecEventScale::fa[ch]); // Assign implicit event info if (tdcPedQC[ch] == false && tdcPedMissing[ch] == false) { tdcPedOr[ch] = true; @@ -386,13 +513,44 @@ float RecEventFlat::yZNC() float RecEventFlat::xZPA() { - // X coordinates of tower centers - // Positive because calorimeter is on the right of ZNA when looking - // from IP - const static float xc[4] = {2.8, 8.4, 14.0, 19.6}; + float x, rms; + centroidZPA(x, rms); + return x; +} + +float RecEventFlat::xZPC() +{ + float x, rms; + centroidZPC(x, rms); + return x; +} + +float RecEventFlat::rmsZPA() +{ + float x, rms; + centroidZPA(x, rms); + return rms; +} + +float RecEventFlat::rmsZPC() +{ + float x, rms; + centroidZPC(x, rms); + return rms; +} + +void RecEventFlat::centroidZPA(float& x, float& rms) +{ + // x coordinates of tower centers + // Negative because ZPA calorimeter is on the left of ZNA when looking from IP + // rms can result from 0 to sqrt((2.8^2+19.6^2)/2-((2.6+19.4)/2)^2) = 8.66 + const static float xc[4] = {-19.6, -14.0, -8.4, -2.8}; + const static float xq[4] = {19.6 * 19.6, 14.0 * 14.0, 8.4 * 8.4, 2.8 * 2.8}; static float c = 0; - if (mComputed[2]) { - return c; + static float d = 0; + if (mComputed[2] == true) { + x = c; + rms = d; } mComputed[2] = true; float e[4], w[4]; @@ -402,35 +560,56 @@ float RecEventFlat::xZPA() e[3] = EZDC(IdZPA4); if (e[0] < -1000000 || e[1] < -1000000 || e[2] < -1000000 || e[3] < -1000000) { c = -std::numeric_limits::infinity(); - return c; + d = -std::numeric_limits::infinity(); + x = c; + rms = d; + return; } float sumw = 0; c = 0; + d = 0; for (int i = 0; i < 4; i++) { if (e[i] < 0) { - e[i] = 0; + w[i] = 0; + } else { + // w[i] = std::sqrt(e[i]); + w[i] = e[i]; } - w[i] = std::sqrt(e[i]); sumw += w[i]; - c += w[i] * xc[i]; + c = c + w[i] * xc[i]; + d = d + w[i] * xq[i]; } if (sumw <= 0) { c = -std::numeric_limits::infinity(); + d = -std::numeric_limits::infinity(); } else { - c /= sumw; + c = c / sumw; + d = d / sumw; + d = d - c * c; // variance + if (d >= 0) { + d = TMath::Sqrt(d); + } else { + LOGF(warn, "%s FOP exception @ %d", __FILE__, __LINE__); + d = -std::numeric_limits::infinity(); + } } - return c; + x = c; + rms = d; + return; } -float RecEventFlat::xZPC() +void RecEventFlat::centroidZPC(float& x, float& rms) { - // X coordinates of tower centers - // Negative because calorimeter is on the left of ZNC when looking - // from IP - const static float xc[4] = {-2.8, -8.4, -14.0, -19.6}; + // x coordinates of tower centers + // Positive because ZPC calorimeter is on the right of ZNC when looking from IP + // x can result in a value from 2.8 to 19.6 + const static float xc[4] = {2.8, 8.4, 14.0, 19.6}; + const static float xq[4] = {2.8 * 2.8, 8.4 * 8.4, 14.0 * 14.0, 19.6 * 19.6}; static float c = 0; + static float d = 0; if (mComputed[3]) { - return c; + x = c; + rms = d; } mComputed[3] = true; float e[4], w[4]; @@ -440,24 +619,42 @@ float RecEventFlat::xZPC() e[3] = EZDC(IdZPC4); if (e[0] < -1000000 || e[1] < -1000000 || e[2] < -1000000 || e[3] < -1000000) { c = -std::numeric_limits::infinity(); - return c; + d = -std::numeric_limits::infinity(); + x = c; + rms = d; + return; } float sumw = 0; c = 0; + d = 0; for (int i = 0; i < 4; i++) { if (e[i] < 0) { - e[i] = 0; + w[i] = 0; + } else { + // w[i] = std::sqrt(e[i]); + w[i] = e[i]; } - w[i] = std::sqrt(e[i]); sumw += w[i]; c += w[i] * xc[i]; + d += w[i] * xq[i]; } if (sumw <= 0) { c = -std::numeric_limits::infinity(); + d = -std::numeric_limits::infinity(); } else { c /= sumw; + d /= sumw; + d = d - c * c; // variance + if (d >= 0) { + d = TMath::Sqrt(d); + } else { + LOGF(warn, "%s FOP exception @ %d", __FILE__, __LINE__); + d = -std::numeric_limits::infinity(); + } } - return c; + x = c; + rms = d; + return; } void RecEventFlat::print() const @@ -568,12 +765,11 @@ void RecEventFlat::printEvent() const int ich = o2::zdc::TDCSignal[itdc]; int nhit = NtdcV(itdc); if (NtdcA(itdc) != nhit) { - fprintf(stderr, "Mismatch in TDC %d data Val=%d Amp=%d\n", itdc, NtdcV(itdc), NtdcA(ich)); + fprintf(stderr, "Mismatch in TDC %d data length Val=%d Amp=%d values\n", itdc, NtdcV(itdc), NtdcA(ich)); continue; } for (int32_t ipos = 0; ipos < nhit; ipos++) { - printf("%9u.%04u T %2d %s = %g @ %g \n", mCurB.ir.orbit, mCurB.ir.bc, ich, ChannelNames[ich].data(), - FTDCAmp * TDCAmp[itdc][ipos], FTDCVal * TDCVal[itdc][ipos]); + printf("%9u.%04u T %2d %s = %g @ %g \n", mCurB.ir.orbit, mCurB.ir.bc, ich, ChannelNames[ich].data(), TDCAmp[itdc][ipos], FTDCVal * TDCVal[itdc][ipos]); } } // Energy diff --git a/DataFormats/Detectors/ZDC/src/ZDCTDCData.cxx b/DataFormats/Detectors/ZDC/src/ZDCTDCData.cxx index e86ddddbf2ce7..5c55eddee1525 100644 --- a/DataFormats/Detectors/ZDC/src/ZDCTDCData.cxx +++ b/DataFormats/Detectors/ZDC/src/ZDCTDCData.cxx @@ -14,7 +14,6 @@ using namespace o2::zdc; uint32_t ZDCTDCDataErr::mErrVal[NTDCChannels] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -uint32_t ZDCTDCDataErr::mErrAmp[NTDCChannels] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint32_t ZDCTDCDataErr::mErrId = 0; void o2::zdc::ZDCTDCData::print() const @@ -24,5 +23,5 @@ void o2::zdc::ZDCTDCData::print() const if (id != 0xff && itdc >= 0 && itdc < NTDCChannels) { isig = TDCSignal[itdc]; } - printf("%2d (%s) %d = %8.3f @ %d = %6.3f%s%s\n", isig, channelName(isig), amp, amplitude(), val, value(), isBeg() ? " B" : "", isEnd() ? " E" : ""); + printf("%2d (%s) %8.3f @ %d = %6.3f%s%s\n", isig, channelName(isig), amp, val, value(), isBeg() ? " B" : "", isEnd() ? " E" : ""); } diff --git a/DataFormats/Headers/include/Headers/DataHeader.h b/DataFormats/Headers/include/Headers/DataHeader.h index 8e2b8961c15cf..dbcdb8e0bba89 100644 --- a/DataFormats/Headers/include/Headers/DataHeader.h +++ b/DataFormats/Headers/include/Headers/DataHeader.h @@ -224,14 +224,21 @@ struct Descriptor { // Note: don't need to define operator=(ItgType v) because the compiler // can use Descriptor(ItgType initializer) for conversion + using ImplicitConversion = std::conditional_t<(size <= 8), ItgType, std::string_view>; // type cast operator for simplified usage of the descriptor's integer member - // TODO: this is sort of a hack, takes the first element. - // we should rethink these implicit conversions - operator ItgType() const + // in case it does not fit into the descriptor, the string representation is returned + operator ImplicitConversion() const { - static_assert(arraySize == 1, "casting Descriptor to ItgType only allowed for N<=8"); - return itg[0]; + if constexpr (std::is_same_v) { + return itg[0]; + } else { + size_t len = size; + while (len > 1 && str[len - 1] == 0) { + --len; + } + return std::string_view(str, len); + } } /// constructor from a compile-time string @@ -279,6 +286,8 @@ struct Descriptor { bool operator<(const Descriptor& other) const { return std::memcmp(this->str, other.str, N) < 0; } bool operator!=(const Descriptor& other) const { return not this->operator==(other); } + // Convesion operators for comparison with their implicitly convertible types + friend bool operator==(const Descriptor& lhs, ImplicitConversion rhs) { return static_cast(lhs) == rhs; } // explicitly forbid comparison with e.g. const char* strings // use: value == Descriptor("DESC") for the appropriate // template instantiation instead @@ -364,7 +373,8 @@ struct BaseHeader { uint32_t flags; struct { uint32_t flagsNextHeader : 1, // do we have a next header after this one? - flagsUnused : 31; // currently unused + flagsReserved : 15, // reserved for future use + flagsDerivedHeader : 16; // reserved for usage by the derived header }; }; @@ -574,6 +584,13 @@ constexpr o2::header::DataOrigin gDataOriginFOC{"FOC"}; constexpr o2::header::DataOrigin gDataOriginTRK{"TRK"}; constexpr o2::header::DataOrigin gDataOriginFT3{"FT3"}; constexpr o2::header::DataOrigin gDataOriginFCT{"FCT"}; +constexpr o2::header::DataOrigin gDataOriginTF3{"TF3"}; +constexpr o2::header::DataOrigin gDataOriginRCH{"RCH"}; +constexpr o2::header::DataOrigin gDataOriginMI3{"MI3"}; +constexpr o2::header::DataOrigin gDataOriginECL{"ECL"}; // upgrades +constexpr o2::header::DataOrigin gDataOriginFD3{"FD3"}; // upgrades + +constexpr o2::header::DataOrigin gDataOriginGPU{"GPU"}; // possible data types constexpr o2::header::DataDescription gDataDescriptionAny{"***************"}; @@ -583,6 +600,7 @@ constexpr o2::header::DataDescription gDataDescriptionClusters{"CLUSTERS"}; constexpr o2::header::DataDescription gDataDescriptionTracks{"TRACKS"}; constexpr o2::header::DataDescription gDataDescriptionConfig{"CONFIGURATION"}; constexpr o2::header::DataDescription gDataDescriptionInfo{"INFORMATION"}; +constexpr o2::header::DataDescription gDataDescriptionEos{"EOS"}; constexpr o2::header::DataDescription gDataDescriptionROOTStreamers{"ROOT STREAMERS"}; constexpr o2::header::DataDescription gDataDescriptionDISTSTF{"DISTSUBTIMEFRAME"}; /// @} // end of doxygen group diff --git a/DataFormats/Headers/include/Headers/DataHeaderHelpers.h b/DataFormats/Headers/include/Headers/DataHeaderHelpers.h index 41d59090e0274..4f7e49acb4d98 100644 --- a/DataFormats/Headers/include/Headers/DataHeaderHelpers.h +++ b/DataFormats/Headers/include/Headers/DataHeaderHelpers.h @@ -38,23 +38,23 @@ struct fmt::formatter::value, c } template - auto format(const T& p, FormatContext& ctx) + auto format(const T& p, FormatContext& ctx) const { - return format_to(ctx.out(), "{}", p.template as()); + return fmt::format_to(ctx.out(), "{}", p.template as()); } }; template <> struct fmt::formatter { - // Presentation format: 'f' - fixed, 'e' - exponential. char presentation = 's'; - // Parses format specifications of the form ['f' | 'e']. constexpr auto parse(format_parse_context& ctx) { auto it = ctx.begin(), end = ctx.end(); if (it != end && (*it == 's')) { presentation = *it++; + } else if (it != end && (*it == 'x')) { + presentation = *it++; } // Check if reached the end of the range: @@ -67,19 +67,28 @@ struct fmt::formatter { } template - auto format(const o2::header::DataHeader& h, FormatContext& ctx) + auto format(const o2::header::DataHeader& h, FormatContext& ctx) const { - auto res = fmt::format("Data header version {}, flags: {}\n", h.headerVersion, h.flags) + - fmt::format(" origin : {}\n", h.dataOrigin.str) + - fmt::format(" serialization: {}\n", h.payloadSerializationMethod.str) + - fmt::format(" description : {}\n", h.dataDescription.str) + - fmt::format(" sub spec. : {}\n", (long long unsigned int)h.subSpecification) + - fmt::format(" header size : {}\n", h.headerSize) + - fmt::format(" payloadSize : {}\n", (long long unsigned int)h.payloadSize) + - fmt::format(" firstTForbit : {}\n", h.firstTForbit) + - fmt::format(" tfCounter : {}\n", h.tfCounter) + - fmt::format(" runNumber : {}\n", h.runNumber); - return format_to(ctx.out(), "{}", res); + if (presentation == 's') { + auto res = fmt::format("Data header version {}, flags: {}\n", h.headerVersion, h.flags) + + fmt::format(" origin : {}\n", h.dataOrigin.str) + + fmt::format(" serialization: {}\n", h.payloadSerializationMethod.str) + + fmt::format(" description : {}\n", h.dataDescription.str) + + fmt::format(" sub spec. : {}\n", (long long unsigned int)h.subSpecification) + + fmt::format(" header size : {}\n", h.headerSize) + + fmt::format(" payloadSize : {}\n", (long long unsigned int)h.payloadSize) + + fmt::format(" firstTForbit : {}\n", h.firstTForbit) + + fmt::format(" tfCounter : {}\n", h.tfCounter) + + fmt::format(" runNumber : {}\n", h.runNumber) + + fmt::format(" split : {}/{}\n", h.splitPayloadIndex, h.splitPayloadParts); + return fmt::format_to(ctx.out(), "{}", res); + } else { + auto res = fmt::format("{}/{}/{}", + h.dataOrigin.str, + h.dataDescription.str, + (long long unsigned int)h.subSpecification); + return fmt::format_to(ctx.out(), "{}", res); + } } }; diff --git a/DataFormats/Headers/include/Headers/Stack.h b/DataFormats/Headers/include/Headers/Stack.h index 184733e35c339..9770df9fa54ef 100644 --- a/DataFormats/Headers/include/Headers/Stack.h +++ b/DataFormats/Headers/include/Headers/Stack.h @@ -14,9 +14,7 @@ #include "MemoryResources/MemoryResources.h" #include "Headers/DataHeader.h" -namespace o2 -{ -namespace header +namespace o2::header { //__________________________________________________________________________________________________ /// @struct Stack @@ -38,26 +36,27 @@ struct Stack { private: struct freeobj { - freeobj(memory_resource* mr) : resource(mr) {} + freeobj(memory_resource* mr, size_t s) : resource(mr), size(s) {} memory_resource* resource{nullptr}; - void operator()(std::byte* ptr) { resource->deallocate(ptr, 0, 0); } + size_t size{0}; + void operator()(std::byte* ptr) { resource->deallocate(ptr, size, alignof(std::max_align_t)); } }; public: - using allocator_type = boost::container::pmr::polymorphic_allocator; + using allocator_type = fair::mq::pmr::polymorphic_allocator; using value_type = std::byte; - using BufferType = std::unique_ptr; //this gives us proper default move semantics for free + using BufferType = std::unique_ptr; // this gives us proper default move semantics for free Stack() = default; Stack(Stack&&) = default; Stack(Stack&) = delete; Stack& operator=(Stack&) = delete; - Stack& operator=(Stack&&) = default; + Stack& operator=(Stack&&) = delete; - value_type* data() const { return buffer.get(); } - size_t size() const { return bufferSize; } + [[nodiscard]] value_type* data() const { return buffer.get(); } + [[nodiscard]] size_t size() const { return bufferSize; } allocator_type get_allocator() const { return allocator; } - const BaseHeader* first() const { return reinterpret_cast(this->data()); } + [[nodiscard]] const BaseHeader* first() const { return reinterpret_cast(this->data()); } static const BaseHeader* firstHeader(std::byte const* buf) { return BaseHeader::get(buf); } static const BaseHeader* lastHeader(std::byte const* buf) { @@ -89,9 +88,9 @@ struct Stack { /// all headers must derive from BaseHeader, in addition also other stacks can be passed to ctor. template >::value, int> = 0> + !std::is_convertible>::value, int> = 0> Stack(FirstArgType&& firstHeader, Headers&&... headers) - : Stack(boost::container::pmr::new_delete_resource(), std::forward(firstHeader), + : Stack(fair::mq::pmr::new_delete_resource(), std::forward(firstHeader), std::forward(headers)...) { } @@ -101,9 +100,13 @@ struct Stack { Stack(const allocator_type allocatorArg, Headers&&... headers) : allocator{allocatorArg}, bufferSize{calculateSize(std::forward(headers)...)}, - buffer{static_cast(allocator.resource()->allocate(bufferSize, alignof(std::max_align_t))), freeobj{allocator.resource()}} + buffer{static_cast(allocator.resource()->allocate(bufferSize, alignof(std::max_align_t))), freeobj{allocator.resource(), bufferSize}} { - inject(buffer.get(), std::forward(headers)...); + if constexpr (sizeof...(headers) > 1) { + injectAll(buffer.get(), std::forward(headers)...); + } else if (sizeof...(headers) == 1) { + injectBool(buffer.get(), std::forward(headers)..., false); + } } //______________________________________________________________________________________________ @@ -117,7 +120,7 @@ struct Stack { template constexpr static size_t calculateSize(T&& h) noexcept { - //if it's a pointer (to a stack) traverse it + // if it's a pointer (to a stack) traverse it if constexpr (std::is_convertible_v) { const BaseHeader* next = BaseHeader::get(std::forward(h)); if (!next) { @@ -128,23 +131,23 @@ struct Stack { size += next->size(); } return size; - //otherwise get the size directly + // otherwise get the size directly } else { return h.size(); } } - //recursion terminator + // recursion terminator constexpr static size_t calculateSize() { return 0; } private: - allocator_type allocator{boost::container::pmr::new_delete_resource()}; + allocator_type allocator{fair::mq::pmr::new_delete_resource()}; size_t bufferSize{0}; - BufferType buffer{nullptr, freeobj{allocator.resource()}}; + BufferType buffer{nullptr, freeobj{allocator.resource(), 0}}; //______________________________________________________________________________________________ template - static std::byte* inject(std::byte* here, T&& h, bool more = false) noexcept + static std::byte* injectBool(std::byte* here, T&& h, bool more) noexcept { using headerType = typename std::remove_cv::type>::type; if (here == nullptr) { @@ -189,11 +192,15 @@ struct Stack { //______________________________________________________________________________________________ template - static std::byte* inject(std::byte* here, T&& h, Args&&... args) noexcept + static std::byte* injectAll(std::byte* here, T&& h, Args&&... args) noexcept { bool more = hasNonEmptyArg(args...); - auto alsohere = inject(here, h, more); - return inject(alsohere, args...); + auto alsohere = injectBool(here, h, more); + if constexpr (sizeof...(args) > 1) { + return injectAll(alsohere, args...); + } else { + return injectBool(alsohere, args..., false); + } } //______________________________________________________________________________________________ @@ -222,7 +229,6 @@ struct Stack { } }; -} // namespace header -} // namespace o2 +} // namespace o2::header #endif // HEADERS_STACK_H diff --git a/DataFormats/Headers/include/Headers/SubframeMetadata.h b/DataFormats/Headers/include/Headers/SubframeMetadata.h deleted file mode 100644 index 255fa0ceb8db6..0000000000000 --- a/DataFormats/Headers/include/Headers/SubframeMetadata.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef SUBFRAMEMETADATA_H -#define SUBFRAMEMETADATA_H - -#include - -namespace o2 -{ -namespace data_flow -{ - -struct SubframeMetadata { - // TODO: replace with timestamp struct - // IDEA: not timeframeID because can be calculcated with helper function - // QUESTION: isn't the duration set to ~22ms? - uint64_t startTime = ~(uint64_t)0; - uint64_t duration = ~(uint64_t)0; - - //further meta data to be added - - // putting data specific to FLP origin - int flpIndex; -}; - -// Helper function to derive the timeframe id from the actual timestamp. -// Timestamp is in nanoseconds. Each Timeframe is ~22ms i.e. 2^17 nanoseconds, -// so we can get a unique id by dividing by the timeframe period and masking -// the lower 16 bits. Overlaps will only happen every ~ 22 minutes. -constexpr uint16_t - timeframeIdFromTimestamp(uint64_t timestamp, uint64_t timeFrameDuration) -{ - return (timestamp / timeFrameDuration) & 0xffff; -} - -// A Mockup class to describe some TPC-like payload -struct TPCTestCluster { - float x = 0.f; - float y = 0.f; - float z = 1.5f; - float q = 0.; - uint64_t timeStamp; // the time this thing was digitized/recorded -}; - -struct TPCTestPayload { - std::vector clusters; -}; - -// a mockup class to describe some "ITS" payload -struct ITSRawData { - float x = -1.; - float y = 1.; - uint64_t timeStamp; -}; - -} // namespace data_flow -} // namespace o2 - -#endif diff --git a/DataFormats/Headers/include/Headers/TimeStamp.h b/DataFormats/Headers/include/Headers/TimeStamp.h index bd4bd8c8b94e9..b6d2b26ea4822 100644 --- a/DataFormats/Headers/include/Headers/TimeStamp.h +++ b/DataFormats/Headers/include/Headers/TimeStamp.h @@ -23,9 +23,7 @@ #include #include // for std::integral_constant -namespace o2 -{ -namespace header +namespace o2::header { // https://lhc-machine-outreach.web.cern.ch/lhc-machine-outreach/collisions.htm @@ -140,12 +138,14 @@ class TimeStamp static_assert(std::is_same::value && std::is_same::value, "only clock and duration types defining the rep and period member types are allowed"); using duration = std::chrono::duration; - if (mUnit == sClockLHC) { + static_assert(sizeof(mUnit) == sizeof(sClockLHC), "size mismatch of mUnit and sClockLHC"); + if (memcmp(&mUnit, &sClockLHC, sizeof(sClockLHC)) == 0) { // cast each part individually, if the precision of the return type // is smaller the values are simply truncated return std::chrono::duration_cast(LHCOrbitClock::duration(mPeriod) + LHCBunchClock::duration(mBCNumber)); } - if (mUnit == sMicroSeconds) { + static_assert(sizeof(mUnit) == sizeof(sMicroSeconds), "size mismatch of mUnit and sMicroSeconds"); + if (memcmp(&mUnit, &sMicroSeconds, sizeof(sMicroSeconds)) == 0) { // TODO: is there a better way to mark the subticks invalid for the // micro seconds representation? First step is probably to remove/rename the // variable @@ -159,7 +159,7 @@ class TimeStamp } // TODO: implement transformation from one unit to the other - //void transform(const TimeUnitID& unit) { + // void transform(const TimeUnitID& unit) { // if (mUnit == unit) return; // ... //} @@ -182,7 +182,6 @@ class TimeStamp }; }; }; -} //namespace header -} //namespace o2 +} // namespace o2::header #endif diff --git a/DataFormats/Headers/test/testDataHeader.cxx b/DataFormats/Headers/test/testDataHeader.cxx index 0703fc6c3ae71..2403c1a6230be 100644 --- a/DataFormats/Headers/test/testDataHeader.cxx +++ b/DataFormats/Headers/test/testDataHeader.cxx @@ -280,8 +280,8 @@ BOOST_AUTO_TEST_CASE(headerStack_test) Stack s2{s1, meta}; BOOST_CHECK(s2.size() == s1.size() + sizeof(decltype(meta))); - //check dynamic construction - where we don't have the type information and need to - //work with BaseHeader pointers + // check dynamic construction - where we don't have the type information and need to + // work with BaseHeader pointers const test::MetaHeader thead{2}; o2::header::BaseHeader const* bname = reinterpret_cast(&thead); Stack ds2(s1, *bname); @@ -313,8 +313,8 @@ BOOST_AUTO_TEST_CASE(headerStack_test) BOOST_REQUIRE(h3 != nullptr); BOOST_CHECK(h3->secret == 42); - //test constructing from a buffer and an additional header - using namespace boost::container::pmr; + // test constructing from a buffer and an additional header + using namespace fair::mq::pmr; Stack s5(new_delete_resource(), s1.data(), Stack{}, meta); BOOST_CHECK(s5.size() == s1.size() + sizeof(meta)); // check if we can find the header even though there was an empty stack in the middle diff --git a/DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h b/DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h deleted file mode 100644 index 59be2a86c3c2b..0000000000000 --- a/DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -//-*- Mode: C++ -*- - -#ifndef TPCRAWCLUSTER_H -#define TPCRAWCLUSTER_H -//**************************************************************************** -//* This file is free software: you can redistribute it and/or modify * -//* it under the terms of the GNU General Public License as published by * -//* the Free Software Foundation, either version 3 of the License, or * -//* (at your option) any later version. * -//* * -//* Primary Authors: Matthias Richter * -//* * -//* The authors make no claims about the suitability of this software for * -//* any purpose. It is provided "as is" without express or implied warranty. * -//**************************************************************************** - -// @file TPCRawCluster.h -// @author Matthias Richter -// @since 2015-09-27 -// @brief ALICE HLT TPC raw cluster structure and tools - -#include -#include // ifstream -#include // memcpy - -namespace o2 -{ -namespace AliceHLT -{ - -/** - * @struct RawCluster - * This is a redefinition from AliRoot/HLT/TPCLib/AliHLTTPCRawCluster.h for the - * sake of reading HLT TPC raw cluster files into O2. - * - * TODO: there is no dependence on AliRoot, however, a test needs to be added - * to check consistency if AliRoot is available in the build. - */ -struct RawCluster { - - int16_t GetPadRow() const { return fPadRow; } - float GetPad() const { return fPad; } - float GetTime() const { return fTime; } - float GetSigmaPad2() const { return fSigmaPad2; } - float GetSigmaTime2() const { return fSigmaTime2; } - int32_t GetCharge() const { return fCharge; } - int32_t GetQMax() const { return fQMax; } - bool GetFlagSplitPad() const { return (fFlags & (1 << 0)); } - bool GetFlagSplitTime() const { return (fFlags & (1 << 1)); } - bool GetFlagSplitAny() const { return (fFlags & 3); } - uint16_t GetFlags() const { return (fFlags); } - - int16_t fPadRow; - uint16_t fFlags; //Flags: (1 << 0): Split in pad direction - // (1 << 1): Split in time direction - //During cluster merging, flags are or'd - float fPad; - float fTime; - float fSigmaPad2; - float fSigmaTime2; - uint16_t fCharge; - uint16_t fQMax; -}; - -/** - * @struct RawClusterData - * Header data struct for a raw cluster block - */ -struct RawClusterData { - uint32_t fVersion; // version number - uint32_t fCount; // number of clusters - RawCluster fClusters[0]; // array of clusters -}; - -std::ostream& operator<<(std::ostream& stream, const RawCluster& cluster) -{ - stream << "TPCRawCluster:" - << " " << cluster.GetPadRow() - << " " << cluster.GetPad() - << " " << cluster.GetTime() - << " " << cluster.GetSigmaPad2() - << " " << cluster.GetSigmaTime2() - << " " << cluster.GetCharge() - << " " << cluster.GetQMax(); - return stream; -} - -/** - * @class RawClusterArray Wrapper to binary data block of HLT TPC raw clusters - * Container class which provides access to the content of a binary block of - * HLT TPC raw clusters. - */ -class RawClusterArray -{ - public: - RawClusterArray() : mBuffer(nullptr), mBufferSize(0), mNClusters(0), mClusters(NULL), mClustersEnd(NULL) {} - RawClusterArray(const char* filename) : mBuffer(nullptr), mBufferSize(0), mNClusters(0), mClusters(NULL), mClustersEnd(NULL) - { - init(filename); - } - RawClusterArray(unsigned char* buffer, int size) : mBuffer(nullptr), mBufferSize(0), mNClusters(0), mClusters(NULL), mClustersEnd(NULL) - { - init(buffer, size); - } - ~RawClusterArray() {} - - typedef uint8_t Buffer_t; - - int init(const char* filename) - { - std::ifstream input(filename, std::ifstream::binary); - clear(0); - if (input) { - // get length of file: - input.seekg(0, input.end); - int length = input.tellg(); - input.seekg(0, input.beg); - - // allocate memory: - mBuffer = new Buffer_t[length]; - mBufferSize = length; - - // read data as a block: - input.read(reinterpret_cast(mBuffer), length); - if (!input.good()) { - clear(-1); - std::cerr << "failed to read " << length << " byte(s) from file " << filename << std::endl; - } - - input.close(); - return init(); - } - std::cerr << "failed to open file " << filename << std::endl; - return -1; - } - - int init(unsigned char* buffer, int size) - { - if (!buffer || size <= 0) - return -1; - clear(0); - mBuffer = new Buffer_t[size]; - mBufferSize = size; - memcpy(mBuffer, buffer, size); - return init(); - } - - int GetNClusters() const { return mNClusters; } - - RawCluster* begin() { return mClusters; } - - RawCluster* end() { return mClustersEnd; } - - RawCluster& operator[](int i) - { - if (i + 1 > mNClusters) { - // runtime exeption? - static RawCluster dummy; - return dummy; - } - return *(mClusters + i); - } - - void print() { print(std::cout); } - - template - StreamT& print(StreamT& stream) - { - std::cout << "RawClusterArray: " << mNClusters << " cluster(s)" << std::endl; - for (RawCluster* cluster = mClusters; cluster != mClustersEnd; cluster++) { - std::cout << " " << *cluster << std::endl; - } - return stream; - } - - private: - int init() - { - if (mBuffer == nullptr || mBufferSize == 0) - return 0; - if (mBufferSize < sizeof(RawClusterData)) - return -1; - RawClusterData& clusterData = *reinterpret_cast(mBuffer); - - if (clusterData.fCount * sizeof(RawCluster) + sizeof(RawClusterData) > mBufferSize) { - std::cerr << "Format error, " << clusterData.fCount << " cluster(s) " - << "would require " - << (clusterData.fCount * sizeof(RawCluster) + sizeof(RawClusterData)) - << " byte(s), but only " << mBufferSize << " available" << std::endl; - return clear(-1); - } - - mNClusters = clusterData.fCount; - mClusters = clusterData.fClusters; - mClustersEnd = mClusters + mNClusters; - - return mNClusters; - } - - int clear(int returnValue) - { - mNClusters = 0; - mClusters = NULL; - mClustersEnd = NULL; - delete[] mBuffer; - mBuffer = nullptr; - mBufferSize = 0; - - return returnValue; - } - - Buffer_t* mBuffer; - int mBufferSize; - int mNClusters; - RawCluster* mClusters; - RawCluster* mClustersEnd; -}; - -}; // namespace AliceHLT -}; // namespace o2 -#endif diff --git a/DataFormats/MemoryResources/include/MemoryResources/MemoryResources.h b/DataFormats/MemoryResources/include/MemoryResources/MemoryResources.h index 42dd69aff38e3..b52f5c715575e 100644 --- a/DataFormats/MemoryResources/include/MemoryResources/MemoryResources.h +++ b/DataFormats/MemoryResources/include/MemoryResources/MemoryResources.h @@ -111,172 +111,16 @@ class MessageResource : public FairMQMemoryResource } }; -//__________________________________________________________________________________________________ -// A spectator pmr memory resource which only watches the memory of the underlying buffer, does not -// carry out real allocation. It owns the underlying buffer which is destroyed on deallocation. -template -class SpectatorMemoryResource : public boost::container::pmr::memory_resource -{ - public: - using buffer_type = BufferType; - - SpectatorMemoryResource() noexcept = delete; - SpectatorMemoryResource(const SpectatorMemoryResource&) noexcept = delete; - SpectatorMemoryResource(SpectatorMemoryResource&&) noexcept = default; - SpectatorMemoryResource& operator=(const SpectatorMemoryResource&) = delete; - SpectatorMemoryResource& operator=(SpectatorMemoryResource&&) = default; - ~SpectatorMemoryResource() noexcept override = default; - - // the resource is the pointer managed by unique_ptr - template - SpectatorMemoryResource(std::unique_ptr&& buffer, size_t size) - : mBuffer{std::move(buffer)}, mPointer{mBuffer.get()}, mSize{size} - { - } - - // the resource is the data of the vector managed by unique ptr - template - SpectatorMemoryResource(std::unique_ptr, typename buffer_type::deleter_type>&& buffer) - : mBuffer{std::move(buffer)}, mPointer{mBuffer->data()}, mSize{mBuffer->size() * sizeof(T)} - { - } - - // TODO: the underlying resource can be directly the vector or the read only buffer - protected: - void* do_allocate(std::size_t bytes, std::size_t /*alignment*/) override - { - if (mSize > 0) { - if (bytes > mSize) { - throw std::bad_alloc(); - } - mSize = 0; - return mPointer; - } - throw std::runtime_error("Can not allocate: this memory resource is only supposed to provide spectator access to external buffer"); - } - - void do_deallocate(void* p, std::size_t /*bytes*/, std::size_t /*alignment*/) override - { - if (p == mPointer) { - mBuffer.reset(); - mPointer = nullptr; - } else if (mPointer == nullptr) { - // there is an error in the logic flow, this should never be called more than once - throw std::logic_error("underlying controlled resource has been released already"); - } else { - throw std::logic_error("this resource can only deallocate the controlled resource pointer"); - } - } - bool do_is_equal(const memory_resource& /*other*/) const noexcept override - { - // uniquely owns the underlying resource, can never be equal to any other instance - return false; - } - - private: - buffer_type mBuffer; - void* mPointer = nullptr; - size_t mSize = 0; -}; - -//__________________________________________________________________________________________________ -// This in general (as in STL) is a bad idea, but here it is safe to inherit from an allocator since we -// have no additional data and only override some methods so we don't get into slicing and other problems. -template -class SpectatorAllocator : public boost::container::pmr::polymorphic_allocator -{ - public: - using boost::container::pmr::polymorphic_allocator::polymorphic_allocator; - using propagate_on_container_move_assignment = std::true_type; - - // skip default construction of empty elements - // this is important for two reasons: one: it allows us to adopt an existing buffer (e.g. incoming message) and - // quickly construct large vectors while skipping the element initialization. - template - void construct(U*) - { - } - - // dont try to call destructors, makes no sense since resource is managed externally AND allowed - // types cannot have side effects - template - void destroy(U*) - { - } - - T* allocate(size_t size) { return reinterpret_cast(this->resource()->allocate(size * sizeof(T), 64)); } - void deallocate(T* ptr, size_t size) - { - this->resource()->deallocate(const_cast::type*>(ptr), size); - } -}; - -//__________________________________________________________________________________________________ -/// This allocator has a pmr-like interface, but keeps the unique MessageResource as internal state, -/// allowing full resource (associated message) management internally without any global state. -template -class OwningMessageSpectatorAllocator -{ - public: - using value_type = T; - - MessageResource mResource; - - OwningMessageSpectatorAllocator() noexcept = default; - OwningMessageSpectatorAllocator(const OwningMessageSpectatorAllocator&) noexcept = default; - OwningMessageSpectatorAllocator(OwningMessageSpectatorAllocator&&) noexcept = default; - OwningMessageSpectatorAllocator(MessageResource&& resource) noexcept : mResource{resource} {} - - template - OwningMessageSpectatorAllocator(const OwningMessageSpectatorAllocator& other) noexcept : mResource(other.mResource) - { - } - - OwningMessageSpectatorAllocator& operator=(const OwningMessageSpectatorAllocator& other) - { - mResource = other.mResource; - return *this; - } - - OwningMessageSpectatorAllocator select_on_container_copy_construction() const - { - return OwningMessageSpectatorAllocator(); - } - - boost::container::pmr::memory_resource* resource() { return &mResource; } - - // skip default construction of empty elements - // this is important for two reasons: one: it allows us to adopt an existing buffer (e.g. incoming message) and - // quickly construct large vectors while skipping the element initialization. - template - void construct(U*) - { - } - - // dont try to call destructors, makes no sense since resource is managed externally AND allowed - // types cannot have side effects - template - void destroy(U*) - { - } - - T* allocate(size_t size) { return reinterpret_cast(mResource.allocate(size * sizeof(T), 64)); } - void deallocate(T* ptr, size_t size) - { - mResource.deallocate(const_cast::type*>(ptr), size); - } -}; - // The NoConstructAllocator behaves like the normal pmr vector but does not call constructors / destructors template -class NoConstructAllocator : public boost::container::pmr::polymorphic_allocator +class NoConstructAllocator : public fair::mq::pmr::polymorphic_allocator { public: - using boost::container::pmr::polymorphic_allocator::polymorphic_allocator; + using fair::mq::pmr::polymorphic_allocator::polymorphic_allocator; using propagate_on_container_move_assignment = std::true_type; template - NoConstructAllocator(Args&&... args) : boost::container::pmr::polymorphic_allocator(std::forward(args)...) + NoConstructAllocator(Args&&... args) : fair::mq::pmr::polymorphic_allocator(std::forward(args)...) { } @@ -301,20 +145,9 @@ class NoConstructAllocator : public boost::container::pmr::polymorphic_allocator //__________________________________________________________________________________________________ //__________________________________________________________________________________________________ -using ByteSpectatorAllocator = SpectatorAllocator; -using BytePmrAllocator = boost::container::pmr::polymorphic_allocator; +using BytePmrAllocator = fair::mq::pmr::polymorphic_allocator; template -using vector = std::vector>; - -//__________________________________________________________________________________________________ -/// Return a std::vector spanned over the contents of the message, takes ownership of the message -template -auto adoptVector(size_t nelem, fair::mq::MessagePtr message) -{ - static_assert(std::is_trivially_destructible::value); - return std::vector>( - nelem, OwningMessageSpectatorAllocator(MessageResource{std::move(message)})); -}; +using vector = std::vector>; //__________________________________________________________________________________________________ /// Get the allocator associated to a transport factory diff --git a/DataFormats/MemoryResources/test/testMemoryResources.cxx b/DataFormats/MemoryResources/test/testMemoryResources.cxx index 592e76c8ef1f4..a49cd00d75255 100644 --- a/DataFormats/MemoryResources/test/testMemoryResources.cxx +++ b/DataFormats/MemoryResources/test/testMemoryResources.cxx @@ -47,12 +47,12 @@ int testData::nconstructions = 0; BOOST_AUTO_TEST_CASE(transportallocatormap_test) { - size_t session{fair::mq::tools::UuidHash()}; + size_t session{(size_t)getpid() * 1000}; fair::mq::ProgOptions config; config.SetProperty("session", std::to_string(session)); auto factoryZMQ = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); - auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem"); + auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem", "transportallocatormap_test", &config); auto allocZMQ = getTransportAllocator(factoryZMQ.get()); auto allocSHM = getTransportAllocator(factorySHM.get()); BOOST_CHECK(allocZMQ != nullptr && allocSHM != allocZMQ); @@ -60,16 +60,16 @@ BOOST_AUTO_TEST_CASE(transportallocatormap_test) BOOST_CHECK(_tmp == allocZMQ); } -using namespace boost::container::pmr; +using namespace fair::mq::pmr; BOOST_AUTO_TEST_CASE(allocator_test) { - size_t session{fair::mq::tools::UuidHash()}; + size_t session{(size_t)getpid() * 1000 + 1}; fair::mq::ProgOptions config; config.SetProperty("session", std::to_string(session)); auto factoryZMQ = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); - auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem"); + auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem", "allocator_test", &config); auto allocZMQ = getTransportAllocator(factoryZMQ.get()); auto allocSHM = getTransportAllocator(factorySHM.get()); @@ -88,26 +88,17 @@ BOOST_AUTO_TEST_CASE(allocator_test) } testData::nconstructions = 0; - { - std::vector> v(SpectatorAllocator{allocZMQ}); - v.reserve(3); - BOOST_CHECK(allocZMQ->getNumberOfMessages() == 1); - v.emplace_back(1); - v.emplace_back(2); - v.emplace_back(3); - BOOST_CHECK(testData::nconstructions == 3); - } BOOST_CHECK(allocZMQ->getNumberOfMessages() == 0); } BOOST_AUTO_TEST_CASE(getMessage_test) { - size_t session{fair::mq::tools::UuidHash()}; + size_t session{(size_t)getpid() * 1000 + 2}; fair::mq::ProgOptions config; config.SetProperty("session", std::to_string(session)); auto factoryZMQ = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); - auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem"); + auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem", "getMessage_test", &config); auto allocZMQ = getTransportAllocator(factoryZMQ.get()); auto allocSHM = getTransportAllocator(factorySHM.get()); @@ -147,73 +138,6 @@ BOOST_AUTO_TEST_CASE(getMessage_test) messageArray = static_cast(message->GetData()); BOOST_CHECK(messageArray[0] == 4 && messageArray[1] == 5 && messageArray[2] == 6); - { - std::vector> v(SpectatorAllocator{allocSHM}); - } -} - -BOOST_AUTO_TEST_CASE(adoptVector_test) -{ - size_t session{fair::mq::tools::UuidHash()}; - fair::mq::ProgOptions config; - config.SetProperty("session", std::to_string(session)); - - auto factoryZMQ = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); - auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem"); - auto allocZMQ = getTransportAllocator(factoryZMQ.get()); - auto allocSHM = getTransportAllocator(factorySHM.get()); - - testData::nconstructions = 0; - - //Create a bogus message - auto message = factoryZMQ->CreateMessage(3 * sizeof(testData)); - auto messageAddr = message.get(); - testData tmpBuf[3] = {3, 2, 1}; - std::memcpy(message->GetData(), tmpBuf, 3 * sizeof(testData)); - - auto adoptedOwner = adoptVector(3, std::move(message)); - BOOST_CHECK(adoptedOwner[0].i == 3); - BOOST_CHECK(adoptedOwner[1].i == 2); - BOOST_CHECK(adoptedOwner[2].i == 1); - - auto reclaimedMessage = o2::pmr::getMessage(std::move(adoptedOwner)); - BOOST_CHECK(reclaimedMessage.get() == messageAddr); - BOOST_CHECK(adoptedOwner.size() == 0); - - auto modified = adoptVector(3, std::move(reclaimedMessage)); - modified.emplace_back(9); - BOOST_CHECK(modified[3].i == 9); - BOOST_CHECK(modified.size() == 4); - BOOST_CHECK(testData::nconstructions == 7); - auto modifiedMessage = getMessage(std::move(modified)); - BOOST_CHECK(modifiedMessage != nullptr); - BOOST_CHECK(modifiedMessage.get() != messageAddr); -} - -BOOST_AUTO_TEST_CASE(test_SpectatorMemoryResource) -{ - constexpr int size = 5; - auto buffer = std::make_unique(size); - auto const* bufferdata = buffer.get(); - SpectatorMemoryResource resource(std::move(buffer), size * sizeof(int)); - std::vector> bufferclone(size, o2::pmr::SpectatorAllocator(&resource)); - BOOST_CHECK(bufferclone.data() == bufferdata); - BOOST_CHECK(bufferclone.size() == size); - BOOST_CHECK_THROW(bufferclone.resize(2 * size), std::runtime_error); - - auto vecbuf = std::make_unique>(size); - auto const* vectordata = vecbuf->data(); - SpectatorMemoryResource vecresource(std::move(vecbuf)); - std::vector> vecclone(size, o2::pmr::SpectatorAllocator(&vecresource)); - BOOST_CHECK(vecclone.data() == vectordata); - BOOST_CHECK(vecclone.size() == size); - BOOST_CHECK_THROW(vecclone.resize(2 * size), std::runtime_error); - - std::vector> vecmove; - vecmove = std::move(vecclone); - BOOST_CHECK(vecclone.size() == 0); - BOOST_CHECK(vecmove.data() == vectordata); - BOOST_CHECK(vecmove.size() == size); } }; // namespace o2::pmr diff --git a/DataFormats/Parameters/CMakeLists.txt b/DataFormats/Parameters/CMakeLists.txt index 3539e9e11fd1e..ed137a42f6565 100644 --- a/DataFormats/Parameters/CMakeLists.txt +++ b/DataFormats/Parameters/CMakeLists.txt @@ -14,7 +14,8 @@ o2_add_library(DataFormatsParameters src/GRPLHCIFData.cxx src/GRPECSObject.cxx src/GRPMagField.cxx - PUBLIC_LINK_LIBRARIES FairRoot::Base O2::CommonConstants + src/AggregatedRunInfo.cxx + PUBLIC_LINK_LIBRARIES FairRoot::Base O2::CommonConstants O2::CommonTypes O2::CCDB O2::DetectorsCommonDataFormats) @@ -24,6 +25,7 @@ o2_target_root_dictionary(DataFormatsParameters include/DataFormatsParameters/GRPLHCIFData.h include/DataFormatsParameters/GRPECSObject.h include/DataFormatsParameters/GRPMagField.h + include/DataFormatsParameters/AggregatedRunInfo.h LINKDEF src/ParametersDataLinkDef.h) o2_add_executable(simgrp-tool diff --git a/DataFormats/Parameters/include/DataFormatsParameters/AggregatedRunInfo.h b/DataFormats/Parameters/include/DataFormatsParameters/AggregatedRunInfo.h new file mode 100644 index 0000000000000..d0347114b5b4c --- /dev/null +++ b/DataFormats/Parameters/include/DataFormatsParameters/AggregatedRunInfo.h @@ -0,0 +1,70 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GRPECSObject.h +/// \brief Header of the AggregatedRunInfo struct +/// \author ruben.shahoyan@cern.ch sandro.wenzel@cern.ch + +#ifndef ALICEO2_DATA_AGGREGATEDRUNINFO_H_ +#define ALICEO2_DATA_AGGREGATEDRUNINFO_H_ + +#include +#include "CCDB/BasicCCDBManager.h" + +namespace o2::parameters +{ + +class GRPECSObject; +class GRPLHCIFData; + +/// Composite struct where one may collect important global properties of data "runs" +/// aggregated from various sources (GRPECS, RunInformation CCDB entries, etc.). +/// Also offers the authoritative algorithms to collect these information for easy reuse +/// across various algorithms (anchoredMC, analysis, ...) +struct AggregatedRunInfo { + int runNumber = 0; // run number + int64_t sor = 0; // best known timestamp for the start of run + int64_t eor = 0; // best known timestamp for end of run + int64_t orbitsPerTF = 0; // number of orbits per TF (takes precedence over that in GRPECS) + int64_t orbitReset = 0; // timestamp of orbit reset before run + int64_t orbitSOR = 0; // orbit when run starts after orbit reset + int64_t orbitEOR = 0; // orbit when run ends after orbit reset + + // we may have pointers to actual data source objects GRPECS, ... + const o2::parameters::GRPECSObject* grpECS = nullptr; // pointer to GRPECSobject (fetched during struct building) + const o2::parameters::GRPLHCIFData* grpLHC = nullptr; + + static AggregatedRunInfo buildAggregatedRunInfo(int runnumber, long sorMS, long eorMS, long orbitResetMUS, const o2::parameters::GRPECSObject* grpecs, const std::vector* ctfFirstRunOrbitVec, const o2::parameters::GRPLHCIFData* grplhcif = nullptr); + + // fills and returns AggregatedRunInfo for a given data run number. + static AggregatedRunInfo buildAggregatedRunInfo_DATA(o2::ccdb::CCDBManagerInstance& ccdb, int runnumber); + + // Returns the meta-data (MCProdInfo) associated to production lpm_prod_tag (performed by username) + static std::map getMCProdInfo(o2::ccdb::CCDBManagerInstance& ccdb, int runnumber, + std::string const& lpm_prod_tag, std::string const& username = "aliprod"); + + // function that adjusts with values from MC + void adjust_from_MC(o2::ccdb::CCDBManagerInstance& ccdb, int run_number, std::string const& lpm_prod_tag, std::string const& username = "aliprod"); + + // Fills and returns AggregatedRunInfo for a given run number. + // If a non-empty lpm_prod_tag is given, it will potentially override values with specifics from a + // MC production identified by that tag and username. + static AggregatedRunInfo buildAggregatedRunInfo(o2::ccdb::CCDBManagerInstance& ccdb, + int runnumber, + std::string const& lpm_prod_tag = "", + std::string const& username = "aliprod"); + + ClassDefNV(AggregatedRunInfo, 1); +}; + +} // namespace o2::parameters + +#endif diff --git a/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h b/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h index 7ac6cd6b00133..ddcda8f66507c 100644 --- a/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h +++ b/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h @@ -41,6 +41,7 @@ class GRPLHCIFData std::pair getBeamEnergyPerZWithTime() const { return mBeamEnergyPerZ; } int32_t getBeamEnergyPerZ() const { return mBeamEnergyPerZ.second; } + float getBeamEnergyPerZinGeV() const { return mBeamEnergyPerZ.second * 0.12; } long getBeamEnergyPerZTime() const { return mBeamEnergyPerZ.first; } void setBeamEnergyPerZWithTime(std::pair p) { mBeamEnergyPerZ = p; } void setBeamEnergyPerZWithTime(long t, int32_t v) { mBeamEnergyPerZ = std::make_pair(t, v); } @@ -84,12 +85,18 @@ class GRPLHCIFData /// getters/setters for given beam A and Z info, encoded as A<<16+Z int getBeamZ(beamDirection beam) const { return mBeamAZ[static_cast(beam)] & 0xffff; } int getBeamA(beamDirection beam) const { return mBeamAZ[static_cast(beam)] >> 16; } + int getBeamZ(int beam) const { return mBeamAZ[beam] & 0xffff; } + int getBeamA(int beam) const { return mBeamAZ[beam] >> 16; } float getBeamZoverA(beamDirection beam) const; + float getBeamZoverA(int beam) const; void setBeamAZ(int a, int z, beamDirection beam) { mBeamAZ[static_cast(beam)] = (a << 16) + z; } void setBeamAZ(beamDirection beam); void setBeamAZ(); /// getters/setters for beam energy per charge and per nucleon float getBeamEnergyPerNucleon(beamDirection beam) const { return mBeamEnergyPerZ.second * getBeamZoverA(beam); } + float getBeamEnergyPerNucleon(int beam) const { return mBeamEnergyPerZ.second * getBeamZoverA(beam); } + float getBeamEnergyPerNucleonInGeV(beamDirection beam) const { return getBeamEnergyPerZinGeV() * getBeamZoverA(beam); } + float getBeamEnergyPerNucleonInGeV(int beam) const { return getBeamEnergyPerZinGeV() * getBeamZoverA(beam); } /// calculate center of mass energy per nucleon collision float getSqrtS() const; /// helper function for BunchFilling @@ -119,6 +126,14 @@ inline float GRPLHCIFData::getBeamZoverA(beamDirection b) const return a ? getBeamZ(b) / static_cast(a) : 0.f; } +//______________________________________________ +inline float GRPLHCIFData::getBeamZoverA(int b) const +{ + // Z/A of beam 0 or 1 + int a = getBeamA(b); + return a ? getBeamZ(b) / static_cast(a) : 0.f; +} + } // namespace parameters } // namespace o2 #endif diff --git a/DataFormats/Parameters/include/DataFormatsParameters/GRPMagField.h b/DataFormats/Parameters/include/DataFormatsParameters/GRPMagField.h index e11f9946e8e35..f9749c2b2a36f 100644 --- a/DataFormats/Parameters/include/DataFormatsParameters/GRPMagField.h +++ b/DataFormats/Parameters/include/DataFormatsParameters/GRPMagField.h @@ -34,8 +34,18 @@ class GRPMagField ~GRPMagField() = default; /// getters/setters for magnets currents - o2::units::Current_t getL3Current() const { return mL3Current; } - o2::units::Current_t getDipoleCurrent() const { return mDipoleCurrent; } + o2::units::Current_t getL3Current() const + { + static float v = checkL3Override(); + return v == NOOVERRIDEVAL ? mL3Current : v; + } + + o2::units::Current_t getDipoleCurrent() const + { + static float v = checkDipoleOverride(); + return v == NOOVERRIDEVAL ? mDipoleCurrent : v; + } + bool getFieldUniformity() const { return mUniformField; } int8_t getNominalL3Field(); void setL3Current(o2::units::Current_t v) { mL3Current = v; } @@ -54,6 +64,10 @@ class GRPMagField int8_t mNominalL3Field = 0; //!< Nominal L3 field deduced from mL3Current bool mNominalL3FieldValid = false; //!< Has the field been computed (for caching) + static constexpr float NOOVERRIDEVAL = 1e99; + static float checkL3Override(); + static float checkDipoleOverride(); + ClassDefNV(GRPMagField, 2); }; @@ -62,7 +76,7 @@ inline int8_t GRPMagField::getNominalL3Field() // compute nominal L3 field in kG if (mNominalL3FieldValid == false) { - mNominalL3Field = std::lround(5.f * mL3Current / 30000.f); + mNominalL3Field = std::lround(5.f * getL3Current() / 30000.f); mNominalL3FieldValid = true; } return mNominalL3Field; diff --git a/DataFormats/Parameters/src/AggregatedRunInfo.cxx b/DataFormats/Parameters/src/AggregatedRunInfo.cxx new file mode 100644 index 0000000000000..40402a33af68b --- /dev/null +++ b/DataFormats/Parameters/src/AggregatedRunInfo.cxx @@ -0,0 +1,151 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AggregatedRunInfo.cxx +/// \author sandro.wenzel@cern.ch + +#include "DataFormatsParameters/AggregatedRunInfo.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsParameters/GRPECSObject.h" +#include "DataFormatsParameters/GRPLHCIFData.h" +#include "CommonConstants/LHCConstants.h" +#include "Framework/Logger.h" +#include + +using namespace o2::parameters; + +o2::parameters::AggregatedRunInfo AggregatedRunInfo::buildAggregatedRunInfo_DATA(o2::ccdb::CCDBManagerInstance& ccdb, int runnumber) +{ + // TODO: could think about caching results per runnumber to + // avoid going to CCDB multiple times ---> but should be done inside the CCDBManagerInstance + + // we calculate the first orbit of a run based on sor (start-of-run) and eor + // we obtain these by calling getRunDuration + auto [sor, eor] = ccdb.getRunDuration(runnumber); + + // determine a good timestamp to query OrbitReset for this run + // --> the middle of the run is very appropriate and safer than just sor + auto run_mid_timestamp = sor + (eor - sor) / 2; + + // query the time of the orbit reset (when orbit is defined to be 0) + auto ctpx = ccdb.getForTimeStamp>("CTP/Calib/OrbitReset", run_mid_timestamp); + int64_t tsOrbitReset = (*ctpx)[0]; // us + + // get timeframe length from GRPECS + std::map metadata; + metadata["runNumber"] = Form("%d", runnumber); + auto grpecs = ccdb.getSpecific("GLO/Config/GRPECS", run_mid_timestamp, metadata); + auto grplhcif = ccdb.getSpecific("GLO/Config/GRPLHCIF", run_mid_timestamp); // no run metadata here + bool oldFatalState = ccdb.getFatalWhenNull(); + ccdb.setFatalWhenNull(false); + auto ctp_first_run_orbit = ccdb.getForTimeStamp>("CTP/Calib/FirstRunOrbit", run_mid_timestamp); + ccdb.setFatalWhenNull(oldFatalState); + return buildAggregatedRunInfo(runnumber, sor, eor, tsOrbitReset, grpecs, ctp_first_run_orbit, grplhcif); +} + +o2::parameters::AggregatedRunInfo AggregatedRunInfo::buildAggregatedRunInfo(int runnumber, long sorMS, long eorMS, long orbitResetMUS, const o2::parameters::GRPECSObject* grpecs, const std::vector* ctfFirstRunOrbitVec, const o2::parameters::GRPLHCIFData* grplhcif) +{ + auto nOrbitsPerTF = grpecs->getNHBFPerTF(); + // calculate SOR/EOR orbits + int64_t orbitSOR = -1; + if (ctfFirstRunOrbitVec && ctfFirstRunOrbitVec->size() >= 3) { // if we have CTP first run orbit available, we should use it + int64_t creation_timeIGNORED = (*ctfFirstRunOrbitVec)[0]; // do not use CTP start of run time! + int64_t ctp_run_number = (*ctfFirstRunOrbitVec)[1]; + int64_t ctp_orbitSOR = (*ctfFirstRunOrbitVec)[2]; + if (creation_timeIGNORED == -1 && ctp_run_number == -1 && ctp_orbitSOR == -1) { + LOGP(warn, "Default dummy CTP/Calib/FirstRunOrbit was provides, ignoring"); + } else if (ctp_run_number == runnumber) { + orbitSOR = ctp_orbitSOR; + auto sor_new = (int64_t)((orbitResetMUS + ctp_orbitSOR * o2::constants::lhc::LHCOrbitMUS) / 1000.); + if (sor_new != sorMS) { + LOGP(warn, "Adjusting SOR from {} to {}", sorMS, sor_new); + sorMS = sor_new; + } + } else { + LOGP(error, "AggregatedRunInfo: run number inconsistency found (asked: {} vs CTP found: {}, ignoring", runnumber, ctp_run_number); + } + } + int64_t orbitEOR = (eorMS * 1000 - orbitResetMUS) / o2::constants::lhc::LHCOrbitMUS; + if (runnumber > 523897) { // condition was introduced starting from LHC22o + orbitEOR = orbitEOR / nOrbitsPerTF * nOrbitsPerTF; + } + if (orbitSOR < 0) { // extract from SOR + orbitSOR = (sorMS * 1000 - orbitResetMUS) / o2::constants::lhc::LHCOrbitMUS; + if (runnumber > 523897) { + orbitSOR = (orbitSOR / nOrbitsPerTF + 1) * nOrbitsPerTF; + } + } + return AggregatedRunInfo{runnumber, sorMS, eorMS, nOrbitsPerTF, orbitResetMUS, orbitSOR, orbitEOR, grpecs, grplhcif}; +} + +namespace +{ + +// get path where to find MC production info +std::string getFullPath_MC(std::string const& username, std::string const& lpm_prod_tag) +{ + // construct the path where to lookup + std::string path = "/Users/" + std::string(1, username[0]) + "/" + username; + std::string fullpath = path + "/" + "MCProdInfo/" + lpm_prod_tag; + return fullpath; +} + +} // namespace + +std::map AggregatedRunInfo::getMCProdInfo(o2::ccdb::CCDBManagerInstance& ccdb, + int run_number, + std::string const& lpm_prod_tag, + std::string const& username) +{ + std::map metaDataFilter; + metaDataFilter["LPMProductionTag"] = lpm_prod_tag; + + // fetch the meta information for MC productions + auto header_data = ccdb.getCCDBAccessor().retrieveHeaders(getFullPath_MC(username, lpm_prod_tag), metaDataFilter, run_number); + return header_data; +} + +void AggregatedRunInfo::adjust_from_MC(o2::ccdb::CCDBManagerInstance& ccdb, + int run_number, + std::string const& lpm_prod_tag, + std::string const& username) +{ + auto header_data = AggregatedRunInfo::getMCProdInfo(ccdb, run_number, lpm_prod_tag, username); + + // adjust timeframe length if we find entry for MC production + auto iter = header_data.find("OrbitsPerTF"); + if (iter != header_data.end()) { + auto mc_orbitsPerTF = std::stoi(iter->second); + if (mc_orbitsPerTF != orbitsPerTF) { + LOG(info) << "Adjusting OrbitsPerTF from " << orbitsPerTF << " to " << mc_orbitsPerTF << " based on differing MC info"; + orbitsPerTF = mc_orbitsPerTF; + } + } else { + LOG(warn) << "No OrbitsPerTF information found for MC production " << lpm_prod_tag << " and run number " << run_number; + } +} + +AggregatedRunInfo AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::CCDBManagerInstance& ccdb, int run_number, std::string const& lpm_prod_tag, std::string const& username) +{ + // (a) lookup the AggregatedRunInfo for the data run + // (b) modify/overwrite the info object with MC specific settings if lpm_prod_tag is given + + auto original_info = buildAggregatedRunInfo_DATA(ccdb, run_number); + + if (lpm_prod_tag.size() == 0) { + return original_info; + } + + // in this case we adjust the info from MC + original_info.adjust_from_MC(ccdb, run_number, lpm_prod_tag, username); + + return original_info; +} diff --git a/DataFormats/Parameters/src/GRPLHCIFData.cxx b/DataFormats/Parameters/src/GRPLHCIFData.cxx index dcbca0932cf69..d39569f79376b 100644 --- a/DataFormats/Parameters/src/GRPLHCIFData.cxx +++ b/DataFormats/Parameters/src/GRPLHCIFData.cxx @@ -28,6 +28,8 @@ using namespace o2::constants::lhc; const std::unordered_map GRPLHCIFData::mZtoA = { {1, 1}, + {8, 16}, + {10, 20}, {82, 208}}; //_______________________________________________ @@ -60,8 +62,8 @@ void GRPLHCIFData::setBeamAZ() float GRPLHCIFData::getSqrtS() const { // get center of mass energy - double e0 = getBeamEnergyPerNucleon(BeamC); - double e1 = getBeamEnergyPerNucleon(BeamA); + double e0 = getBeamEnergyPerNucleonInGeV(BeamC); + double e1 = getBeamEnergyPerNucleonInGeV(BeamA); if (e0 <= MassProton || e1 <= MassProton) { return 0.f; } diff --git a/DataFormats/Parameters/src/GRPMagField.cxx b/DataFormats/Parameters/src/GRPMagField.cxx index e3dd412f0d118..8f5ae0b414998 100644 --- a/DataFormats/Parameters/src/GRPMagField.cxx +++ b/DataFormats/Parameters/src/GRPMagField.cxx @@ -41,3 +41,25 @@ void GRPMagField::print() const { printf("magnet currents (A) L3 = %.3f, Dipole = %.f; uniformity = %s\n", getL3Current(), getDipoleCurrent(), mUniformField ? "true" : "false"); } + +o2::units::Current_t GRPMagField::checkDipoleOverride() +{ + static float v = getenv("O2_OVERRIDE_DIPOLE_CURRENT") ? atof(getenv("O2_OVERRIDE_DIPOLE_CURRENT")) : NOOVERRIDEVAL; + static bool alarmShown = false; + if (v != NOOVERRIDEVAL && !alarmShown) { + LOGP(error, "Overriding DIPOLE current to {}", v); + alarmShown = true; + } + return v; +} + +o2::units::Current_t GRPMagField::checkL3Override() +{ + static float v = getenv("O2_OVERRIDE_L3_CURRENT") ? atof(getenv("O2_OVERRIDE_L3_CURRENT")) : NOOVERRIDEVAL; + static bool alarmShown = false; + if (v != NOOVERRIDEVAL && !alarmShown) { + LOGP(error, "Overriding L3 current to {}", v); + alarmShown = true; + } + return v; +} diff --git a/DataFormats/Parameters/src/GRPTool.cxx b/DataFormats/Parameters/src/GRPTool.cxx index 86e4b5002dc05..e7561e6fc1ef6 100644 --- a/DataFormats/Parameters/src/GRPTool.cxx +++ b/DataFormats/Parameters/src/GRPTool.cxx @@ -57,13 +57,15 @@ struct Options { std::string outprefix = ""; std::string fieldstring = ""; std::string bcPatternFile = ""; - bool print = false; // whether to print outcome of GRP operation + bool print = false; // whether to print outcome of GRP operation bool lhciffromccdb = false; // whether only to take GRPLHCIF from CCDB std::string publishto = ""; std::string ccdbhost = "http://alice-ccdb.cern.ch"; bool isRun5 = false; // whether or not this is supposed to be a Run5 detector configuration std::string vertex = "ccdb"; std::string configKeyValues = ""; + uint64_t timestamp = 0; + std::string detectorList; // detector layout }; namespace @@ -77,10 +79,16 @@ class CCDBHelper auto soreor = o2::ccdb::BasicCCDBManager::getRunDuration(api, opts.run); runStart = soreor.first; runEnd = soreor.second; + if (opts.timestamp > 0) { + timestamp = opts.timestamp; + } else { + timestamp = runStart; + } } o2::ccdb::CcdbApi api; uint64_t runStart = -1; uint64_t runEnd = -1; + uint64_t timestamp = -1; }; } // namespace @@ -198,7 +206,7 @@ bool publish(std::string const& filename, std::string const& path, std::string C try { o2::utils::createDirectoriesIfAbsent(targetdir); } catch (std::exception e) { - LOGP(error, fmt::format("Could not create local snapshot cache directory {}, reason: {}", targetdir, e.what())); + LOGP(error, "Could not create local snapshot cache directory {}, reason: {}", targetdir, e.what()); return false; } @@ -218,7 +226,7 @@ bool anchor_GRPs(Options const& opts, std::vector const& paths = {" gCCDBWrapper = std::move(std::make_unique(opts)); } // fix the timestamp early - uint64_t runStart = gCCDBWrapper->runStart; + uint64_t timestamp = gCCDBWrapper->timestamp; const bool preserve_path = true; const std::string filename("snapshot.root"); @@ -226,7 +234,7 @@ bool anchor_GRPs(Options const& opts, std::vector const& paths = {" bool success = true; for (auto& p : paths) { LOG(info) << "Fetching " << p << " from CCDB"; - success &= gCCDBWrapper->api.retrieveBlob(p, opts.publishto, filter, runStart, preserve_path, filename); + success &= gCCDBWrapper->api.retrieveBlob(p, opts.publishto, filter, timestamp, preserve_path, filename); } return success; } @@ -304,10 +312,12 @@ bool create_GRPs(Options const& opts) auto soreor = ccdbmgr.getRunDuration(opts.run); runStart = soreor.first; grp.setTimeStart(runStart); - grp.setTimeEnd(runStart + 3600000); + grp.setTimeEnd(soreor.second); grp.setNHBFPerTF(opts.orbitsPerTF); std::vector modules{}; - o2::conf::SimConfig::determineActiveModules(opts.readout, std::vector(), modules, opts.isRun5); + if (!o2::conf::SimConfig::determineActiveModulesList(opts.detectorList, opts.readout, std::vector(), modules)) { + return false; + } std::vector readout{}; o2::conf::SimConfig::determineReadoutDetectors(modules, std::vector(), opts.skipreadout, readout); for (auto& detstr : readout) { @@ -553,6 +563,7 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) // ls command has the following options: bpo::options_description desc("create options"); + desc.add_options()("detectorList", bpo::value(&optvalues.detectorList)->default_value("ALICE2"), "Pick a specific version of ALICE, for specifics check the o2-sim description"); desc.add_options()("readoutDets", bpo::value>(&optvalues.readout)->multitoken()->default_value(std::vector({"all"}), "all Run3 detectors"), "Detector list to be readout/active"); desc.add_options()("skipReadout", bpo::value>(&optvalues.skipreadout)->multitoken()->default_value(std::vector(), "nothing skipped"), "list of inactive detectors (precendence over --readout)"); desc.add_options()("run", bpo::value(&optvalues.run)->default_value(-1), "Run number"); @@ -563,8 +574,9 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) desc.add_options()("lhcif-CCDB", "take GRPLHCIF directly from CCDB"); desc.add_options()("print", "print resulting GRPs"); desc.add_options()("publishto", bpo::value(&optvalues.publishto)->default_value(""), "Base path under which GRP objects should be published on disc. This path can serve as lookup for CCDB queries of the GRP objects."); - desc.add_options()("isRun5", bpo::bool_switch(&optvalues.isRun5), "Whether or not to expect a Run5 detector configuration."); + desc.add_options()("isRun5", bpo::bool_switch(&optvalues.isRun5), "Whether or not to expect a Run5 detector configuration. (deprecated, use detectorList option)"); desc.add_options()("vertex", bpo::value(&optvalues.vertex)->default_value("ccdb"), "How the vertex is to be initialized. Default is CCDB. Alternative is \"Diamond\" which is constructing the mean vertex from the Diamond param via the configKeyValues path"); + desc.add_options()("timestamp", bpo::value(&optvalues.timestamp)->default_value(0), "Force timestamp to be used (useful when anchoring)"); desc.add_options()("configKeyValues", bpo::value(&optvalues.configKeyValues)->default_value(""), "Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')"); if (!subparse(desc, vm, "createGRPs")) { return false; diff --git a/DataFormats/Parameters/src/ParametersDataLinkDef.h b/DataFormats/Parameters/src/ParametersDataLinkDef.h index e4817bd443302..d2cbee2049c52 100644 --- a/DataFormats/Parameters/src/ParametersDataLinkDef.h +++ b/DataFormats/Parameters/src/ParametersDataLinkDef.h @@ -30,5 +30,6 @@ #pragma link C++ class o2::parameters::GRPMagField + ; #pragma link C++ class std::unordered_map < unsigned int, unsigned int> + ; #pragma link C++ class std::pair < unsigned long, std::string> + ; +#pragma link C++ struct o2::parameters::AggregatedRunInfo + ; #endif diff --git a/DataFormats/QualityControl/CMakeLists.txt b/DataFormats/QualityControl/CMakeLists.txt index 0d17d0d1d0f9e..c0f40a88f88c1 100644 --- a/DataFormats/QualityControl/CMakeLists.txt +++ b/DataFormats/QualityControl/CMakeLists.txt @@ -10,67 +10,65 @@ # or submit itself to any jurisdiction. file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include) -### Prepare the list of methods in FlagReasonFactory -file(READ "etc/flagReasons.csv" CSV_FLAG_REASONS) +### Prepare the list of methods in FlagTypeFactory +file(READ "etc/flagTypes.csv" CSV_FLAG_TYPES) # delete the CSV file header -string(REPLACE \"id\",\"method\",\"name\",\"bad\",\"obsolete\" "" CSV_FLAG_REASONS ${CSV_FLAG_REASONS}) +string(REPLACE \"id\",\"method\",\"name\",\"bad\",\"obsolete\" "" CSV_FLAG_TYPES ${CSV_FLAG_TYPES}) # detects if there is obsolete flag '1' in the last column, adds [[deprecated]] if so and retains the rest of the string string(REGEX REPLACE \([0-9]+,\".[^\"]*.\",.[^\"]*.,[0-1]\),1 "[[deprecated]] \\1,1" - CSV_FLAG_REASONS - ${CSV_FLAG_REASONS}) + CSV_FLAG_TYPES + ${CSV_FLAG_TYPES}) # replaces the flag reason entry with a c++ method to create it string(REGEX REPLACE \([0-9]+\),\"\(.[^\"]*.\)\",\(.[^\"]*.\),\([0-1]\),[0-1] - "static FlagReason \\2\(\) { return { static_cast\(\\1\), \\3, static_cast\(\\4\) }; }" - CSV_FLAG_REASONS - ${CSV_FLAG_REASONS}) + "static FlagType \\2\(\) { return { static_cast\(\\1\), \\3, static_cast\(\\4\) }; }" + CSV_FLAG_TYPES + ${CSV_FLAG_TYPES}) # put the method lists inside the template -configure_file("include/DataFormatsQualityControl/FlagReasonFactory.h.in" - "${CMAKE_CURRENT_BINARY_DIR}/include/DataFormatsQualityControl/FlagReasonFactory.h" +configure_file("include/DataFormatsQualityControl/FlagTypeFactory.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/include/DataFormatsQualityControl/FlagTypeFactory.h" @ONLY) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/DataFormatsQualityControl/FlagReasonFactory.h" +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/DataFormatsQualityControl/FlagTypeFactory.h" DESTINATION include/DataFormatsQualityControl) o2_add_library(DataFormatsQualityControl - SOURCES src/FlagReasons.cxx - src/TimeRangeFlag.cxx - src/TimeRangeFlagCollection.cxx - PUBLIC_LINK_LIBRARIES O2::Headers - O2::FrameworkLogger + SOURCES src/FlagType.cxx + src/QualityControlFlag.cxx + src/QualityControlFlagCollection.cxx + PUBLIC_LINK_LIBRARIES O2::FrameworkLogger O2::DetectorsCommonDataFormats PUBLIC_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/include include ) o2_target_root_dictionary(DataFormatsQualityControl - HEADERS include/DataFormatsQualityControl/FlagReasons.h - include/DataFormatsQualityControl/TimeRangeFlag.h - include/DataFormatsQualityControl/TimeRangeFlagCollection.h) - + HEADERS include/DataFormatsQualityControl/FlagType.h + include/DataFormatsQualityControl/QualityControlFlag.h + include/DataFormatsQualityControl/QualityControlFlagCollection.h) if(BUILD_TESTING) -o2_add_test(FlagReasons - SOURCES test/testFlagReasons.cxx +o2_add_test(FlagTypes + SOURCES test/testFlagTypes.cxx COMPONENT_NAME DataFormatsQualityControl PUBLIC_LINK_LIBRARIES O2::DataFormatsQualityControl - TARGETVARNAME flagreasons) + TARGETVARNAME flagtypes) -target_include_directories(${flagreasons} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include) +target_include_directories(${flagtypes} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include) -o2_add_test(TimeRangeFlag - SOURCES test/testTimeRangeFlag.cxx +o2_add_test(QualityControlFlag + SOURCES test/testQualityControlFlag.cxx COMPONENT_NAME DataFormatsQualityControl PUBLIC_LINK_LIBRARIES O2::DataFormatsQualityControl - TARGETVARNAME timerangeflag) + TARGETVARNAME qualitycontrolflag) -target_include_directories(${timerangeflag} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) +target_include_directories(${qualitycontrolflag} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) -o2_add_test(TimeRangeFlagCollection - SOURCES test/testTimeRangeFlagCollection.cxx +o2_add_test(QualityControlFlagCollection + SOURCES test/testQualityControlFlagCollection.cxx COMPONENT_NAME DataFormatsQualityControl PUBLIC_LINK_LIBRARIES O2::DataFormatsQualityControl - TARGETVARNAME timerangeflagcollection) + TARGETVARNAME qualitycontrolflagcollection) -target_include_directories(${timerangeflagcollection} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) +target_include_directories(${qualitycontrolflagcollection} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) endif() diff --git a/DataFormats/QualityControl/README.md b/DataFormats/QualityControl/README.md index ededa028c7b93..33821319b7316 100644 --- a/DataFormats/QualityControl/README.md +++ b/DataFormats/QualityControl/README.md @@ -1,63 +1,100 @@ \page refDataFormatsQualityControl Data Formats Quality Control -Data formats for tagging good quality data for analysis. +## Tagging Good Quality Data for Processing and Analysis -# Flagging time ranges -## General idea -* Each detector has its own CCDB (QCDB) entry - TimeRangeFlagCollection -* The CCDB Entry validity will be run or fill, depending on the final data taking granularity -* Each entry can define sub ranges (TimeRangeFlags) of certain data characteristics (FlagReasons) inside the CCDB Entry validity -* Flags are defined in a CSV master file, they are used to derive the Data Tags - the final filters for good quality data during analysis. -* Data Tag are stored as CCDB entries. They might require different detectors and may suppress different flags dependent on the analysis type. - -## Implementation - -[Flag Reason](include/DataFormatsQualityControl/FlagReasons.h) is defined with an identifier number, a name and a 'bad quality' determinant. -The latter decides if such a flag should mark the data quality as bad by default. -FlagReasons can be created only with FlagReasonFactory, so that the list of available reasons is common and centralised. -For example: -``` -id: 10 -name: Limited Acceptance -bad: true -``` -The list of available FlagReasons is defined in [etc/flagReasons.csv](etc/flagReasons.csv), which is used to generate the corresponding list of methods in FlagReasonFactory. -The existing flags should never be modified, except for marking them as obsolete. -New flags may be added via pull requests if there is no flag which conveys a similar meaning. +This document outlines the data formats used for tagging good quality data for further processing and analysis. +They allow us to describe problems affecting the data in concrete time intervals. +Using this information, data can be filtered out according to criteria specific to a given analysis type. + +### Data Quality Control workflow + +Data quality is determined through two methods: + +1. **Automated Checks:** The Quality Control framework runs Checks which may return quality Flags. +2. **Manual Review:** Detector experts review data using the Run Condition Table (RCT) and can modify or add Flags. + +Both methods utilize the same data format for Flags. +During processing (both synchronous and asynchronous), Checks produce Qualities and associate them with Flags. +The Quality Control framework then transmits these Flags to the RCT through a gRPC interface (more details in QC repository documentation). +Detector experts can then review the automatically generated Flags and make any necessary modifications or additions directly in the RCT. + +### Quality Control Flag Structure + +A [Quality Control Flag](include/DataFormatsQualityControl/QualityControlFlag.h) consists of the following elements: + +* **Flag Type:** This identifies the specific issue the flag represents. (More details below) +* **Time Range:** This defines the time period affected by the flag. +* **Comment (Optional):** This allows human-readable explanations for assigning the flag. +* **Source String:** This identifies the entity (person or software module) that created the flag. + +**Example:** -With [TimeRangeFlags](include/DataFormatsQualityControl/TimeRangeFlag.h) we can define the time range of a chosen FlagReason, add an additional comment and specify the source of this flag. -For example: ``` -start: 1612707603626 +flag: Limited Acceptance MC Reproducible +start: 1612707603626 end: 1613999652000 -flag: Limited Acceptance comment: Sector B in TPC inactive -source: o2::quality_control_modules::tpc::ClustersCheck +source: TPC/Clusters Check ``` -[TimeRangeFlagCollection](include/DataFormatsQualityControl/TimeRangeFlagCollection.h) contains all TimeRangeFlags for the validity range (run or fill). -TimeRangeFlags may overlap, e.g. if they use different FlagReasons and they are sorted by their start time. -If certain period does not include any TimeRangeFlags with *bad* FlagReasons, then the data quality can be considered as good. -The [TimeRangeFlagCollection test](test/testTimeRangeFlagCollection.cxx) shows the usage example. +### Flag Types + +[Flag Types](include/DataFormatsQualityControl/FlagType.h) define the specific categories of issues represented by flags. +Each Flag Type has the following attributes: + +* **Identifier Number:** A unique numerical ID for the Flag Type. +* **Name:** A human-readable name describing the issue. +* **"Bad Quality" Determinant:** This boolean constant indicates whether the type inherently signifies bad data quality. + +#### Creating and Managing Flag Types + +* **FlagTypeFactory** ensures a centralized and consistent list of available Flag Types. + New Flags can only be created through this factory. +* **[flagTypes.csv](etc/flagTypes.csv)** defines the existing Flag Types, including their ID, name, and "bad quality" determinant, factory method name and a switch to deprecate a flag. + The table serves as the source to automatically generate the corresponding methods in FlagTypeFactory. +* **Adding new Flag Types:** If a new issue requires a flag not currently defined, propose the addition by contacting the async QC coordinators. + They have the authority to add new Flag Types to the RCT. + These changes will then be reflected in the [flagTypes.csv](etc/flagTypes.csv) file through a pull request. + Any proposals for new Flag Types should describe the effects on usability of data from analyzer point of view and they should not be detector-specific unless well-argumented. +* **Modification of existing Flag Types:** Existing Flag Types should not be modified in terms of their definition. + Instead, one may create a new Flag Type and mark the existing one as obsolete in the CSV table. + This will add the `[[ deprecated ]]` attribute to the corresponding method. -TimeRangeFlagCollections are supposed to be created automatically with QC Post-processing Tasks based on Quality Objects created by QC Checks. -However, they might be created manually by QA experts as well. -The procedure to do that has to be defined. +#### Currently available Flag Types -## TODO -* Define the complete list of available Flag Reasons -* implement CCDB storage and access - - define CCDB storage place e.g. - * `/QC/QualityFlags` - * `Analysis/QualityFlags/` -* Data Tags Definitions and Data Tags +This section details the currently available Flag Types and provides a brief explanation of their intended use cases. -### Notes on plans for Data Tags +* **Good:** a Check or an expert sees nothing wrong with given time interval, but would like to add a comment. + Note that the absence of any flag for a run implies good data quality. + Thus, there is no need to mark it explicitly as such by using this flag type. +* **No Detector Data:** a complete and unexpected absence of data for a specific detector. +* **Limited Acceptance MC Not Reproducible:** a part of a detector did not acquire good data and this condition cannot be reproduced in Monte Carlo. + If an automated Check cannot determine MC reproducibility, it should default to "Not Reproducible" for later expert review. +* **Limited Acceptance MC Reproducible:** a part of a detector did not acquire good data, but this condition can be reproduced in Monte Carlo. +* **Bad Tracking:** analyses relying on accurate track reconstruction should not use this data. +* **Bad PID:** analyses relying on correct identification of all kinds of tracked particles should not use this data. +* **Bad Hadron PID:** analyses relying on correct hadron identification should not use this data. +* **Bad Electron PID:** analyses relying on correct electron identification should not use this data. +* **Bad Photon Calorimetry:** analyses relying on correct photon calorimetry should not use this data. +* **Bad EMCalorimetry:** analyses relying on correct electromagnetic calorimetry should not use this data. +* **Unknown:** the exact impact of an issue on the data is unclear, but it's likely bad. + Treat data with this flag with caution until further investigation. +* **Unknown Quality:** the quality of data could not be determined definitively. +* **Invalid:** there was an issue with processing the flags. + +## Usage in Analysis framework (plans, wishlist) + +## General idea +* RCT exports a read-only copy of the flags for each detector, run and pass combination in the CCDB. +* The Analysis framework uses the flags and user selection criteria to provide their Analysis Task with data matching these criteria. + **Data Tags** are the structures defining the good time intervals for the provided criteria. + +## Notes on plans for Data Tags Data Tag definition has: * name -* global suppression list for bad flag reasons (applies to all detectors) -* global requirement list for not bad flag reasons +* global suppression list for bad flag types (applies to all detectors) +* global requirement list for not bad flag types * list of needed detectors, * list of suppression list and requirement list specific to detectors @@ -94,10 +131,3 @@ Example configurations } ] ``` - -## Wishlist / Ideas -* executable to add flags to the flag store -* executable to extrags the flag store -* summary of all masks for one TimeRangeFlagCollection -* functionality to extract flags for a specific detector (from CCDB) -* cut class to specify detectors and flags which to exclude \ No newline at end of file diff --git a/DataFormats/QualityControl/etc/flagReasons.csv b/DataFormats/QualityControl/etc/flagReasons.csv deleted file mode 100644 index 23acc1b3000fa..0000000000000 --- a/DataFormats/QualityControl/etc/flagReasons.csv +++ /dev/null @@ -1,15 +0,0 @@ -"id","method","name","bad","obsolete" -1,"Unknown","Unknown",1,0 -2,"UnknownQuality","Unknown Quality",1,0 -3,"CertifiedByExpert","Certified by Expert",0,0 -10,"NoDetectorData","No Detector Data",1,0 -11,"LimitedAcceptance","Limited acceptance",1,0 -12,"BadPID","Bad PID",1,0 -13,"BadTracking","Bad Tracking",1,0 -14,"BadHadronPID","Bad Hadron PID",1,0 -15,"BadElectronPID","Bad Electron PID",1,0 -16,"BadEMCalorimetry","Bad EM Calorimetry",1,0 -17,"BadPhotonCalorimetry","Bad Photon Calorimetry",1,0 -65500,"ObsoleteFlagExample","Obsolete flag example",1,1 -65501,"NotBadFlagExample","Not bad flag example",0,0 -65535,"Invalid","Invalid",1,0 diff --git a/DataFormats/QualityControl/etc/flagTypes.csv b/DataFormats/QualityControl/etc/flagTypes.csv new file mode 100644 index 0000000000000..97107cd15a9e7 --- /dev/null +++ b/DataFormats/QualityControl/etc/flagTypes.csv @@ -0,0 +1,14 @@ +"id","method","name","bad","obsolete" +1,"UnknownQuality","Unknown Quality",1,0 +3,"NoDetectorData","No Detector Data",1,0 +4,"LimitedAcceptanceMCNotReproducible","Limited Acceptance MC Not Reproducible",1,0 +5,"LimitedAcceptanceMCReproducible","Limited Acceptance MC Reproducible",1,0 +6,"BadPID","Bad PID",1,0 +7,"BadTracking","Bad Tracking",1,0 +8,"BadHadronPID","Bad Hadron PID",1,0 +9,"Good","Good",0,0 +10,"Invalid","Invalid",1,0 +11,"BadElectronPID","Bad Electron PID",1,0 +12,"BadEMCalorimetry","Bad EM Calorimetry",1,0 +13,"BadPhotonCalorimetry","Bad Photon Calorimetry",1,0 +14,"Unknown","Unknown",1,0 diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasonFactory.h.in b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasonFactory.h.in deleted file mode 100644 index 59bd7b4c5628f..0000000000000 --- a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasonFactory.h.in +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file FlagReasonFactory.h -/// \brief A class to create FlagReasons based on the predefined CSV list. -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -#ifndef O2_FLAGREASONFACTORY_H -#define O2_FLAGREASONFACTORY_H - -#include "DataFormatsQualityControl/FlagReasons.h" - -namespace o2::quality_control -{ - -class FlagReasonFactory { -public: -FlagReasonFactory() = delete; -@CSV_FLAG_REASONS@ -}; - -} // namespace o2::quality_control -#endif // O2_FLAGREASONFACTORY_H \ No newline at end of file diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasons.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasons.h deleted file mode 100644 index 67f5b4cc27cde..0000000000000 --- a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasons.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_ANALYSIS_FLAGREASONS -#define ALICEO2_ANALYSIS_FLAGREASONS - -/// \file FlagReasons.h -/// \brief classes keeping reasons for flagging time ranges -/// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -// STL -#include -#include - -// ROOT includes -#include - -namespace o2 -{ -namespace quality_control -{ - -class FlagReasonFactory; -class TimeRangeFlagCollection; - -class FlagReason -{ - private: - uint16_t mId; - std::string mName; - bool mBad; // if true, data should become bad by default - - // By making the constructor private and FlagReasons available only in the FlagReasonFactory - // we forbid to declare any flags in the user code. If you need a new FlagReason, please add it FlagReasonFactory. - private: - FlagReason(uint16_t id, const char* name, bool bad) : mId(id), mName(name), mBad(bad) {} - - public: - FlagReason(); - FlagReason& operator=(const FlagReason&) = default; - FlagReason(const FlagReason&) = default; - bool operator==(const FlagReason& rhs) const; - bool operator!=(const FlagReason& rhs) const; - bool operator<(const FlagReason& rhs) const; - bool operator>(const FlagReason& rhs) const; - - uint16_t getID() const { return mId; } - const std::string& getName() const { return mName; } - bool getBad() const { return mBad; } - - friend std::ostream& operator<<(std::ostream& os, FlagReason const& me); - friend class FlagReasonFactory; - friend class TimeRangeFlagCollection; - - ClassDefNV(FlagReason, 1); -}; - -} // namespace quality_control -} // namespace o2 - -// TODO: remove once we include it in QualityControl -#include "DataFormatsQualityControl/FlagReasonFactory.h" - -#endif diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagType.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagType.h new file mode 100644 index 0000000000000..94e4a301a1784 --- /dev/null +++ b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagType.h @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_QUALITYCONTROL_FLAGTYPE_H +#define O2_QUALITYCONTROL_FLAGTYPE_H + +// STL +#include +#include + +// ROOT includes +#include + +namespace o2 +{ +namespace quality_control +{ + +class FlagTypeFactory; +class QualityControlFlagCollection; + +class FlagType +{ + private: + uint16_t mId; + std::string mName; + bool mBad; // if true, data should become bad by default + + // By making the constructor private and FlagTypes available only in the FlagTypeFactory + // we forbid to declare any flags in the user code. If you need a new FlagType, please add it FlagTypeFactory. + private: + FlagType(uint16_t id, const char* name, bool bad) : mId(id), mName(name), mBad(bad) {} + + public: + FlagType(); + FlagType& operator=(const FlagType&) = default; + FlagType(const FlagType&) = default; + bool operator==(const FlagType& rhs) const; + bool operator!=(const FlagType& rhs) const; + bool operator<(const FlagType& rhs) const; + bool operator>(const FlagType& rhs) const; + + uint16_t getID() const { return mId; } + const std::string& getName() const { return mName; } + bool getBad() const { return mBad; } + + friend std::ostream& operator<<(std::ostream& os, FlagType const& me); + friend class FlagTypeFactory; + friend class QualityControlFlagCollection; + + ClassDefNV(FlagType, 1); +}; + +} // namespace quality_control +} // namespace o2 +#endif // O2_QUALITYCONTROL_FLAGTYPE_H diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagTypeFactory.h.in b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagTypeFactory.h.in new file mode 100644 index 0000000000000..38b6c463d239f --- /dev/null +++ b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagTypeFactory.h.in @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FlagTypeFactory.h +/// \brief A class to create FlagTypes based on the predefined CSV list. +/// \author Piotr Konopka, piotr.jan.konopka@cern.ch + +#ifndef O2_FLAGTYPEFACTORY_H +#define O2_FLAGTYPEFACTORY_H + +#include "DataFormatsQualityControl/FlagType.h" + +namespace o2::quality_control +{ + +class FlagTypeFactory { +public: +FlagTypeFactory() = delete; +@CSV_FLAG_TYPES@ +}; + +} // namespace o2::quality_control +#endif // O2_FLAGTYPEFACTORY_H \ No newline at end of file diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlag.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlag.h new file mode 100644 index 0000000000000..9c38952562483 --- /dev/null +++ b/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlag.h @@ -0,0 +1,88 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_QUALITYCONTROL_QCFLAG_H +#define O2_QUALITYCONTROL_QCFLAG_H + +/// \file QualityControlFlag.h +/// \brief Class to define a flag type with a time range and comments +/// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de +/// \author Piotr Konopka, piotr.jan.konopka@cern.ch + +// System includes +#include +#include + +// ROOT includes +#include + +#include + +#include "DataFormatsQualityControl/FlagType.h" +#include "DataFormatsQualityControl/FlagTypeFactory.h" + +namespace o2 +{ +namespace quality_control +{ + +/// \class QualityControlFlag +/// A Class for associating a bit mask with a time range +class QualityControlFlag +{ + public: + using time_type = uint64_t; + using RangeInterval = o2::math_utils::detail::Bracket; + + QualityControlFlag() = default; + QualityControlFlag(QualityControlFlag const&) = default; + QualityControlFlag(time_type start, time_type end, FlagType flag, std::string comment = "", std::string source = "Unknown"); + + time_type getStart() const { return mInterval.getMin(); } + time_type getEnd() const { return mInterval.getMax(); } + RangeInterval& getInterval() { return mInterval; } + const RangeInterval getInterval() const { return mInterval; } + FlagType getFlag() const { return mFlag; } + const std::string& getComment() const { return mComment; } + const std::string& getSource() const { return mSource; } + + void setStart(time_type start) { mInterval.setMin(start); } + void setEnd(time_type end) { mInterval.setMax(end); } + void setInterval(RangeInterval interval) { mInterval = interval; } + void setFlag(FlagType flag) { mFlag = flag; } + void setComment(const std::string& comment) { mComment = comment; } + void setSource(const std::string& source) { mSource = source; } + + /// equal operator + bool operator==(const QualityControlFlag& rhs) const; + + /// comparison operators + bool operator<(const QualityControlFlag& rhs) const; + bool operator>(const QualityControlFlag& rhs) const; + + /// write data to ostream + void streamTo(std::ostream& output) const; + + /// overloading output stream operator + friend std::ostream& operator<<(std::ostream& output, const QualityControlFlag& data); + + private: + RangeInterval mInterval = {}; ///< time interval of the masked range + FlagType mFlag; ///< flag reason + std::string mComment = ""; ///< optional comment, which may extend the reason + std::string mSource = "Unknown"; ///< optional (but encouraged) source of the flag (e.g. Qc Check name) + + ClassDefNV(QualityControlFlag, 1); +}; + +} // namespace quality_control +} // namespace o2 +#endif // O2_QUALITYCONTROL_QCFLAG_H diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlagCollection.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlagCollection.h new file mode 100644 index 0000000000000..120605f048cbe --- /dev/null +++ b/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlagCollection.h @@ -0,0 +1,102 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_QUALITYCONTROL_QCFLAGCOLLECTION_H +#define O2_QUALITYCONTROL_QCFLAGCOLLECTION_H +/// \file QualityControlFlagCollection.h +/// \brief classes for defining time ranges with a certain mask to be able to cut on +/// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de + +// System includes +#include + +// ROOT includes +#include "Rtypes.h" + +// O2 includes +#include "MathUtils/detail/Bracket.h" +#include "DataFormatsQualityControl/QualityControlFlag.h" + +// STL +#include + +namespace o2 +{ +namespace quality_control +{ + +/// \class QualityControlFlagCollection +/// A Class for keeping several time ranges of type QualityControlFlag +class QualityControlFlagCollection +{ + public: + using collection_t = std::set; + using time_type = uint64_t; + using RangeInterval = o2::math_utils::detail::Bracket; + + explicit QualityControlFlagCollection(std::string name, std::string detector = "TST", RangeInterval validityRange = {}, + int runNumber = 0, std::string periodName = "Invalid", std::string passName = "Invalid", + std::string provenance = "qc"); + + void insert(QualityControlFlag&&); + void insert(const QualityControlFlag&); + + size_t size() const; + + // moves all non-repeating QualityControlFlags from other to this + void merge(QualityControlFlagCollection& other); + // add all non-repeating QualityControlFlags from other to this. + void merge(const QualityControlFlagCollection& other); + + collection_t::const_iterator begin() const; + collection_t::const_iterator end() const; + + const std::string& getName() const; + const std::string& getDetector() const; + int getRunNumber() const; + const std::string& getPeriodName() const; + const std::string& getPassName() const; + const std::string& getProvenance() const; + + time_type getStart() const { return mValidityRange.getMin(); } + time_type getEnd() const { return mValidityRange.getMax(); } + RangeInterval& getInterval() { return mValidityRange; } + + void setStart(time_type start) { mValidityRange.setMin(start); } + void setEnd(time_type end) { mValidityRange.setMax(end); } + void setInterval(RangeInterval interval) { mValidityRange = interval; } + + /// write data to ostream + void streamTo(std::ostream& output) const; + /// Read data from instream + void streamFrom(std::istream& input); + + /// overloading output stream operator + friend std::ostream& operator<<(std::ostream& output, const QualityControlFlagCollection& data); + + private: + std::string mDetID; // three letter detector code + std::string mName; // some description of the collection, e.g. "Raw data checks", "QA Expert masks" + // with std::set we can sort the flags in time and have merge() for granted. + collection_t mQualityControlFlags; + RangeInterval mValidityRange; // we need a validity range to e.g. state that there are no TRFs for given time interval + int mRunNumber; + std::string mPeriodName; + std::string mPassName; + std::string mProvenance; + + ClassDefNV(QualityControlFlagCollection, 1); +}; + +} // namespace quality_control +} // namespace o2 + +#endif // O2_QUALITYCONTROL_QCFLAGCOLLECTION_H diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlag.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlag.h deleted file mode 100644 index b8e1efbce1f40..0000000000000 --- a/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlag.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_ANALYSIS_TIMERANGEFLAGS -#define ALICEO2_ANALYSIS_TIMERANGEFLAGS - -/// \file TimeRangeFlag.h -/// \brief Class to define a time range of a flag type -/// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -// System includes -#include -#include - -// ROOT includes -#include - -#include - -#include "DataFormatsQualityControl/FlagReasons.h" -#include "DataFormatsQualityControl/FlagReasonFactory.h" - -namespace o2 -{ -namespace quality_control -{ - -/// \class TimeRangeFlag -/// A Class for associating a bit mask with a time range -class TimeRangeFlag -{ - public: - using time_type = uint64_t; - using flag_type = FlagReason; - using RangeInterval = o2::math_utils::detail::Bracket; - - TimeRangeFlag() = default; - TimeRangeFlag(TimeRangeFlag const&) = default; - TimeRangeFlag(time_type start, time_type end, flag_type flag, std::string comment = "", std::string source = "Unknown"); - - time_type getStart() const { return mInterval.getMin(); } - time_type getEnd() const { return mInterval.getMax(); } - RangeInterval& getInterval() { return mInterval; } - flag_type getFlag() const { return mFlag; } - const std::string& getComment() const { return mComment; } - const std::string& getSource() const { return mSource; } - - void setStart(time_type start) { mInterval.setMin(start); } - void setEnd(time_type end) { mInterval.setMax(end); } - void setInterval(RangeInterval interval) { mInterval = interval; } - void setFlag(flag_type flag) { mFlag = flag; } - void setComment(const std::string& comment) { mComment = comment; } - void setSource(const std::string& source) { mSource = source; } - - /// equal operator - bool operator==(const TimeRangeFlag& rhs) const; - - /// comparison operators - bool operator<(const TimeRangeFlag& rhs) const; - bool operator>(const TimeRangeFlag& rhs) const; - - /// write data to ostream - void streamTo(std::ostream& output) const; - - /// overloading output stream operator - friend std::ostream& operator<<(std::ostream& output, const TimeRangeFlag& data); - - private: - RangeInterval mInterval = {}; ///< time interval of the masked range - flag_type mFlag; ///< flag reason - std::string mComment = ""; ///< optional comment, which may extend the reason - std::string mSource = "Unknown"; ///< optional (but encouraged) source of the flag (e.g. Qc Check name) - - ClassDefNV(TimeRangeFlag, 1); -}; - -} // namespace quality_control -} // namespace o2 -#endif diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlagCollection.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlagCollection.h deleted file mode 100644 index 30a8230fc66e2..0000000000000 --- a/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlagCollection.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_ANALYSIS_TIMERANGEMASK -#define ALICEO2_ANALYSIS_TIMERANGEMASK - -/// \file TimeRangeFlagCollection.h -/// \brief classes for defining time ranges with a certain mask to be able to cut on -/// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de - -// System includes -#include - -// ROOT includes -#include "Rtypes.h" - -// O2 includes -#include "MathUtils/detail/Bracket.h" -#include "DataFormatsQualityControl/TimeRangeFlag.h" - -// STL -#include - -namespace o2 -{ -namespace quality_control -{ - -/// \class TimeRangeFlagCollection -/// A Class for keeping several time ranges of type TimeRangeFlag -class TimeRangeFlagCollection -{ - public: - using collection_t = std::set; - using time_type = uint64_t; - using RangeInterval = o2::math_utils::detail::Bracket; - - explicit TimeRangeFlagCollection(std::string name, std::string detector = "TST", RangeInterval validityRange = {}, - int runNumber = 0, std::string periodName = "Invalid", std::string passName = "Invalid", - std::string provenance = "qc"); - - void insert(TimeRangeFlag&&); - void insert(const TimeRangeFlag&); - - size_t size() const; - - // moves all non-repeating TimeRangeFlags from other to this - void merge(TimeRangeFlagCollection& other); - // add all non-repeating TimeRangeFlags from other to this. - void merge(const TimeRangeFlagCollection& other); - - collection_t::const_iterator begin() const; - collection_t::const_iterator end() const; - - const std::string& getName() const; - const std::string& getDetector() const; - int getRunNumber() const; - const std::string& getPeriodName() const; - const std::string& getPassName() const; - const std::string& getProvenance() const; - - time_type getStart() const { return mValidityRange.getMin(); } - time_type getEnd() const { return mValidityRange.getMax(); } - RangeInterval& getInterval() { return mValidityRange; } - - void setStart(time_type start) { mValidityRange.setMin(start); } - void setEnd(time_type end) { mValidityRange.setMax(end); } - void setInterval(RangeInterval interval) { mValidityRange = interval; } - - /// write data to ostream - void streamTo(std::ostream& output) const; - /// Read data from instream - void streamFrom(std::istream& input); - - /// overloading output stream operator - friend std::ostream& operator<<(std::ostream& output, const TimeRangeFlagCollection& data); - - private: - std::string mDetID; // three letter detector code - std::string mName; // some description of the collection, e.g. "Raw data checks", "QA Expert masks" - // with std::set we can sort the flags in time and have merge() for granted. - collection_t mTimeRangeFlags; - RangeInterval mValidityRange; // we need a validity range to e.g. state that there are no TRFs for given time interval - int mRunNumber; - std::string mPeriodName; - std::string mPassName; - std::string mProvenance; - - ClassDefNV(TimeRangeFlagCollection, 1); -}; - -} // namespace quality_control -} // namespace o2 -#endif diff --git a/DataFormats/QualityControl/src/DataFormatsQualityControlLinkDef.h b/DataFormats/QualityControl/src/DataFormatsQualityControlLinkDef.h index 8d77bccef4b7c..ab10c361b29f9 100644 --- a/DataFormats/QualityControl/src/DataFormatsQualityControlLinkDef.h +++ b/DataFormats/QualityControl/src/DataFormatsQualityControlLinkDef.h @@ -15,8 +15,8 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::quality_control::FlagReason + ; -#pragma link C++ class o2::quality_control::TimeRangeFlag + ; -#pragma link C++ class o2::quality_control::TimeRangeFlagCollection + ; +#pragma link C++ class o2::quality_control::FlagType + ; +#pragma link C++ class o2::quality_control::QualityControlFlag + ; +#pragma link C++ class o2::quality_control::QualityControlFlagCollection + ; #endif diff --git a/DataFormats/QualityControl/src/FlagReasons.cxx b/DataFormats/QualityControl/src/FlagReasons.cxx deleted file mode 100644 index e85a0d0c7faa7..0000000000000 --- a/DataFormats/QualityControl/src/FlagReasons.cxx +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "DataFormatsQualityControl/FlagReasons.h" -#include "DataFormatsQualityControl/FlagReasonFactory.h" - -#include -#include - -namespace o2::quality_control -{ - -FlagReason::FlagReason() -{ - *this = FlagReasonFactory::Invalid(); -} - -std::ostream& operator<<(std::ostream& os, FlagReason const& my) -{ - os << "Flag Reason: id - " << my.mId << ", name - " << my.mName << ", bad - " << (my.mBad ? "true" : "false"); - return os; -} -bool FlagReason::operator==(const FlagReason& rhs) const -{ - return std::tie(mId, mName, mBad) == std::tie(rhs.mId, rhs.mName, rhs.mBad); -} -bool FlagReason::operator!=(const FlagReason& rhs) const -{ - return std::tie(mId, mName, mBad) != std::tie(rhs.mId, rhs.mName, rhs.mBad); -} -bool FlagReason::operator<(const FlagReason& rhs) const -{ - return std::tie(mId, mName, mBad) < std::tie(rhs.mId, rhs.mName, rhs.mBad); -} -bool FlagReason::operator>(const FlagReason& rhs) const -{ - return std::tie(mId, mName, mBad) > std::tie(rhs.mId, rhs.mName, rhs.mBad); -} - -} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/src/FlagType.cxx b/DataFormats/QualityControl/src/FlagType.cxx new file mode 100644 index 0000000000000..27e59911ed775 --- /dev/null +++ b/DataFormats/QualityControl/src/FlagType.cxx @@ -0,0 +1,48 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsQualityControl/FlagType.h" +#include "DataFormatsQualityControl/FlagTypeFactory.h" + +#include +#include + +namespace o2::quality_control +{ + +FlagType::FlagType() +{ + *this = FlagTypeFactory::Invalid(); +} + +std::ostream& operator<<(std::ostream& os, FlagType const& my) +{ + os << "Flag Reason: id - " << my.mId << ", name - " << my.mName << ", bad - " << (my.mBad ? "true" : "false"); + return os; +} +bool FlagType::operator==(const FlagType& rhs) const +{ + return std::tie(mId, mName, mBad) == std::tie(rhs.mId, rhs.mName, rhs.mBad); +} +bool FlagType::operator!=(const FlagType& rhs) const +{ + return std::tie(mId, mName, mBad) != std::tie(rhs.mId, rhs.mName, rhs.mBad); +} +bool FlagType::operator<(const FlagType& rhs) const +{ + return std::tie(mId, mName, mBad) < std::tie(rhs.mId, rhs.mName, rhs.mBad); +} +bool FlagType::operator>(const FlagType& rhs) const +{ + return std::tie(mId, mName, mBad) > std::tie(rhs.mId, rhs.mName, rhs.mBad); +} + +} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/src/QualityControlFlag.cxx b/DataFormats/QualityControl/src/QualityControlFlag.cxx new file mode 100644 index 0000000000000..ce749cad5315f --- /dev/null +++ b/DataFormats/QualityControl/src/QualityControlFlag.cxx @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsQualityControl/QualityControlFlag.h" + +#include +#include + +namespace o2::quality_control +{ + +QualityControlFlag::QualityControlFlag(time_type start, time_type end, FlagType flag, std::string comment, std::string source) + : mInterval(start, end), mFlag(flag), mComment(comment), mSource(source) +{ + if (mInterval.isInvalid()) { + throw std::runtime_error("QualityControlFlag start time '" + std::to_string(mInterval.getMin()) + "' is larger than end time '" + std::to_string(mInterval.getMax()) + "'"); + } +} + +bool QualityControlFlag::operator==(const QualityControlFlag& rhs) const +{ + return std::tie(mInterval, mFlag, mComment, mSource) == std::tie(rhs.mInterval, rhs.mFlag, rhs.mComment, rhs.mSource); +} + +bool QualityControlFlag::operator<(const QualityControlFlag& rhs) const +{ + // We don't use the comparison mechanism in Bracket, + // because std::set which is used in TRFCollection assumes that a < b, a > b <=> a == b. + // Using relation operators in Bracket would break insertion and merging. + return std::tie(static_cast(mInterval.getMin()), static_cast(mInterval.getMax()), mFlag, mComment, mSource) < std::tie(static_cast(rhs.mInterval.getMin()), static_cast(rhs.mInterval.getMax()), rhs.mFlag, rhs.mComment, rhs.mSource); +} + +bool QualityControlFlag::operator>(const QualityControlFlag& rhs) const +{ + // we don't use the comparison mechanism in Bracket, + // because std::set which is used in TRFCollection assumes that a < b, a > b <=> a == b + return std::tie(static_cast(mInterval.getMin()), static_cast(mInterval.getMax()), mFlag, mComment, mSource) > std::tie(static_cast(rhs.mInterval.getMin()), static_cast(rhs.mInterval.getMax()), rhs.mFlag, rhs.mComment, rhs.mSource); +} + +void QualityControlFlag::streamTo(std::ostream& output) const +{ + output << "QualityControlFlag:\n"; + output << "- Start: " << mInterval.getMin() << "\n"; + output << "- End: " << mInterval.getMax() << "\n"; + output << "- " << mFlag << "\n"; + output << "- Comment: " << mComment << "\n"; + output << "- Source: " << mSource; +} + +std::ostream& operator<<(std::ostream& output, const QualityControlFlag& data) +{ + data.streamTo(output); + return output; +} + +} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/src/QualityControlFlagCollection.cxx b/DataFormats/QualityControl/src/QualityControlFlagCollection.cxx new file mode 100644 index 0000000000000..042ef2f218d09 --- /dev/null +++ b/DataFormats/QualityControl/src/QualityControlFlagCollection.cxx @@ -0,0 +1,217 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// O2 include +#include "DataFormatsQualityControl/QualityControlFlagCollection.h" +#include "DataFormatsQualityControl/FlagTypeFactory.h" +#include "Framework/Logger.h" + +#include +#include +#include +#include + +namespace o2::quality_control +{ + +constexpr const char* csvHeader = "start,end,flag_id,flag_name,flag_bad,comment,source"; +constexpr size_t csvColumns = 7; + +QualityControlFlagCollection::QualityControlFlagCollection(std::string name, std::string detector, RangeInterval validityRange, + int runNumber, std::string periodName, std::string passName, + std::string provenance) + : mName(std::move(name)), mDetID(std::move(detector)), mValidityRange(validityRange), mRunNumber(runNumber), mPeriodName(std::move(periodName)), mPassName(passName), mProvenance(std::move(provenance)) +{ +} + +void QualityControlFlagCollection::insert(QualityControlFlag&& trf) +{ + mQualityControlFlags.insert(std::move(trf)); +} + +void QualityControlFlagCollection::insert(const QualityControlFlag& trf) +{ + mQualityControlFlags.insert(trf); +} + +size_t QualityControlFlagCollection::size() const +{ + return mQualityControlFlags.size(); +} + +void QualityControlFlagCollection::merge(QualityControlFlagCollection& other) +{ + if (mDetID != other.mDetID) { + // We assume that one QualityControlFlagCollection should correspond to one detector at most. + // However, if this becomes annoying, we can reconsider it. + throw std::runtime_error( + "The detector ID of the target collection '" + mDetID + "' is different than the other '" + mDetID); + } + mQualityControlFlags.merge(other.mQualityControlFlags); +} + +void QualityControlFlagCollection::merge(const QualityControlFlagCollection& other) +{ + QualityControlFlagCollection otherCopy{other}; + merge(otherCopy); +} + +QualityControlFlagCollection::collection_t::const_iterator QualityControlFlagCollection::begin() const +{ + return mQualityControlFlags.begin(); +} + +QualityControlFlagCollection::collection_t::const_iterator QualityControlFlagCollection::end() const +{ + return mQualityControlFlags.end(); +} + +void QualityControlFlagCollection::streamTo(std::ostream& output) const +{ + auto escapeComma = [](const std::string& str) { + return boost::algorithm::replace_all_copy(str, ",", "\\,"); + }; + output << csvHeader << '\n'; + for (const auto& trf : *this) { + output << fmt::format("{},{},{},\"{}\",{:d},\"{}\",\"{}\"\n", + trf.getStart(), trf.getEnd(), + trf.getFlag().getID(), escapeComma(trf.getFlag().getName()), trf.getFlag().getBad(), + escapeComma(trf.getComment()), escapeComma(trf.getSource())); + } +} + +void QualityControlFlagCollection::streamFrom(std::istream& input) +{ + std::string line; + std::getline(input, line); + if (line != csvHeader) { + throw std::runtime_error( + "Unsupported TRFCollection format, the first line is \"" + line + "\" instead of \"" + csvHeader + "\""); + } + + while (std::getline(input, line)) { + boost::tokenizer> tok(line); + + QualityControlFlag::time_type start = 0; + QualityControlFlag::time_type end = 0; + FlagType flag = FlagTypeFactory::Invalid(); + std::string comment; + std::string source; + auto it = tok.begin(); + bool valid = true; + size_t pos = 0; + for (; it != tok.end() && valid; pos++, it++) { + switch (pos) { + case 0: { + if (it->empty()) { + LOG(error) << "Invalid line, empty start time of a flag, skipping..."; + valid = false; + } else { + start = static_cast(std::stoull(*it)); + } + break; + } + case 1: { + if (it->empty()) { + LOG(error) << "Invalid line, empty end time of a flag, skipping..."; + valid = false; + } else { + end = static_cast(std::stoull(*it)); + } + break; + } + case 2: { + if (it->empty()) { + LOG(error) << "Invalid line, empty flag id, skipping..."; + valid = false; + } else { + flag.mId = std::stoul(*it); + } + break; + } + case 3: { + if (it->empty()) { + LOG(error) << "Invalid line, empty flag name, skipping..."; + valid = false; + } else { + flag.mName = *it; + } + break; + } + case 4: { + if (it->empty()) { + LOG(error) << "Invalid line, empty flag 'bad' field, skipping..."; + valid = false; + } else { + flag.mBad = static_cast(std::stoul(*it)); + } + break; + } + case 5: { + comment = *it; + break; + } + case 6: { + source = *it; + break; + } + default: { + LOG(error) << "More columns (" << pos + 1 << ") than expected (" << csvColumns + << ") in this line, skipping..."; + valid = false; + break; + } + } + } + if (valid && pos < csvColumns) { + LOG(error) << "Less columns (" << pos << ") than expected (" << csvColumns << ") in this line, skipping..."; + } else if (valid) { + insert({start, end, flag, comment, source}); + } + } +} + +std::ostream& operator<<(std::ostream& output, const QualityControlFlagCollection& data) +{ + data.streamTo(output); + return output; +} + +const std::string& QualityControlFlagCollection::getName() const +{ + return mName; +} + +const std::string& QualityControlFlagCollection::getDetector() const +{ + return mDetID; +} + +int QualityControlFlagCollection::getRunNumber() const +{ + return mRunNumber; +} + +const std::string& QualityControlFlagCollection::getPeriodName() const +{ + return mPeriodName; +} + +const std::string& QualityControlFlagCollection::getPassName() const +{ + return mPassName; +} +const std::string& QualityControlFlagCollection::getProvenance() const +{ + return mProvenance; +} + +} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/src/TimeRangeFlag.cxx b/DataFormats/QualityControl/src/TimeRangeFlag.cxx deleted file mode 100644 index f96d5bc9b7b72..0000000000000 --- a/DataFormats/QualityControl/src/TimeRangeFlag.cxx +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "DataFormatsQualityControl/TimeRangeFlag.h" - -#include -#include - -namespace o2::quality_control -{ - -TimeRangeFlag::TimeRangeFlag(time_type start, time_type end, flag_type flag, std::string comment, std::string source) - : mInterval(start, end), mFlag(flag), mComment(comment), mSource(source) -{ - if (mInterval.isInvalid()) { - throw std::runtime_error("TimeRangeFlag start time '" + std::to_string(mInterval.getMin()) + "' is larger than end time '" + std::to_string(mInterval.getMax()) + "'"); - } -} - -bool TimeRangeFlag::operator==(const TimeRangeFlag& rhs) const -{ - return std::tie(mInterval, mFlag, mComment, mSource) == std::tie(rhs.mInterval, rhs.mFlag, rhs.mComment, rhs.mSource); -} - -bool TimeRangeFlag::operator<(const TimeRangeFlag& rhs) const -{ - // We don't use the comparison mechanism in Bracket, - // because std::set which is used in TRFCollection assumes that a < b, a > b <=> a == b. - // Using relation operators in Bracket would break insertion and merging. - return std::tie(static_cast(mInterval.getMin()), static_cast(mInterval.getMax()), mFlag, mComment, mSource) < std::tie(static_cast(rhs.mInterval.getMin()), static_cast(rhs.mInterval.getMax()), rhs.mFlag, rhs.mComment, rhs.mSource); -} - -bool TimeRangeFlag::operator>(const TimeRangeFlag& rhs) const -{ - // we don't use the comparison mechanism in Bracket, - // because std::set which is used in TRFCollection assumes that a < b, a > b <=> a == b - return std::tie(static_cast(mInterval.getMin()), static_cast(mInterval.getMax()), mFlag, mComment, mSource) > std::tie(static_cast(rhs.mInterval.getMin()), static_cast(rhs.mInterval.getMax()), rhs.mFlag, rhs.mComment, rhs.mSource); -} - -void TimeRangeFlag::streamTo(std::ostream& output) const -{ - output << "TimeRangeFlag:\n"; - output << "- Start: " << mInterval.getMin() << "\n"; - output << "- End: " << mInterval.getMax() << "\n"; - output << "- " << mFlag << "\n"; - output << "- Comment: " << mComment << "\n"; - output << "- Source: " << mSource; -} - -std::ostream& operator<<(std::ostream& output, const TimeRangeFlag& data) -{ - data.streamTo(output); - return output; -} - -} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/src/TimeRangeFlagCollection.cxx b/DataFormats/QualityControl/src/TimeRangeFlagCollection.cxx deleted file mode 100644 index ee3adc8dc7f2f..0000000000000 --- a/DataFormats/QualityControl/src/TimeRangeFlagCollection.cxx +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// O2 include -#include "DataFormatsQualityControl/TimeRangeFlagCollection.h" -#include "DataFormatsQualityControl/FlagReasonFactory.h" -#include "Framework/Logger.h" - -#include -#include -#include -#include - -namespace o2::quality_control -{ - -constexpr const char* csvHeader = "start,end,flag_id,flag_name,flag_bad,comment,source"; -constexpr size_t csvColumns = 7; - -TimeRangeFlagCollection::TimeRangeFlagCollection(std::string name, std::string detector, RangeInterval validityRange, - int runNumber, std::string periodName, std::string passName, - std::string provenance) - : mName(std::move(name)), mDetID(std::move(detector)), mValidityRange(validityRange), mRunNumber(runNumber), mPeriodName(std::move(periodName)), mPassName(passName), mProvenance(std::move(provenance)) -{ -} - -void TimeRangeFlagCollection::insert(TimeRangeFlag&& trf) -{ - mTimeRangeFlags.insert(std::move(trf)); -} - -void TimeRangeFlagCollection::insert(const TimeRangeFlag& trf) -{ - mTimeRangeFlags.insert(trf); -} - -size_t TimeRangeFlagCollection::size() const -{ - return mTimeRangeFlags.size(); -} - -void TimeRangeFlagCollection::merge(TimeRangeFlagCollection& other) -{ - if (mDetID != other.mDetID) { - // We assume that one TimeRangeFlagCollection should correspond to one detector at most. - // However, if this becomes annoying, we can reconsider it. - throw std::runtime_error( - "The detector ID of the target collection '" + mDetID + "' is different than the other '" + mDetID); - } - mTimeRangeFlags.merge(other.mTimeRangeFlags); -} - -void TimeRangeFlagCollection::merge(const TimeRangeFlagCollection& other) -{ - TimeRangeFlagCollection otherCopy{other}; - merge(otherCopy); -} - -TimeRangeFlagCollection::collection_t::const_iterator TimeRangeFlagCollection::begin() const -{ - return mTimeRangeFlags.begin(); -} - -TimeRangeFlagCollection::collection_t::const_iterator TimeRangeFlagCollection::end() const -{ - return mTimeRangeFlags.end(); -} - -void TimeRangeFlagCollection::streamTo(std::ostream& output) const -{ - auto escapeComma = [](const std::string& str) { - return boost::algorithm::replace_all_copy(str, ",", "\\,"); - }; - output << csvHeader << '\n'; - for (const auto& trf : *this) { - output << fmt::format("{},{},{},\"{}\",{:d},\"{}\",\"{}\"\n", - trf.getStart(), trf.getEnd(), - trf.getFlag().getID(), escapeComma(trf.getFlag().getName()), trf.getFlag().getBad(), - escapeComma(trf.getComment()), escapeComma(trf.getSource())); - } -} - -void TimeRangeFlagCollection::streamFrom(std::istream& input) -{ - std::string line; - std::getline(input, line); - if (line != csvHeader) { - throw std::runtime_error( - "Unsupported TRFCollection format, the first line is \"" + line + "\" instead of \"" + csvHeader + "\""); - } - - while (std::getline(input, line)) { - boost::tokenizer> tok(line); - - TimeRangeFlag::time_type start = 0; - TimeRangeFlag::time_type end = 0; - FlagReason flag = FlagReasonFactory::Invalid(); - std::string comment; - std::string source; - auto it = tok.begin(); - bool valid = true; - size_t pos = 0; - for (; it != tok.end() && valid; pos++, it++) { - switch (pos) { - case 0: { - if (it->empty()) { - LOG(error) << "Invalid line, empty start time of a flag, skipping..."; - valid = false; - } else { - start = static_cast(std::stoull(*it)); - } - break; - } - case 1: { - if (it->empty()) { - LOG(error) << "Invalid line, empty end time of a flag, skipping..."; - valid = false; - } else { - end = static_cast(std::stoull(*it)); - } - break; - } - case 2: { - if (it->empty()) { - LOG(error) << "Invalid line, empty flag id, skipping..."; - valid = false; - } else { - flag.mId = std::stoul(*it); - } - break; - } - case 3: { - if (it->empty()) { - LOG(error) << "Invalid line, empty flag name, skipping..."; - valid = false; - } else { - flag.mName = *it; - } - break; - } - case 4: { - if (it->empty()) { - LOG(error) << "Invalid line, empty flag 'bad' field, skipping..."; - valid = false; - } else { - flag.mBad = static_cast(std::stoul(*it)); - } - break; - } - case 5: { - comment = *it; - break; - } - case 6: { - source = *it; - break; - } - default: { - LOG(error) << "More columns (" << pos + 1 << ") than expected (" << csvColumns - << ") in this line, skipping..."; - valid = false; - break; - } - } - } - if (valid && pos < csvColumns) { - LOG(error) << "Less columns (" << pos << ") than expected (" << csvColumns << ") in this line, skipping..."; - } else if (valid) { - insert({start, end, flag, comment, source}); - } - } -} - -std::ostream& operator<<(std::ostream& output, const TimeRangeFlagCollection& data) -{ - data.streamTo(output); - return output; -} - -const std::string& TimeRangeFlagCollection::getName() const -{ - return mName; -} - -const std::string& TimeRangeFlagCollection::getDetector() const -{ - return mDetID; -} - -int TimeRangeFlagCollection::getRunNumber() const -{ - return mRunNumber; -} - -const std::string& TimeRangeFlagCollection::getPeriodName() const -{ - return mPeriodName; -} - -const std::string& TimeRangeFlagCollection::getPassName() const -{ - return mPassName; -} -const std::string& TimeRangeFlagCollection::getProvenance() const -{ - return mProvenance; -} - -} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/test/testFlagReasons.cxx b/DataFormats/QualityControl/test/testFlagReasons.cxx deleted file mode 100644 index 9e7ff15383674..0000000000000 --- a/DataFormats/QualityControl/test/testFlagReasons.cxx +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#define BOOST_TEST_MODULE Test quality_control FlagReasons class -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -#include - -#include -#include - -// o2 includes -#include "DataFormatsQualityControl/FlagReasons.h" -#include "DataFormatsQualityControl/FlagReasonFactory.h" - -using namespace o2::quality_control; - -BOOST_AUTO_TEST_CASE(FlagReasons) -{ - static_assert(std::is_constructible::value == false, - "FlagReason should not be constructible outside of its static methods and the factory."); - - FlagReason rDefault; - BOOST_CHECK_EQUAL(rDefault, FlagReasonFactory::Invalid()); - - auto r1 = FlagReasonFactory::Unknown(); - BOOST_CHECK_EQUAL(r1.getID(), 1); - BOOST_CHECK_EQUAL(r1.getName(), "Unknown"); - BOOST_CHECK_EQUAL(r1.getBad(), true); - - std::cout << r1 << std::endl; - - auto r2 = r1; - BOOST_CHECK_EQUAL(r2.getID(), 1); - BOOST_CHECK_EQUAL(r1.getName(), r2.getName()); - BOOST_CHECK_EQUAL(r2.getName(), "Unknown"); - BOOST_CHECK_EQUAL(r2.getBad(), true); - - BOOST_CHECK_EQUAL(r1, r2); - BOOST_CHECK((r1 != r2) == false); - BOOST_CHECK(!(r1 < r2)); - BOOST_CHECK(!(r1 > r2)); - - auto r3 = FlagReasonFactory::LimitedAcceptance(); - BOOST_CHECK(r3 > r1); - BOOST_CHECK(!(r3 < r1)); -} diff --git a/DataFormats/QualityControl/test/testFlagTypes.cxx b/DataFormats/QualityControl/test/testFlagTypes.cxx new file mode 100644 index 0000000000000..2fe904aa1c167 --- /dev/null +++ b/DataFormats/QualityControl/test/testFlagTypes.cxx @@ -0,0 +1,56 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test quality_control FlagTypes class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include + +#include +#include + +// o2 includes +#include "DataFormatsQualityControl/FlagType.h" +#include "DataFormatsQualityControl/FlagTypeFactory.h" + +using namespace o2::quality_control; + +BOOST_AUTO_TEST_CASE(FlagTypes) +{ + static_assert(std::is_constructible::value == false, + "FlagType should not be constructible outside of its static methods and the factory."); + + FlagType fDefault; + BOOST_CHECK_EQUAL(fDefault, FlagTypeFactory::Invalid()); + + auto f1 = FlagTypeFactory::Unknown(); + BOOST_CHECK_EQUAL(f1.getID(), 14); + BOOST_CHECK_EQUAL(f1.getName(), "Unknown"); + BOOST_CHECK_EQUAL(f1.getBad(), true); + + BOOST_CHECK_NO_THROW(std::cout << f1 << std::endl); + + auto f2 = f1; + BOOST_CHECK_EQUAL(f2.getID(), 14); + BOOST_CHECK_EQUAL(f1.getName(), f2.getName()); + BOOST_CHECK_EQUAL(f2.getName(), "Unknown"); + BOOST_CHECK_EQUAL(f2.getBad(), true); + + BOOST_CHECK_EQUAL(f1, f2); + BOOST_CHECK((f1 != f2) == false); + BOOST_CHECK(!(f1 < f2)); + BOOST_CHECK(!(f1 > f2)); + + auto f3 = FlagTypeFactory::LimitedAcceptanceMCNotReproducible(); + BOOST_CHECK(f3 < f1); + BOOST_CHECK(!(f3 > f1)); +} diff --git a/DataFormats/QualityControl/test/testQualityControlFlag.cxx b/DataFormats/QualityControl/test/testQualityControlFlag.cxx new file mode 100644 index 0000000000000..2990b3bbd355f --- /dev/null +++ b/DataFormats/QualityControl/test/testQualityControlFlag.cxx @@ -0,0 +1,40 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test quality_control QualityControlFlag class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +// boost includes +#include + +// o2 includes +#include "DataFormatsQualityControl/QualityControlFlag.h" + +using namespace o2::quality_control; + +BOOST_AUTO_TEST_CASE(test_QualityControlFlag) +{ + QualityControlFlag qcFlag1{12, 34, FlagTypeFactory::BadTracking(), "comment", "source"}; + + BOOST_CHECK_EQUAL(qcFlag1.getStart(), 12); + BOOST_CHECK_EQUAL(qcFlag1.getEnd(), 34); + BOOST_CHECK_EQUAL(qcFlag1.getFlag(), FlagTypeFactory::BadTracking()); + BOOST_CHECK_EQUAL(qcFlag1.getComment(), "comment"); + BOOST_CHECK_EQUAL(qcFlag1.getSource(), "source"); + + BOOST_CHECK_THROW((QualityControlFlag{12, 0, FlagTypeFactory::BadTracking()}), std::runtime_error); + + QualityControlFlag qcFlag2{10, 34, FlagTypeFactory::BadTracking(), "comment", "source"}; + + BOOST_CHECK(qcFlag1 > qcFlag2); + BOOST_CHECK(!(qcFlag1 < qcFlag2)); +} \ No newline at end of file diff --git a/DataFormats/QualityControl/test/testQualityControlFlagCollection.cxx b/DataFormats/QualityControl/test/testQualityControlFlagCollection.cxx new file mode 100644 index 0000000000000..4eabfc5908741 --- /dev/null +++ b/DataFormats/QualityControl/test/testQualityControlFlagCollection.cxx @@ -0,0 +1,136 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test quality_control QualityControlFlagCollection class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +// boost includes +#include +// STL +#include +// o2 includes +#include "DataFormatsQualityControl/QualityControlFlagCollection.h" +#include "DataFormatsQualityControl/QualityControlFlag.h" +#include "DataFormatsQualityControl/FlagTypeFactory.h" + +using namespace o2::quality_control; + +BOOST_AUTO_TEST_CASE(test_QualityControlFlagCollection_Methods) +{ + QualityControlFlag flag1{12, 34, FlagTypeFactory::BadTracking(), "comment", "source"}; + QualityControlFlag flag2{10, 34, FlagTypeFactory::BadTracking(), "comment", "source"}; + + QualityControlFlagCollection qcfc1{"Raw data checks", "TOF", {10, 20000}, 12345, "LHC22k5", "passMC", "qc_mc"}; + qcfc1.insert(flag1); // by copy + qcfc1.insert(flag2); + qcfc1.insert({50, 77, FlagTypeFactory::Invalid()}); // by move + BOOST_CHECK_EQUAL(qcfc1.size(), 3); + BOOST_CHECK_EQUAL(qcfc1.getName(), "Raw data checks"); + BOOST_CHECK_EQUAL(qcfc1.getDetector(), "TOF"); + BOOST_CHECK_EQUAL(qcfc1.getStart(), 10); + BOOST_CHECK_EQUAL(qcfc1.getEnd(), 20000); + BOOST_CHECK_EQUAL(qcfc1.getRunNumber(), 12345); + BOOST_CHECK_EQUAL(qcfc1.getPeriodName(), "LHC22k5"); + BOOST_CHECK_EQUAL(qcfc1.getPassName(), "passMC"); + BOOST_CHECK_EQUAL(qcfc1.getProvenance(), "qc_mc"); + + QualityControlFlagCollection qcfc2{"Reco checks", "TOF"}; + qcfc2.insert({50, 77, FlagTypeFactory::Invalid()}); // this is a duplicate to an entry in qcfc1 + qcfc2.insert({51, 77, FlagTypeFactory::Invalid()}); + qcfc2.insert({1234, 3434, FlagTypeFactory::LimitedAcceptanceMCNotReproducible()}); + qcfc2.insert({50, 77, FlagTypeFactory::LimitedAcceptanceMCNotReproducible()}); + BOOST_CHECK_EQUAL(qcfc2.size(), 4); + + // Try merging. Duplicate entries should be left in the 'other' objects. + // Notice that we merge the two partial TRFCs into the third, which covers all cases + QualityControlFlagCollection qcfc3{"ALL", "TOF"}; + qcfc3.merge(qcfc1); + qcfc3.merge(qcfc2); + BOOST_CHECK_EQUAL(qcfc1.size(), 0); + BOOST_CHECK_EQUAL(qcfc2.size(), 1); + BOOST_CHECK_EQUAL(qcfc3.size(), 6); + + // Try const merging. It should copy the elements and keep the 'other' intact. + QualityControlFlagCollection qcfc4{"ALL", "TOF"}; + const auto& constTrfc3 = qcfc3; + qcfc4.merge(constTrfc3); + BOOST_CHECK_EQUAL(qcfc3.size(), 6); + BOOST_CHECK_EQUAL(qcfc4.size(), 6); + + // Try merging different detectors - it should throw. + QualityControlFlagCollection qcfc5{"ALL", "TPC"}; + BOOST_CHECK_THROW(qcfc5.merge(qcfc3), std::runtime_error); + BOOST_CHECK_THROW(qcfc5.merge(constTrfc3), std::runtime_error); + + // try printing + std::cout << qcfc3 << std::endl; + + // iterating + for (const auto& flag : qcfc3) { + (void)flag; + } +} + +BOOST_AUTO_TEST_CASE(test_QualityControlFlagCollection_IO) +{ + { + QualityControlFlagCollection qcfc1{"xyz", "TST"}; + + std::stringstream store; + qcfc1.streamTo(store); + + QualityControlFlagCollection qcfc2{"xyz", "TST"}; + qcfc2.streamFrom(store); + + BOOST_CHECK_EQUAL(qcfc2.size(), 0); + } + { + QualityControlFlagCollection qcfc1{"xyz", "TST"}; + qcfc1.insert({50, 77, FlagTypeFactory::Invalid(), "a comment", "a source"}); + qcfc1.insert({51, 77, FlagTypeFactory::Invalid()}); + qcfc1.insert({1234, 3434, FlagTypeFactory::LimitedAcceptanceMCNotReproducible()}); + qcfc1.insert({50, 77, FlagTypeFactory::LimitedAcceptanceMCNotReproducible()}); + qcfc1.insert({43434, 63421, FlagTypeFactory::Good()}); + + std::stringstream store; + qcfc1.streamTo(store); + + QualityControlFlagCollection qcfc2{"xyz", "TST"}; + qcfc2.streamFrom(store); + + BOOST_REQUIRE_EQUAL(qcfc1.size(), qcfc2.size()); + for (auto it1 = qcfc1.begin(), it2 = qcfc2.begin(); it1 != qcfc1.end() && it2 != qcfc2.end(); ++it1, ++it2) { + BOOST_CHECK_EQUAL(*it1, *it2); + } + } + { + std::stringstream store; + store << "start,end,flag_id,invalid,header,format\n"; + store << R"(123,345,11,"fdsa",1,"comment","source")"; + QualityControlFlagCollection qcfc1{"A", "TST"}; + BOOST_CHECK_THROW(qcfc1.streamFrom(store), std::runtime_error); + } + { + std::stringstream store; + store << "start,end,flag_id,flag_name,flag_bad,comment,source\n"; + store << R"(123,345,11,"fdsa",1,"comment","source","toomanycolumns")" << '\n'; + store << R"(123,345,11,"fdsa",1)" << '\n'; + store << R"(123,,11,"fdsa",1,"comment","source")" << '\n'; + store << R"(,345,11,"fdsa",1,"comment","source")" << '\n'; + store << R"(123,345,,"fdsa",1,"comment","source")" << '\n'; + store << R"(123,345,11,"",1,"comment","source")" << '\n'; + store << R"(123,345,11,"fdsa",,"comment","source")" << '\n'; + QualityControlFlagCollection qcfc1{"A", "TST"}; + BOOST_CHECK_NO_THROW(qcfc1.streamFrom(store)); + BOOST_CHECK_EQUAL(qcfc1.size(), 0); + } +} diff --git a/DataFormats/QualityControl/test/testTimeRangeFlag.cxx b/DataFormats/QualityControl/test/testTimeRangeFlag.cxx deleted file mode 100644 index 47eef61199103..0000000000000 --- a/DataFormats/QualityControl/test/testTimeRangeFlag.cxx +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#define BOOST_TEST_MODULE Test quality_control TimeRangeFlag class -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -// boost includes -#include - -// o2 includes -#include "DataFormatsQualityControl/TimeRangeFlag.h" - -using namespace o2::quality_control; - -BOOST_AUTO_TEST_CASE(test_TimeRangeFlag) -{ - TimeRangeFlag trf1{12, 34, FlagReasonFactory::BadTracking(), "comment", "source"}; - - BOOST_CHECK_EQUAL(trf1.getStart(), 12); - BOOST_CHECK_EQUAL(trf1.getEnd(), 34); - BOOST_CHECK_EQUAL(trf1.getFlag(), FlagReasonFactory::BadTracking()); - BOOST_CHECK_EQUAL(trf1.getComment(), "comment"); - BOOST_CHECK_EQUAL(trf1.getSource(), "source"); - - BOOST_CHECK_THROW((TimeRangeFlag{12, 0, FlagReasonFactory::BadTracking()}), std::runtime_error); - - TimeRangeFlag trf2{10, 34, FlagReasonFactory::BadTracking(), "comment", "source"}; - - BOOST_CHECK(trf1 > trf2); - BOOST_CHECK(!(trf1 < trf2)); -} \ No newline at end of file diff --git a/DataFormats/QualityControl/test/testTimeRangeFlagCollection.cxx b/DataFormats/QualityControl/test/testTimeRangeFlagCollection.cxx deleted file mode 100644 index 9f5468ce00865..0000000000000 --- a/DataFormats/QualityControl/test/testTimeRangeFlagCollection.cxx +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#define BOOST_TEST_MODULE Test quality_control TimeRangeFlagCollection class -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -// boost includes -#include -// STL -#include -// o2 includes -#include "DataFormatsQualityControl/TimeRangeFlagCollection.h" -#include "DataFormatsQualityControl/TimeRangeFlag.h" - -using namespace o2::quality_control; - -BOOST_AUTO_TEST_CASE(test_TimeRangeFlagCollection_Methods) -{ - TimeRangeFlag trf1{12, 34, FlagReasonFactory::BadTracking(), "comment", "source"}; - TimeRangeFlag trf2{10, 34, FlagReasonFactory::BadTracking(), "comment", "source"}; - - TimeRangeFlagCollection trfc1{"Raw data checks", "TOF", {10, 20000}, 12345, "LHC22k5", "passMC", "qc_mc"}; - trfc1.insert(trf1); // by copy - trfc1.insert(trf2); - trfc1.insert({50, 77, FlagReasonFactory::Invalid()}); // by move - BOOST_CHECK_EQUAL(trfc1.size(), 3); - BOOST_CHECK_EQUAL(trfc1.getName(), "Raw data checks"); - BOOST_CHECK_EQUAL(trfc1.getDetector(), "TOF"); - BOOST_CHECK_EQUAL(trfc1.getStart(), 10); - BOOST_CHECK_EQUAL(trfc1.getEnd(), 20000); - BOOST_CHECK_EQUAL(trfc1.getRunNumber(), 12345); - BOOST_CHECK_EQUAL(trfc1.getPeriodName(), "LHC22k5"); - BOOST_CHECK_EQUAL(trfc1.getPassName(), "passMC"); - BOOST_CHECK_EQUAL(trfc1.getProvenance(), "qc_mc"); - - TimeRangeFlagCollection trfc2{"Reco checks", "TOF"}; - trfc2.insert({50, 77, FlagReasonFactory::Invalid()}); // this is a duplicate to an entry in trfc1 - trfc2.insert({51, 77, FlagReasonFactory::Invalid()}); - trfc2.insert({1234, 3434, FlagReasonFactory::LimitedAcceptance()}); - trfc2.insert({50, 77, FlagReasonFactory::LimitedAcceptance()}); - BOOST_CHECK_EQUAL(trfc2.size(), 4); - - // Try merging. Duplicate entries should be left in the 'other' objects. - // Notice that we merge the two partial TRFCs into the third, which covers all cases - TimeRangeFlagCollection trfc3{"ALL", "TOF"}; - trfc3.merge(trfc1); - trfc3.merge(trfc2); - BOOST_CHECK_EQUAL(trfc1.size(), 0); - BOOST_CHECK_EQUAL(trfc2.size(), 1); - BOOST_CHECK_EQUAL(trfc3.size(), 6); - - // Try const merging. It should copy the elements and keep the 'other' intact. - TimeRangeFlagCollection trfc4{"ALL", "TOF"}; - const auto& constTrfc3 = trfc3; - trfc4.merge(constTrfc3); - BOOST_CHECK_EQUAL(trfc3.size(), 6); - BOOST_CHECK_EQUAL(trfc4.size(), 6); - - // Try merging different detectors - it should throw. - TimeRangeFlagCollection trfc5{"ALL", "TPC"}; - BOOST_CHECK_THROW(trfc5.merge(trfc3), std::runtime_error); - BOOST_CHECK_THROW(trfc5.merge(constTrfc3), std::runtime_error); - - // try printing - std::cout << trfc3 << std::endl; - - // iterating - for (const auto& trf : trfc3) { - (void)trf; - } -} - -BOOST_AUTO_TEST_CASE(test_TimeRangeFlagCollection_IO) -{ - { - TimeRangeFlagCollection trfc1{"xyz", "TST"}; - - std::stringstream store; - trfc1.streamTo(store); - - TimeRangeFlagCollection trfc2{"xyz", "TST"}; - trfc2.streamFrom(store); - - BOOST_CHECK_EQUAL(trfc2.size(), 0); - } - { - TimeRangeFlagCollection trfc1{"xyz", "TST"}; - trfc1.insert({50, 77, FlagReasonFactory::Invalid(), "a comment", "a source"}); - trfc1.insert({51, 77, FlagReasonFactory::Invalid()}); - trfc1.insert({1234, 3434, FlagReasonFactory::LimitedAcceptance()}); - trfc1.insert({50, 77, FlagReasonFactory::LimitedAcceptance()}); - trfc1.insert({43434, 63421, FlagReasonFactory::NotBadFlagExample()}); - - std::stringstream store; - trfc1.streamTo(store); - - TimeRangeFlagCollection trfc2{"xyz", "TST"}; - trfc2.streamFrom(store); - - BOOST_REQUIRE_EQUAL(trfc1.size(), trfc2.size()); - for (auto it1 = trfc1.begin(), it2 = trfc2.begin(); it1 != trfc1.end() && it2 != trfc2.end(); ++it1, ++it2) { - BOOST_CHECK_EQUAL(*it1, *it2); - } - } - { - std::stringstream store; - store << "start,end,flag_id,invalid,header,format\n"; - store << R"(123,345,11,"fdsa",1,"comment","source")"; - TimeRangeFlagCollection trfc1{"A", "TST"}; - BOOST_CHECK_THROW(trfc1.streamFrom(store), std::runtime_error); - } - { - std::stringstream store; - store << "start,end,flag_id,flag_name,flag_bad,comment,source\n"; - store << R"(123,345,11,"fdsa",1,"comment","source","toomanycolumns")" << '\n'; - store << R"(123,345,11,"fdsa",1)" << '\n'; - store << R"(123,,11,"fdsa",1,"comment","source")" << '\n'; - store << R"(,345,11,"fdsa",1,"comment","source")" << '\n'; - store << R"(123,345,,"fdsa",1,"comment","source")" << '\n'; - store << R"(123,345,11,"",1,"comment","source")" << '\n'; - store << R"(123,345,11,"fdsa",,"comment","source")" << '\n'; - TimeRangeFlagCollection trfc1{"A", "TST"}; - BOOST_CHECK_NO_THROW(trfc1.streamFrom(store)); - BOOST_CHECK_EQUAL(trfc1.size(), 0); - } -} diff --git a/DataFormats/Reconstruction/CMakeLists.txt b/DataFormats/Reconstruction/CMakeLists.txt index 2849745a2a720..d3ca8fdc70ad6 100644 --- a/DataFormats/Reconstruction/CMakeLists.txt +++ b/DataFormats/Reconstruction/CMakeLists.txt @@ -8,6 +8,7 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# add_compile_options(-O0 -g -fPIC) o2_add_library(ReconstructionDataFormats SOURCES src/TrackParametrization.cxx @@ -17,6 +18,7 @@ o2_add_library(ReconstructionDataFormats src/TrackTPCITS.cxx src/Vertex.cxx src/PrimaryVertex.cxx + src/PrimaryVertexExt.cxx src/MatchInfoTOF.cxx src/MatchInfoTOFReco.cxx src/TrackLTIntegral.cxx @@ -24,19 +26,23 @@ o2_add_library(ReconstructionDataFormats src/DCA.cxx src/V0.cxx src/Cascade.cxx - src/DecayNbody.cxx + src/Decay3Body.cxx + src/DecayNBodyIndex.cxx + src/StrangeTrack.cxx src/GlobalTrackID.cxx src/VtxTrackIndex.cxx src/VtxTrackRef.cxx src/TrackTPCTOF.cxx src/TrackCosmics.cxx src/TrackMCHMID.cxx + src/TrackHMP.cxx src/MatchInfoHMP.cxx PUBLIC_LINK_LIBRARIES O2::GPUCommon O2::GPUUtils O2::FrameworkLogger O2::DetectorsCommonDataFormats - O2::CommonDataFormat) + O2::CommonDataFormat + O2::Field) o2_target_root_dictionary( ReconstructionDataFormats @@ -47,6 +53,7 @@ o2_target_root_dictionary( include/ReconstructionDataFormats/GlobalFwdTrack.h include/ReconstructionDataFormats/Vertex.h include/ReconstructionDataFormats/PrimaryVertex.h + include/ReconstructionDataFormats/PrimaryVertexExt.h include/ReconstructionDataFormats/MatchInfoTOF.h include/ReconstructionDataFormats/MatchInfoTOFReco.h include/ReconstructionDataFormats/TrackLTIntegral.h @@ -54,7 +61,9 @@ o2_target_root_dictionary( include/ReconstructionDataFormats/DCA.h include/ReconstructionDataFormats/V0.h include/ReconstructionDataFormats/Cascade.h - include/ReconstructionDataFormats/DecayNbody.h + include/ReconstructionDataFormats/DecayNBodyIndex.h + include/ReconstructionDataFormats/Decay3Body.h + include/ReconstructionDataFormats/StrangeTrack.h include/ReconstructionDataFormats/GlobalTrackID.h include/ReconstructionDataFormats/VtxTrackIndex.h include/ReconstructionDataFormats/VtxTrackRef.h @@ -63,7 +72,9 @@ o2_target_root_dictionary( include/ReconstructionDataFormats/TrackMCHMID.h include/ReconstructionDataFormats/MatchInfoFwd.h include/ReconstructionDataFormats/BCRange.h + include/ReconstructionDataFormats/TrackHMP.h include/ReconstructionDataFormats/MatchInfoHMP.h + include/ReconstructionDataFormats/HelixHelper.h ) o2_add_test(Vertex diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Cascade.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Cascade.h index 77047357c092f..7744036247f85 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Cascade.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Cascade.h @@ -27,16 +27,12 @@ namespace dataformats class Cascade : public V0 { public: + using V0::V0; Cascade() = default; + Cascade(const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const o2::track::TrackParCov& v0, const o2::track::TrackParCov& bachelor, - int v0ID, GIndex bachelorID, o2::track::PID pid = o2::track::PID::XiMinus); - - GIndex getBachelorID() const { return mProngIDs[1]; } - void setBachelorID(GIndex gid) { mProngIDs[1] = gid; } - - int getV0ID() const { return int(mProngIDs[0].getRaw()); } - void setV0ID(int vid) { mProngIDs[0].setRaw(GIndex::Base_t(vid)); } + o2::track::PID pid = o2::track::PID::XiMinus) : V0(xyz, pxyz, covxyz, v0, bachelor, pid) {} const Track& getV0Track() const { return mProngs[0]; } Track& getV0Track() { return mProngs[0]; } @@ -47,10 +43,10 @@ class Cascade : public V0 void setV0Track(const Track& t) { mProngs[0] = t; } void setBachelorTrack(const Track& t) { mProngs[1] = t; } - protected: - GIndex getProngID(int i) const = delete; + float calcMass2AsXiMinus() const { return calcMass2PID(PID::Lambda, PID::Pion); } + float calcMass2AsOmegaMinus() const { return calcMass2PID(PID::Lambda, PID::Kaon); } - ClassDefNV(Cascade, 1); + ClassDefNV(Cascade, 2); }; } // namespace dataformats diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h index 922470f8992f5..d6edfcb60d560 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h @@ -14,10 +14,10 @@ #include "GPUCommonDef.h" #include "GPUCommonRtypes.h" -#include "GPUCommonArray.h" #ifndef GPUCA_GPUCODE_DEVICE #include +#include #endif /// \author ruben.shahoyan@cern.ch @@ -54,6 +54,17 @@ class DCA mZ = z; } + GPUd() void setY(float y) { mY = y; } + GPUd() void setZ(float z) { mZ = z; } + GPUd() void setSigmaY2(float v) { mCov[0] = v; } + GPUd() void setSigmaYZ(float v) { mCov[1] = v; } + GPUd() void setSigmaZ2(float v) { mCov[2] = v; } + GPUd() void addCov(const std::array& vadd) + { + mCov[0] += vadd[0]; + mCov[1] += vadd[1]; + mCov[2] += vadd[2]; + } GPUd() auto getY() const { return mY; } GPUd() auto getZ() const { return mZ; } GPUd() auto getR2() const { return mY * mY + mZ * mZ; } @@ -61,13 +72,14 @@ class DCA GPUd() auto getSigmaYZ() const { return mCov[1]; } GPUd() auto getSigmaZ2() const { return mCov[2]; } GPUd() const auto& getCovariance() const { return mCov; } + GPUd() float calcChi2() const; void print() const; private: float mY = 0.f; float mZ = 0.f; - gpu::gpustd::array mCov; ///< s2y, syz, s2z + std::array mCov; ///< s2y, syz, s2z ClassDefNV(DCA, 1); }; diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Decay3Body.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Decay3Body.h new file mode 100644 index 0000000000000..f7f18976fa508 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Decay3Body.h @@ -0,0 +1,60 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_3BODY_H +#define ALICEO2_3BODY_H + +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/PID.h" +#include +#include + +namespace o2 +{ +namespace dataformats +{ + +class Decay3Body : public o2::track::TrackParCov /// TO BE DONE: extend to generic N body vertex +{ + public: + using Track = o2::track::TrackParCov; + using PID = o2::track::PID; + + Decay3Body() = default; + Decay3Body(const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2, o2::track::PID pid = o2::track::PID::HyperTriton); + + const Track& getProng(int i) const { return mProngs[i]; } + Track& getProng(int i) { return mProngs[i]; } + void setProng(int i, const Track& t) { mProngs[i] = t; } + + float getCosPA() const { return mCosPA; } + void setCosPA(float c) { mCosPA = c; } + + float getDCA() const { return mDCA; } + void setDCA(float d) { mDCA = d; } + + float calcMass2() const { return calcMass2PID(mProngs[0].getPID(), mProngs[1].getPID(), mProngs[2].getPID()); } + float calcMass2PID(int pid0, int pid1, int pid2) const { return calcMass2(PID::getMass2(pid0), PID::getMass2(pid1), PID::getMass2(pid2)); } + float calcMass2(float mass0, float mass1, float mass2) const; + + float calcR2() const { return getX() * getX() + getY() * getY(); } + + protected: + std::array mProngs; // prongs kinematics at vertex + float mCosPA = 0; // cos of pointing angle + float mDCA = 9990; // distance of closest approach of prongs + + ClassDefNV(Decay3Body, 1); +}; + +} // namespace dataformats +} // namespace o2 +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNBodyIndex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNBodyIndex.h new file mode 100644 index 0000000000000..5a5a8a9e64cca --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNBodyIndex.h @@ -0,0 +1,108 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_NBODY_INDEX_H +#define ALICEO2_NBODY_INDEX_H + +#include "ReconstructionDataFormats/VtxTrackIndex.h" + +namespace o2::dataformats +{ + +template +class DecayNBodyIndex +{ + public: + using GIndex = o2::dataformats::VtxTrackIndex; + DecayNBodyIndex() = default; + DecayNBodyIndex(int v, const std::array& arr) : mVertexID(v), mProngIDs(arr) {} + DecayNBodyIndex(int v, std::initializer_list l) : mVertexID(v) + { + assert(l.size() == N); + int i = 0; + for (auto e : l) { + mProngIDs[i++] = e; + } + } + GIndex getProngID(int i) const { return mProngIDs[i]; } + void setProngID(int i, GIndex gid) { mProngIDs[i] = gid; } + int getVertexID() const { return mVertexID; } + void setVertexID(int id) { mVertexID = id; } + uint8_t getBits() const { return mBits; } + bool testBit(int i) const { return (mBits & (0x1 << i)) != 0; } + void setBit(int i) { mBits |= (0x1 << i); } + void resetBit(int i) { mBits &= ~(0x1 << i); } + + const std::array& getProngs() const { return mProngIDs; } + static constexpr int getNProngs() { return N; } + + protected: + int mVertexID = -1; // id of parent vertex + std::array mProngIDs{}; // global IDs of prongs + uint8_t mBits = 0; // user defined bits + + ClassDefNV(DecayNBodyIndex, 2); +}; + +class V0Index : public DecayNBodyIndex<2> +{ + public: + enum V0Type : uint8_t { + kStandaloneV0 = 0, + kPhotonOnly, + kCollinear, + }; + using DecayNBodyIndex<2>::DecayNBodyIndex; + V0Index(int v, GIndex p, GIndex n) : DecayNBodyIndex<2>(v, {p, n}) {} + bool isStandaloneV0() const { return testBit(kStandaloneV0); } + bool isPhotonOnly() const { return testBit(kPhotonOnly); } + bool isCollinear() const { return testBit(kCollinear); } + void setStandaloneV0() { setBit(kStandaloneV0); } + void setPhotonOnly() { setBit(kPhotonOnly); } + void setCollinear() { setBit(kCollinear); } + ClassDefNV(V0Index, 1); +}; + +class Decay3BodyIndex : public DecayNBodyIndex<3> +{ + public: + using DecayNBodyIndex<3>::DecayNBodyIndex; + Decay3BodyIndex(int v, GIndex p0, GIndex p1, GIndex p2) : DecayNBodyIndex<3>(v, {p0, p1, p2}) {} + ClassDefNV(Decay3BodyIndex, 1); +}; + +class CascadeIndex +{ + public: + using GIndex = o2::dataformats::VtxTrackIndex; + CascadeIndex() = default; + CascadeIndex(int v, int v0id, GIndex bachelorID) : mVertexID(v), mV0ID(v0id), mBach(bachelorID) {} + + GIndex getBachelorID() const { return mBach; } + void setBachelorID(GIndex gid) { mBach = gid; } + + int getV0ID() const { return mV0ID; } + void setV0ID(int vid) { mV0ID = vid; } + + int getVertexID() const { return mVertexID; } + void setVertexID(int id) { mVertexID = id; } + + protected: + int mVertexID = -1; + int mV0ID = -1; + GIndex mBach{}; + + ClassDefNV(CascadeIndex, 1); +}; + +} // namespace o2::dataformats + +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNbody.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNbody.h deleted file mode 100644 index a249aa87fc900..0000000000000 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNbody.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_NBODY_H -#define ALICEO2_NBODY_H - -#include "ReconstructionDataFormats/VtxTrackIndex.h" -#include "ReconstructionDataFormats/Track.h" -#include "ReconstructionDataFormats/PID.h" -#include -#include - -namespace o2 -{ -namespace dataformats -{ - -class DecayNbody : public o2::track::TrackParCov /// TO BE DONE: extend to generic N body vertex -{ - public: - using GIndex = o2::dataformats::VtxTrackIndex; - using Track = o2::track::TrackParCov; - using PID = o2::track::PID; - - DecayNbody() = default; - DecayNbody(PID pid, const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2, GIndex trID0, GIndex trID1, GIndex trID2); - - GIndex getProngID(int i) const { return mProngIDs[i]; } - void setProngID(int i, GIndex gid) { mProngIDs[i] = gid; } - - const Track& getProng(int i) const { return mProngs[i]; } - Track& getProng(int i) { return mProngs[i]; } - void setProng(int i, const Track& t) { mProngs[i] = t; } - - float getCosPA() const { return mCosPA; } - void setCosPA(float c) { mCosPA = c; } - - float getDCA() const { return mDCA; } - void setDCA(float d) { mDCA = d; } - - int getVertexID() const { return mVertexID; } - void setVertexID(int id) { mVertexID = id; } - - float calcMass2() const { return calcMass2(mProngs[0].getPID(), mProngs[1].getPID(), mProngs[2].getPID()); } - float calcMass2(PID pid0, PID pid1, PID pid2) const { return calcMass2(pid0.getMass2(), pid1.getMass2(), pid2.getMass2()); } - float calcMass2(float mass0, float mass1, float mass2) const; - - float calcR2() const { return getX() * getX() + getY() * getY(); } - - protected: - std::array mProngIDs; // global IDs of prongs - std::array mProngs; // prongs kinematics at vertex - float mCosPA = 0; // cos of pointing angle - float mDCA = 9990; // distance of closest approach of prongs - int mVertexID = -1; // id of parent vertex - - ClassDefNV(DecayNbody, 1); -}; - -} // namespace dataformats -} // namespace o2 -#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/GlobalFwdTrack.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/GlobalFwdTrack.h index 15d816b7b7727..5d13a216316ef 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/GlobalFwdTrack.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/GlobalFwdTrack.h @@ -31,6 +31,7 @@ class GlobalFwdTrack : public o2::track::TrackParCovFwd, public o2::dataformats: public: GlobalFwdTrack() = default; GlobalFwdTrack(const GlobalFwdTrack& t) = default; + GlobalFwdTrack(o2::track::TrackParCovFwd const& t) { *this = t; } ~GlobalFwdTrack() = default; SMatrix5 computeResiduals2Cov(const o2::track::TrackParCovFwd& t) const diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h new file mode 100644 index 0000000000000..d197cba256c0e --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h @@ -0,0 +1,307 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file HelixHelper.h +/// \brief Helper classes for helical tracks manipulations +/// \author ruben.shahoyan@cern.ch + +#ifndef _ALICEO2_HELIX_HELPER_ +#define _ALICEO2_HELIX_HELPER_ + +#include "CommonConstants/MathConstants.h" +#include "MathUtils/Utils.h" +#include "MathUtils/Primitive2D.h" + +namespace o2 +{ +namespace track +{ + +///__________________________________________________________________________ +//< precalculated track radius, center, alpha sin,cos and their combinations +struct TrackAuxPar : public o2::math_utils::CircleXYf_t { + float c, s, cc, ss, cs; // cos ans sin of track alpha and their products + + GPUdDefault() TrackAuxPar() = default; + + template + GPUd() TrackAuxPar(const T& trc, float bz) + { + set(trc, bz); + } + GPUd() float cosDif(const TrackAuxPar& t) const { return c * t.c + s * t.s; } // cos(alpha_this - alha_t) + GPUd() float sinDif(const TrackAuxPar& t) const { return s * t.c - c * t.s; } // sin(alpha_this - alha_t) + + template + GPUd() void set(const T& trc, float bz) + { + trc.getCircleParams(bz, *this, s, c); + cc = c * c; + ss = s * s; + cs = c * s; + } + ClassDefNV(TrackAuxPar, 1); +}; + +//__________________________________________________________ +//< crossing coordinates of 2 circles +struct CrossInfo { + static constexpr float MaxDistXYDef = 10.; + float xDCA[2] = {}; + float yDCA[2] = {}; + int nDCA = 0; + + GPUd() int circlesCrossInfo(const TrackAuxPar& trax0, const TrackAuxPar& trax1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) + { + const auto& trcA = trax0.rC > trax1.rC ? trax0 : trax1; // designate the largest circle as A + const auto& trcB = trax0.rC > trax1.rC ? trax1 : trax0; + nDCA = 0; + float xDist = trcB.xC - trcA.xC, yDist = trcB.yC - trcA.yC; + float dist2 = xDist * xDist + yDist * yDist, dist = o2::gpu::GPUCommonMath::Sqrt(dist2), rsum = trcA.rC + trcB.rC; + if (dist < 1e-12) { + return nDCA; // circles are concentric? + } + if (dist > rsum) { // circles don't touch, chose a point in between + // the parametric equation of lines connecting the centers is + // x = x0 + t/dist * (x1-x0), y = y0 + t/dist * (y1-y0) + if (dist - rsum > maxDistXY) { // too large distance + return nDCA; + } + notTouchingXY(dist, xDist, yDist, trcA, trcB.rC, isCollinear); + } else if (auto dfr = dist + trcB.rC - trcA.rC; dfr < 0.) { // the small circle is nestled into large one w/o touching + if (dfr > -maxDistXY) { + // select the point of closest approach of 2 circles + notTouchingXY(dist, xDist, yDist, trcA, -trcB.rC, isCollinear); + } else { + return nDCA; + } + } else { // 2 intersection points + if (isCollinear) { + /// collinear tracks, e.g. electrons from photon conversion + /// if there are 2 crossings of the circle it is better to take + /// a weighted average of the crossing points as a radius + float r2r = trcA.rC + trcB.rC; + float r1_r = trcA.rC / r2r; + float r2_r = trcB.rC / r2r; + xDCA[0] = r2_r * trcA.xC + r1_r * trcB.xC; + yDCA[0] = r2_r * trcA.yC + r1_r * trcB.yC; + nDCA = 1; + } else if (o2::gpu::GPUCommonMath::Abs(xDist) < o2::gpu::GPUCommonMath::Abs(yDist)) { + // to simplify calculations, we move to new frame x->x+Xc0, y->y+Yc0, so that + // the 1st one is centered in origin + float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * yDist), b = -xDist / yDist, ab = a * b, bb = b * b; + float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); + if (det > 0.) { + det = o2::gpu::GPUCommonMath::Sqrt(det); + xDCA[0] = (-ab + det) / (1. + b * b); + yDCA[0] = a + b * xDCA[0] + trcA.yC; + xDCA[0] += trcA.xC; + xDCA[1] = (-ab - det) / (1. + b * b); + yDCA[1] = a + b * xDCA[1] + trcA.yC; + xDCA[1] += trcA.xC; + nDCA = 2; + } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case + notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); + } + } else { + float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * xDist), b = -yDist / xDist, ab = a * b, bb = b * b; + float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); + if (det > 0.) { + det = o2::gpu::GPUCommonMath::Sqrt(det); + yDCA[0] = (-ab + det) / (1. + bb); + xDCA[0] = a + b * yDCA[0] + trcA.xC; + yDCA[0] += trcA.yC; + yDCA[1] = (-ab - det) / (1. + bb); + xDCA[1] = a + b * yDCA[1] + trcA.xC; + yDCA[1] += trcA.yC; + nDCA = 2; + } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case + notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); + } + } + } + return nDCA; + } + + GPUd() void notTouchingXY(float dist, float xDist, float yDist, const TrackAuxPar& trcA, float rBSign, bool isCollinear = false) + { + if (isCollinear) { + /// for collinear tracks it is better to take + /// a weighted average of the crossing points as a radius + float r2r = trcA.rC + rBSign; + float r1_r = trcA.rC / r2r; + float r2_r = rBSign / r2r; + xDCA[0] = r2_r * trcA.xC + r1_r * (xDist + trcA.xC); + yDCA[0] = r2_r * trcA.yC + r1_r * (yDist + trcA.yC); + } else { + // fast method to calculate DCA between 2 circles, assuming that they don't touch each outer: + // the parametric equation of lines connecting the centers is x = xA + t/dist * xDist, y = yA + t/dist * yDist + // with xA,yY being the center of the circle A ( = trcA.xC, trcA.yC ), xDist = trcB.xC = trcA.xC ... + // There are 2 special cases: + // (a) small circle is inside the large one: provide rBSign as -trcB.rC + // (b) circle are side by side: provide rBSign as trcB.rC + auto t2d = (dist + trcA.rC - rBSign) / dist; + xDCA[0] = trcA.xC + 0.5 * (xDist * t2d); + yDCA[0] = trcA.yC + 0.5 * (yDist * t2d); + } + nDCA = 1; + } + + template + GPUd() int linesCrossInfo(const TrackAuxPar& trax0, const T& tr0, + const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) + { + /// closest approach of 2 straight lines + /// TrackParam propagation can be parameterized in lab in a form + /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) + /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) + /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp + /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab + /// frame (filled by TrackAuxPar for straight line tracks). + /// + /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) + /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) + /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) + /// zL(t) = zL + t Kz; Kz = tgl / csp + /// Note that Kx^2 + Ky^2 + Kz^2 = (1+tgl^2) / csp^2 + nDCA = 0; + float dx = trax1.xC - trax0.xC; // for straight line TrackAuxPar stores lab coordinates at referene point!!! + float dy = trax1.yC - trax0.yC; // + float dz = tr1.getZ() - tr0.getZ(); + auto csp0i2 = 1. / tr0.getCsp2(); // 1 / csp^2 + auto csp0i = o2::gpu::GPUCommonMath::Sqrt(csp0i2); + auto tgp0 = tr0.getSnp() * csp0i; + float kx0 = trax0.c - trax0.s * tgp0; + float ky0 = trax0.s + trax0.c * tgp0; + float kz0 = tr0.getTgl() * csp0i; + auto csp1i2 = 1. / tr1.getCsp2(); // 1 / csp^2 + auto csp1i = o2::gpu::GPUCommonMath::Sqrt(csp1i2); + auto tgp1 = tr1.getSnp() * o2::gpu::GPUCommonMath::Sqrt(csp1i2); + float kx1 = trax1.c - trax1.s * tgp1; + float ky1 = trax1.s + trax1.c * tgp1; + float kz1 = tr1.getTgl() * csp1i; + /// Minimize |vecL1 - vecL0|^2 wrt t0 and t1: point of closest approach + /// Leads to system + /// A Dx = B with Dx = {dx0, dx1} + /// with A = + /// | kx0^2+ky0^2+kz0^2 -(kx0*kx1+ky0*ky1+kz0*kz1) | = (1+tgl0^2) / csp0^2 .... + /// | -(kx0*kx1+ky0*ky1+kz0*kz1) kx0^2+ky0^2+kz0^2 | ..... (1+tgl1^2) / csp1^2 + /// and B = {(dx Kx0 + dy Ky0 + dz Kz0), -(dx Kx1 + dy Ky1 + dz Kz1) } + /// + float a00 = (1.f + tr0.getTgl() * tr0.getTgl()) * csp0i2, a11 = (1.f + tr1.getTgl() * tr1.getTgl()) * csp1i2, a01 = -(kx0 * kx1 + ky0 * ky1 + kz0 * kz1); + float b0 = dx * kx0 + dy * ky0 + dz * kz0, b1 = -(dx * kx1 + dy * ky1 + dz * kz1); + float det = a00 * a11 - a01 * a01, det0 = b0 * a11 - b1 * a01, det1 = a00 * b1 - a01 * b0; + if (o2::gpu::GPUCommonMath::Sqrt(det) > o2::constants::math::Almost0) { + auto detI = 1. / det; + auto t0 = det0 * detI; + auto t1 = det1 * detI; + float addx0 = kx0 * t0, addy0 = ky0 * t0, addx1 = kx1 * t1, addy1 = ky1 * t1; + dx += addx1 - addx0; // recalculate XY distance at DCA + dy += addy1 - addy0; + if (dx * dx + dy * dy > maxDistXY * maxDistXY) { + return nDCA; + } + xDCA[0] = (trax0.xC + addx0 + trax1.xC + addx1) * 0.5; + yDCA[0] = (trax0.yC + addy0 + trax1.yC + addy1) * 0.5; + nDCA = 1; + } + return nDCA; + } + + template + GPUd() int circleLineCrossInfo(const TrackAuxPar& trax0, const T& tr0, + const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) + { + /// closest approach of line and circle + /// TrackParam propagation can be parameterized in lab in a form + /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) + /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) + /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp + /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab + /// frame (filled by TrackAuxPar for straight line tracks). + /// + /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) + /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) + /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) + /// zL(t) = zL + t Kz; Kz = tgl / csp + /// Note that Kx^2 + Ky^2 = 1 / csp^2 + + const auto& traxH = trax0.rC > trax1.rC ? trax0 : trax1; // circle (for the line rC is set to 0) + const auto& traxL = trax0.rC > trax1.rC ? trax1 : trax0; // line + const auto& trcL = trax0.rC > trax1.rC ? tr1 : tr0; // track of the line + + // solve quadratic equation of line crossing the circle + float dx = traxL.xC - traxH.xC; // X distance between the line lab reference and circle center + float dy = traxL.yC - traxH.yC; // Y... + // t^2(kx^2+ky^2) + 2t(dx*kx+dy*ky) + dx^2 + dy^2 - r^2 = 0 + auto cspi2 = 1. / trcL.getCsp2(); // 1 / csp^2 == kx^2 + ky^2 + auto cspi = o2::gpu::GPUCommonMath::Sqrt(cspi2); + auto tgp = trcL.getSnp() * cspi; + float kx = traxL.c - traxL.s * tgp; + float ky = traxL.s + traxL.c * tgp; + double dk = dx * kx + dy * ky; + double det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC); + if (det > 0) { // 2 crossings + det = o2::gpu::GPUCommonMath::Sqrt(det); + float t0 = (-dk + det) * cspi2; + float t1 = (-dk - det) * cspi2; + xDCA[0] = traxL.xC + kx * t0; + yDCA[0] = traxL.yC + ky * t0; + xDCA[1] = traxL.xC + kx * t1; + yDCA[1] = traxL.yC + ky * t1; + nDCA = 2; + } else { + // there is no crossing, find the point of the closest approach on the line which is closest to the circle center + float t = -dk * cspi2; + float xL = traxL.xC + kx * t, yL = traxL.yC + ky * t; // point on the line, need to average with point on the circle + float dxc = xL - traxH.xC, dyc = yL - traxH.yC, dist = o2::gpu::GPUCommonMath::Sqrt(dxc * dxc + dyc * dyc); + if (dist - traxH.rC > maxDistXY) { // too large distance + return nDCA; + } + float drcf = traxH.rC / dist; // radius / distance to circle center + float xH = traxH.xC + dxc * drcf, yH = traxH.yC + dyc * drcf; + xDCA[0] = (xL + xH) * 0.5; + yDCA[0] = (yL + yH) * 0.5; + nDCA = 1; + } + return nDCA; + } + + template + GPUd() int set(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) + { + // calculate up to 2 crossings between 2 circles + nDCA = 0; + if (trax0.rC > o2::constants::math::Almost0 && trax1.rC > o2::constants::math::Almost0) { // both are not straight lines + nDCA = circlesCrossInfo(trax0, trax1, maxDistXY, isCollinear); + } else if (trax0.rC < o2::constants::math::Almost0 && trax1.rC < o2::constants::math::Almost0) { // both are straigt lines + nDCA = linesCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); + } else { + nDCA = circleLineCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); + } + // + return nDCA; + } + + GPUdDefault() CrossInfo() = default; + + template + GPUd() CrossInfo(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) + { + set(trax0, tr0, trax1, tr1, maxDistXY, isCollinear); + } + ClassDefNV(CrossInfo, 1); +}; + +} // namespace track +} // namespace o2 + +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoHMP.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoHMP.h index 2d614201c2220..e065b3d7dfe40 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoHMP.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoHMP.h @@ -28,22 +28,43 @@ class MatchInfoHMP using GTrackID = o2::dataformats::GlobalTrackID; public: - MatchInfoHMP(int idxHMPClus, GTrackID idxTrack, float angle = 0, float q = 0, float size = 0, int idxPhotClus = 0) : mIdxHMPClus(idxHMPClus), mIdxTrack(idxTrack), mCkovAngle(angle), mMipCluQ(q), mMipCluSize(size), mIdxPhotClus(idxPhotClus){}; + MatchInfoHMP(int idxHMPClus, GTrackID idxTrack, float xmip = 0, float ymip = 0, float xtrk = 0, float ytrk = 0, float theta = 0, float phi = 0, float angle = 0, float size = 0, int idxPhotClus = 0, int hmpqn = 0, float hmpMom = 0) : mIdxHMPClus(idxHMPClus), mIdxTrack(idxTrack), mCkovAngle(angle), mMipX(xmip), mMipY(ymip), mTrkX(xtrk), mTrkY(ytrk), mTrkTheta(theta), mTrkPhi(phi), mMipCluSize(size), mIdxPhotClus(idxPhotClus), mHMPqn(hmpqn), mHmpMom(hmpMom) + { // Initialize the mPhotCharge array + for (int i = 0; i < 10; i++) { + mPhotCharge[i] = 0.0; + } + }; MatchInfoHMP() = default; - void setIdxHMPClus(int index) { mIdxHMPClus = index; } + void setIdxHMPClus(int ch, int idx) { mIdxHMPClus = ch * 1000000 + idx; } int getIdxHMPClus() const { return mIdxHMPClus; } void setIdxTrack(GTrackID index) { mIdxTrack = index; } + int getTrackIndex() const { return mIdxTrack.getIndex(); } + GTrackID getTrackRef() const { return mIdxTrack; } - int getTrackIndex() const { return mIdxTrack.getIndex(); } + void setMipX(float x) { mMipX = x; } + float getMipX() const { return mMipX; } - void setCkovAngle(float angle) { mCkovAngle = angle; } - float getCkovAngle() const { return mCkovAngle; } + void setMipY(float y) { mMipY = y; } + float getMipY() const { return mMipY; } - void setMipClusQ(float q) { mMipCluQ = q; } - float getMipClusQ() const { return mMipCluQ; } + void setTrkX(float x) { mTrkX = x; } + float getTrkX() const { return mTrkX; } + + void setTrkY(float y) { mTrkY = y; } + float getTrkY() const { return mTrkY; } + + void setHMPsignal(float angle) { mCkovAngle = angle; } + float getHMPsignal() const + { + if (mCkovAngle > 0) { + return mCkovAngle - (Int_t)mCkovAngle; + } else { + return mCkovAngle; + } + } void setMipClusSize(int size) { mMipCluSize = size; } int getMipClusSize() const { return mMipCluSize; } @@ -54,18 +75,71 @@ class MatchInfoHMP void setPhotIndex(int idx) { mIdxPhotClus = idx; } int getPhotIndex() const { return mIdxPhotClus; } + float getOccupancy() const { return (Int_t)mCkovAngle / 10.0; } + + void setHMPIDtrk(float x, float y, float th, float ph) + { + mTrkX = x; + mTrkY = y; + mTrkTheta = th; + mTrkPhi = ph; + } + + void getHMPIDtrk(float& x, float& y, float& th, float& ph) const + { + x = mTrkX; + y = mTrkY; + th = mTrkTheta; + ph = mTrkPhi; + } + + void setHMPIDmip(float x, float y, int q, int nph = 0) + { + mMipX = x; + mMipY = y; + mHMPqn = 1000000 * nph + q; + } + + void getHMPIDmip(float& x, float& y, int& q, int& nph) const + { + x = mMipX; + y = mMipY; + q = mHMPqn % 1000000; + nph = mHMPqn / 1000000; + } + + void setHmpMom(float p) { mHmpMom = p; } + float getHmpMom() const { return mHmpMom; } + + void setPhotCharge(float* chargeArray) + { + for (int i = 0; i < 10; i++) { + mPhotCharge[i] = chargeArray[i]; + } + } + + const float* getPhotCharge() const { return mPhotCharge; } + void print() const; private: int mIdxHMPClus; // Idx for HMP cluster GTrackID mIdxTrack; // Idx for track + float mMipX; // local x coordinate of macthed cluster + float mMipY; // local y coordinate of macthed cluster + float mTrkX; // local x coordinate of extrapolated track intersection point + float mTrkY; // local y coordinate of extrapolated track intersection point + float mTrkTheta; // theta track + float mTrkPhi; // phi track float mCkovAngle; // emission angle value - float mMipCluQ = 0.0; // MIP cluster charge int mMipCluSize = 0.0; // MIP cluster size int mNPhots = 0.0; // number of candidate photo-electrons int mIdxPhotClus; // index of the first photo + int mHMPqn; // 1000000*number of photon clusters + QDC + float mHmpMom; // track momentum at HMPID chambers + float mPhotCharge[10] = {}; - ClassDefNV(MatchInfoHMP, 1); + ClassDefNV(MatchInfoHMP, 2); }; } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h index 9f6b2f00bb37c..7bcfd7af0911a 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h @@ -28,7 +28,7 @@ class MatchInfoTOF using GTrackID = o2::dataformats::GlobalTrackID; public: - MatchInfoTOF(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, float dt = 0, float z = 0, float dx = 0, float dz = 0) : mIdLocal(idLocal), mIdxTOFCl(idxTOFCl), mSignal(time), mChi2(chi2), mIntLT(trkIntLT), mIdxTrack(idxTrack), mDeltaT(dt), mZatTOF(z), mDXatTOF(dx), mDZatTOF(dz){}; + MatchInfoTOF(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, float dt = 0, float z = 0, float dx = 0, float dz = 0, float dy = 0, float geanttime = 0.0, double t0 = 0.0) : mIdLocal(idLocal), mIdxTOFCl(idxTOFCl), mSignal(time), mChi2(chi2), mIntLT(trkIntLT), mIdxTrack(idxTrack), mDeltaT(dt), mZatTOF(z), mDXatTOF(dx), mDZatTOF(dz), mDYatTOF(dy), mTgeant(geanttime), mT0true(t0){}; MatchInfoTOF() = default; void setIdxTOFCl(int index) { mIdxTOFCl = index; } void setIdxTrack(GTrackID index) { mIdxTrack = index; } @@ -38,9 +38,15 @@ class MatchInfoTOF int getTOFClIndex() const { return mIdxTOFCl; } int getTrackIndex() const { return mIdxTrack.getIndex(); } - void setChi2(int chi2) { mChi2 = chi2; } + void setChi2(float chi2) { mChi2 = chi2; } float getChi2() const { return mChi2; } + void setHitPatternUpDown(bool v) { mHitUpDown = v; } + bool getHitPatternUpDown() const { return mHitUpDown; } + + void setHitPatternLeftRight(bool v) { mHitLeftRight = v; } + bool getHitPatternLeftRight() const { return mHitLeftRight; } + o2::track::TrackLTIntegral& getLTIntegralOut() { return mIntLT; } const o2::track::TrackLTIntegral& getLTIntegralOut() const { return mIntLT; } void print() const; @@ -53,11 +59,41 @@ class MatchInfoTOF float getDZatTOF() const { return mDZatTOF; } void setDXatTOF(float val) { mDXatTOF = val; } float getDXatTOF() const { return mDXatTOF; } + void setDYatTOF(float val) { mDYatTOF = val; } + float getDYatTOF() const { return mDYatTOF; } void setSignal(double time) { mSignal = time; } double getSignal() const { return mSignal; } int getIdLocal() const { return mIdLocal; } + float getVz() const { return mVz; } + void setVz(float val) { mVz = val; } + int getChannel() const { return mChannel; } + void setChannel(int val) { mChannel = val; } + float getTgeant() const { return mTgeant; } + void setTgeant(float val) { mTgeant = val; } + double getT0true() const { return mT0true; } + void setT0true(double val) { mT0true = val; } + + enum QualityFlags { isMultiHitX = 0x1 << 0, + isMultiHitZ = 0x1 << 1, + badDy = 0x1 << 2, + isMultiStrip = 0x1 << 3, + isNotInPad = 0x1 << 4, + chiGT3 = 0x1 << 5, + chiGT5 = 0x1 << 6, + hasT0sameBC = 0x1 << 7, + hasT0_1BCbefore = 0x1 << 8, + hasT0_2BCbefore = 0x1 << 9 }; + + void setFT0Best(double val, float res = 200.) + { + mFT0Best = val; + mFT0BestRes = res; + } + double getFT0Best() const { return mFT0Best; } + float getFT0BestRes() const { return mFT0BestRes; } + private: int mIdLocal; // track id in sector of the pair track-TOFcluster float mChi2; // chi2 of the pair track-TOFcluster @@ -67,10 +103,21 @@ class MatchInfoTOF float mZatTOF = 0.0; ///< Z position at TOF float mDXatTOF = 0.0; ///< DX position at TOF float mDZatTOF = 0.0; ///< DZ position at TOF + float mDYatTOF = 0.0; ///< DY position at TOF float mDeltaT = 0.0; ///< tTOF - TPC (microsec) double mSignal = 0.0; ///< TOF time in ps + float mVz = 0.0; ///< Vz from TOF match + int mChannel = -1; ///< channel + // Hit pattern information + bool mHitUpDown = false; ///< hit pattern in TOF up-down + bool mHitLeftRight = false; ///< hit pattern in TOF left-right + float mTgeant = 0.0; ///< geant time in MC + double mT0true = 0.0; ///< t0true + + double mFT0Best = 0.0; //< best info for collision time + float mFT0BestRes = 200.0; //< resolution (in ps) of the best info for collision time - ClassDefNV(MatchInfoTOF, 5); + ClassDefNV(MatchInfoTOF, 9); }; } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h index b1d3830e8083c..f1b555301bf80 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h @@ -35,13 +35,22 @@ class MatchInfoTOFReco : public MatchInfoTOF ITSTPCTRD, SIZEALL }; - MatchInfoTOFReco(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, TrackType trkType, float dt = 0, float z = 0, float dx = 0, float dz = 0) : MatchInfoTOF(idLocal, idxTOFCl, time, chi2, trkIntLT, idxTrack, dt, z, dx, dz), mTrackType(trkType){}; + MatchInfoTOFReco(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, TrackType trkType, float dt = 0, float z = 0, float dx = 0, float dz = 0, float dy = 0) : MatchInfoTOF(idLocal, idxTOFCl, time, chi2, trkIntLT, idxTrack, dt, z, dx, dz, dy), mTrackType(trkType){}; MatchInfoTOFReco() = default; void setFakeMatch() { mFakeMC = true; } void resetFakeMatch() { mFakeMC = false; } bool isFake() const { return mFakeMC; } + float pt() const { return mPt; } + void setPt(float pt) { mPt = pt; } + + void setResX(float val) { mResX = val; } + void setResZ(float val) { mResZ = val; } + void setResT(float val) { mResT = val; } + float getResX() const { return mResX; } + float getResZ() const { return mResZ; } + float getResT() const { return mResT; } void setTrackType(TrackType value) { mTrackType = value; } TrackType getTrackType() const { return mTrackType; } @@ -49,7 +58,11 @@ class MatchInfoTOFReco : public MatchInfoTOF private: TrackType mTrackType; ///< track type (TPC, ITSTPC, TPCTRD, ITSTPCTRD) bool mFakeMC = false; - ClassDefNV(MatchInfoTOFReco, 3); + float mPt = 0; + float mResX = 1; + float mResZ = 1; + float mResT = 1; + ClassDefNV(MatchInfoTOFReco, 5); }; } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h index f7124f12902d3..ce70e69aa6ddd 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h @@ -16,6 +16,10 @@ #ifndef ALICEO2_track_PID_H_ #define ALICEO2_track_PID_H_ +#ifndef GPUCA_GPUCODE_DEVICE +#include +#endif + #include "GPUCommonDef.h" #include "GPUCommonRtypes.h" #include "CommonConstants/PhysicsConstants.h" @@ -29,16 +33,19 @@ namespace o2cp = o2::constants::physics; namespace pid_constants // GPUs currently cannot have static constexpr array members { typedef uint8_t ID; -static constexpr ID NIDsTot = 17; +static constexpr ID NIDsTot = 19; + +#if !defined(GPUCA_GPUCODE_DEVICE) || defined(GPUCA_GPU_DEBUG_PRINT) GPUconstexpr() const char* sNames[NIDsTot + 1] = ///< defined particle names {"Electron", "Muon", "Pion", "Kaon", "Proton", "Deuteron", "Triton", "He3", "Alpha", - "Pion0", "Photon", "K0", "Lambda", "HyperTriton", "Hyperhydrog4", "XiMinus", "OmegaMinus", nullptr}; + "Pion0", "Photon", "K0", "Lambda", "HyperTriton", "Hyperhydrog4", "XiMinus", "OmegaMinus", "HyperHelium4", "HyperHelium5", nullptr}; +#endif GPUconstexpr() const float sMasses[NIDsTot] = ///< defined particle masses {o2cp::MassElectron, o2cp::MassMuon, o2cp::MassPionCharged, o2cp::MassKaonCharged, o2cp::MassProton, o2cp::MassDeuteron, o2cp::MassTriton, o2cp::MassHelium3, o2cp::MassAlpha, o2cp::MassPionNeutral, o2cp::MassPhoton, - o2cp::MassKaonNeutral, o2cp::MassLambda, o2cp::MassHyperTriton, o2cp::MassHyperhydrog4, o2cp::MassXiMinus, o2cp::MassOmegaMinus}; + o2cp::MassKaonNeutral, o2cp::MassLambda, o2cp::MassHyperTriton, o2cp::MassHyperhydrog4, o2cp::MassXiMinus, o2cp::MassOmegaMinus, o2cp::MassHyperHelium4, o2cp::MassHyperHelium5}; GPUconstexpr() const float sMasses2[NIDsTot] = ///< defined particle masses^2 {o2cp::MassElectron * o2cp::MassElectron, @@ -57,7 +64,9 @@ GPUconstexpr() const float sMasses2[NIDsTot] = ///< defined particle masses^2 o2cp::MassHyperTriton* o2cp::MassHyperTriton, o2cp::MassHyperhydrog4* o2cp::MassHyperhydrog4, o2cp::MassXiMinus* o2cp::MassXiMinus, - o2cp::MassOmegaMinus* o2cp::MassOmegaMinus}; + o2cp::MassOmegaMinus* o2cp::MassOmegaMinus, + o2cp::MassHyperHelium4* o2cp::MassHyperHelium4, + o2cp::MassHyperHelium5* o2cp::MassHyperHelium5}; GPUconstexpr() const float sMasses2Z[NIDsTot] = ///< defined particle masses / Z {o2cp::MassElectron, o2cp::MassMuon, @@ -66,12 +75,14 @@ GPUconstexpr() const float sMasses2Z[NIDsTot] = ///< defined particle masses / Z o2cp::MassTriton, o2cp::MassHelium3 / 2., o2cp::MassAlpha / 2., 0, 0, 0, 0, o2cp::MassHyperTriton, o2cp::MassHyperhydrog4, - o2cp::MassXiMinus, o2cp::MassOmegaMinus}; + o2cp::MassXiMinus, o2cp::MassOmegaMinus, + o2cp::MassHyperHelium4 / 2., o2cp::MassHyperHelium5 / 2.}; GPUconstexpr() const int sCharges[NIDsTot] = ///< defined particle charges {1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, - 1, 1}; + 1, 1, + 2, 2}; } // namespace pid_constants class PID @@ -103,8 +114,10 @@ class PID static constexpr ID Hyperhydrog4 = 14; static constexpr ID XiMinus = 15; static constexpr ID OmegaMinus = 16; + static constexpr ID HyperHelium4 = 17; + static constexpr ID HyperHelium5 = 18; static constexpr ID FirstExt = PI0; - static constexpr ID LastExt = OmegaMinus; + static constexpr ID LastExt = HyperHelium5; static constexpr ID NIDsTot = pid_constants::NIDsTot; ///< total number of defined IDs static_assert(NIDsTot == LastExt + 1, "Incorrect NIDsTot, please update!"); @@ -112,7 +125,7 @@ class PID GPUd() PID(ID id) : mID(id) {} GPUd() PID(const char* name); GPUdDefault() PID(const PID& src) = default; - GPUdDefault() PID& operator=(const PID& src) = default; + GPUhdDefault() PID& operator=(const PID& src) = default; GPUd() ID getID() const { return mID; } GPUd() operator ID() const { return getID(); } @@ -126,7 +139,7 @@ class PID GPUd() static float getMass2(ID id) { return pid_constants::sMasses2[id]; } GPUd() static float getMass2Z(ID id) { return pid_constants::sMasses2Z[id]; } GPUd() static int getCharge(ID id) { return pid_constants::sCharges[id]; } -#ifndef GPUCA_GPUCODE_DEVICE +#if !defined(GPUCA_GPUCODE_DEVICE) || defined(GPUCA_GPU_DEBUG_PRINT) GPUd() const char* getName() const { return getName(mID); @@ -137,13 +150,13 @@ class PID private: ID mID = Pion; +#if !defined(GPUCA_GPUCODE_DEVICE) || defined(GPUCA_GPU_DEBUG_PRINT) // are 2 strings equal ? (trick from Giulio) GPUdi() static constexpr bool sameStr(char const* x, char const* y) { return !*x && !*y ? true : /* default */ (*x == *y && sameStr(x + 1, y + 1)); } -#ifndef GPUCA_GPUCODE_DEVICE GPUdi() static constexpr ID nameToID(char const* name, ID id) { return id > LastExt ? id : sameStr(name, pid_constants::sNames[id]) ? id : nameToID(name, id + 1); diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h index ce470361034f5..62de31d335e80 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h @@ -37,7 +37,12 @@ class PrimaryVertex : public Vertex> void setIR(const InteractionRecord& ir) { mIRMin = mIRMax = ir; } bool hasUniqueIR() const { return !mIRMin.isDummy() && (mIRMin == mIRMax); } -#ifndef GPUCA_ALIGPUCODE + float getTMAD() const { return mTMAD; } + void setTMAD(float v) { mTMAD = v; } + float getZMAD() const { return mZMAD; } + void setZMAD(float v) { mZMAD = v; } + +#ifndef GPUCA_GPUCODE_DEVICE void print() const; std::string asString() const; #endif @@ -45,11 +50,13 @@ class PrimaryVertex : public Vertex> protected: InteractionRecord mIRMin{}; ///< by default not assigned! InteractionRecord mIRMax{}; ///< by default not assigned! + float mTMAD = -1; ///< MAD estimator for Tsigma + float mZMAD = -1; ///< MAD estimator for Zsigma - ClassDefNV(PrimaryVertex, 1); + ClassDefNV(PrimaryVertex, 2); }; -#ifndef GPUCA_ALIGPUCODE +#ifndef GPUCA_GPUCODE_DEVICE std::ostream& operator<<(std::ostream& os, const o2::dataformats::PrimaryVertex& v); #endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertexExt.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertexExt.h new file mode 100644 index 0000000000000..4fd36ed248677 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertexExt.h @@ -0,0 +1,66 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_PRIMARYVERTEX_EXT_H +#define ALICEO2_PRIMARYVERTEX_EXT_H + +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "SimulationDataFormat/MCEventLabel.h" + +namespace o2 +{ +namespace dataformats +{ + +// extended primary vertex info + +struct PrimaryVertexExt : public PrimaryVertex { + using PrimaryVertex::PrimaryVertex; + std::array nSrc{}; // N contributors for each source type + std::array nSrcA{}; // N associated and passing cuts for each source type + std::array nSrcAU{}; // N ambgous associated and passing cuts for each source type + o2::MCEventLabel mcLb{}; + double FT0Time = -1.; // time of closest FT0 trigger + float FT0A = -1; // amplitude of closest FT0 A side + float FT0C = -1; // amplitude of closest FT0 C side + int VtxID = -1; // original vtx ID + + int getNSrc(int i) const { return nSrc[i]; } + int getNSrcA(int i) const { return nSrcA[i]; } + int getNSrcAU(int i) const { return nSrcAU[i]; } + +#ifndef GPUCA_GPUCODE_DEVICE + void print() const; + std::string asString() const; +#endif + + ClassDefNV(PrimaryVertexExt, 7); +}; + +#ifndef GPUCA_GPUCODE_DEVICE +std::ostream& operator<<(std::ostream& os, const o2::dataformats::PrimaryVertexExt& v); +#endif + +} // namespace dataformats + +/// Defining PrimaryVertexExt explicitly as messageable +namespace framework +{ +template +struct is_messageable; +template <> +struct is_messageable : std::true_type { +}; +} // namespace framework + +} // namespace o2 +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/StrangeTrack.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/StrangeTrack.h new file mode 100644 index 0000000000000..12e376f10f3d8 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/StrangeTrack.h @@ -0,0 +1,86 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file StrangeTrack.h +/// \brief +/// + +#ifndef _ALICEO2_STRANGETRACK_ +#define _ALICEO2_STRANGETRACK_ + +#include +#include "ReconstructionDataFormats/Track.h" + +namespace o2 +{ +namespace dataformats +{ + +enum kPartType { kStrkV0, + kStrkCascade, + kStrkThreeBody }; + +struct StrangeTrack { + kPartType mPartType; + o2::track::TrackParCovF mMother; + unsigned int mITSRef = -1; + unsigned int mDecayRef = -1; + std::array mDecayVtx; + std::array mDecayMom; + std::array mMasses; // V0: hypertriton and hyperhydrongen4, cascade: Xi and Omega. + unsigned int mClusterSizes = 0u; // same encoding used for the ITS track + float mMatchChi2; + float mTopoChi2; + + void setClusterSize(int l, int size) + { + if (l >= 8) { + return; + } + if (size > 15) { + size = 15; + } + mClusterSizes &= ~(0xf << (l * 4)); + mClusterSizes |= (size << (l * 4)); + } + + int getClusterSize(int l) const + { + if (l >= 7) { + return 0; + } + return (mClusterSizes >> (l * 4)) & 0xf; + } + + int getClusterSizes() const + { + return mClusterSizes; + } + + float getAverageClusterSize() const + { + int nClusters = 0; + int nClustersSize = 0; + for (int i = 0; i < 7; ++i) { + int size = getClusterSize(i); + if (size > 0) { + nClustersSize += size; + nClusters++; + } + } + return nClusters > 0 ? static_cast(nClustersSize) / nClusters : 0.f; + } +}; + +} // namespace dataformats +} // namespace o2 + +#endif // _ALICEO2_STRANGETRACK_ diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h index 01c873496b5a6..fca117583027f 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h @@ -31,6 +31,12 @@ using SMatrix55Sym = ROOT::Math::SMatrix; using SMatrix5 = ROOT::Math::SVector; +template +class TrackParametrization; // fwd declaration for conversion method + +template +class TrackParametrizationWithError; // fwd declaration for conversion method + class TrackParFwd { // Forward track parameterization, kinematics only. public: @@ -42,6 +48,9 @@ class TrackParFwd TrackParFwd(TrackParFwd&&) = delete; TrackParFwd& operator=(TrackParFwd&&) = delete; + template + void toBarrelTrackPar(TrackParametrization& t) const; + /// return Z coordinate (cm) Double_t getZ() const { return mZ; } /// set Z coordinate (cm) @@ -145,6 +154,9 @@ class TrackParCovFwd : public TrackParFwd TrackParCovFwd& operator=(const TrackParCovFwd& tpf) = default; TrackParCovFwd(const Double_t z, const SMatrix5& parameters, const SMatrix55Sym& covariances, const Double_t chi2); + template + void toBarrelTrackParCov(TrackParametrizationWithError& t) const; + const SMatrix55Sym& getCovariances() const { return mCovariances; } void setCovariances(const SMatrix55Sym& covariances) { mCovariances = covariances; } void deleteCovariances() { mCovariances = SMatrix55Sym(); } @@ -161,6 +173,7 @@ class TrackParCovFwd : public TrackParFwd void propagateToZquadratic(double zEnd, double zField); void propagateToZhelix(double zEnd, double zField); void propagateToZ(double zEnd, double zField); // Parameters: helix; errors: quadratic + void propagateToDCAhelix(double zField, const std::array& p, std::array& dca); // Add Multiple Coulomb Scattering effects void addMCSEffect(double x2X0); @@ -168,6 +181,12 @@ class TrackParCovFwd : public TrackParFwd // Kalman filter/fitting bool update(const std::array& p, const std::array& cov); + // Propagate fwd track to vertex including MCS effects + bool propagateToVtxhelixWithMCS(double z, const std::array& p, const std::array& cov, double field, double x_over_X0); + bool propagateToVtxlinearWithMCS(double z, const std::array& p, const std::array& cov, double x_over_X0); + + bool getCovXYZPxPyPzGlo(std::array& cv) const; + private: /// Covariance matrix of track parameters, ordered as follows:
   ///                                     
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackHMP.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackHMP.h
new file mode 100644
index 0000000000000..8bef3a40b603a
--- /dev/null
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackHMP.h
@@ -0,0 +1,54 @@
+// Copyright 2020-2022 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#ifndef ALICEO2_TRACKHMP_H
+#define ALICEO2_TRACKHMP_H
+
+#include "TMath.h"
+#include "TVector2.h"
+
+#include "ReconstructionDataFormats/TrackParametrizationWithError.h"
+#include "ReconstructionDataFormats/TrackParametrization.h"
+#include "ReconstructionDataFormats/Track.h"
+#include "ReconstructionDataFormats/TrackLTIntegral.h"
+#include "ReconstructionDataFormats/GlobalTrackID.h"
+
+// #include "HMPIDBase/Param.h"                 // for param
+
+namespace o2
+{
+namespace dataformats
+{
+
+class TrackHMP : public o2::track::TrackParCov
+{
+ public:
+  TrackHMP();
+  TrackHMP(const TrackHMP& t) = default;
+  TrackHMP(const o2::track::TrackParCov& t) : o2::track::TrackParCov{t} {};
+  //  TrackHMP(const o2::track::TrackParCov& t);
+  TrackHMP& operator=(const o2::track::TrackParCov& t);
+  ~TrackHMP() = default;
+
+  Bool_t intersect(Double_t pnt[3], Double_t norm[3], double bz) const; //
+  void propagate(Double_t len, std::array& x, std::array& p, double bz) const;
+
+  // protected:
+  //    Bool_t   Update(const AliCluster */*c*/, Double_t /*chi2*/, Int_t /*idx*/) {return 0;}
+  //    Double_t GetPredictedChi2(const AliCluster */*c*/) const {return 0.;}
+ private:
+  ClassDef(TrackHMP, 1) // HMPID reconstructed tracks
+};
+
+} // namespace dataformats
+} // namespace o2
+
+#endif
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h
index 6cf9ceda8e195..e799804805972 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h
@@ -63,7 +63,7 @@ class TrackLTIntegral
     }
   }
 
-  GPUd() void addStep(float dL, float p2Inv);
+  GPUd() void addStep(float dL, float q2p2);
   GPUd() void addX2X0(float d) { mX2X0 += d; }
   GPUd() void addXRho(float d) { mXRho += d; }
 
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h
index 5d7f3f4415a16..918633d914230 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h
@@ -29,7 +29,6 @@
 #include "GPUCommonDef.h"
 #include "GPUCommonRtypes.h"
 #include "GPUCommonMath.h"
-#include "GPUCommonArray.h"
 #include "GPUROOTCartesianFwd.h"
 
 #ifndef GPUCA_GPUCODE_DEVICE
@@ -39,9 +38,10 @@
 #include 
 #include 
 #include 
+#include 
 #endif
 
-#ifndef GPUCA_ALIGPUCODE //Used only by functions that are hidden on the GPU
+#ifndef GPUCA_ALIGPUCODE // Used only by functions that are hidden on the GPU
 #include "ReconstructionDataFormats/BaseCluster.h"
 #include 
 #endif
@@ -66,6 +66,9 @@ class DCA;
 
 namespace track
 {
+
+class TrackParFwd; // fwd declaration for conversion method
+
 // aliases for track elements
 enum ParLabels : int { kY,
                        kZ,
@@ -115,6 +118,15 @@ GPUconstexpr() int CovarMap[kNParams][kNParams] = {{0, 1, 3, 6, 10},
 GPUconstexpr() int DiagMap[kNParams] = {0, 2, 5, 9, 14};
 
 constexpr float HugeF = o2::constants::math::VeryBig;
+constexpr float MaxPT = 100000.;                  // do not allow pTs exceeding this value (to avoid NANs)
+constexpr float MinPTInv = 1. / MaxPT;            // do not allow q/pTs less this value (to avoid NANs)
+constexpr float ELoss2EKinThreshInv = 1. / 0.025; // do not allow E.Loss correction step with dE/Ekin above the inverse of this value
+constexpr int MaxELossIter = 50;                  // max number of iteration for the ELoss to account for BB dependence on beta*gamma
+constexpr float DefaultDCA = 999.f;               // default DCA value
+constexpr float DefaultDCACov = 999.f;            // default DCA cov value
+
+// uncomment this to enable correction for BB dependence on beta*gamma via BB derivative
+// #define _BB_NONCONST_CORR_
 
 template 
 class TrackParametrization
@@ -122,9 +134,9 @@ class TrackParametrization
 
  public:
   using value_t = value_T;
-  using dim2_t = gpu::gpustd::array;
-  using dim3_t = gpu::gpustd::array;
-  using params_t = gpu::gpustd::array;
+  using dim2_t = std::array;
+  using dim3_t = std::array;
+  using params_t = std::array;
 
   struct yzerr_t { // 2 measurement with error
     dim2_t yz;
@@ -140,8 +152,8 @@ class TrackParametrization
   GPUd() TrackParametrization(const dim3_t& xyz, const dim3_t& pxpypz, int charge, bool sectorAlpha = true, const PID pid = PID::Pion);
   GPUdDefault() TrackParametrization(const TrackParametrization&) = default;
   GPUdDefault() TrackParametrization(TrackParametrization&&) = default;
-  GPUdDefault() TrackParametrization& operator=(const TrackParametrization& src) = default;
-  GPUdDefault() TrackParametrization& operator=(TrackParametrization&& src) = default;
+  GPUhdDefault() TrackParametrization& operator=(const TrackParametrization& src) = default;
+  GPUhdDefault() TrackParametrization& operator=(TrackParametrization&& src) = default;
   GPUdDefault() ~TrackParametrization() = default;
 
   GPUd() void set(value_t x, value_t alpha, const params_t& par, int charge = 1, const PID pid = PID::Pion);
@@ -154,11 +166,13 @@ class TrackParametrization
   GPUd() value_t getZ() const;
   GPUd() value_t getSnp() const;
   GPUd() value_t getTgl() const;
-  GPUd() value_t getQ2Pt() const;
+  GPUhd() value_t getQ2Pt() const;
   GPUd() value_t getCharge2Pt() const;
+  GPUd() value_t getR2() const;
+  GPUd() value_t getR() const;
   GPUd() int getAbsCharge() const;
   GPUd() PID getPID() const;
-  GPUd() void setPID(const PID pid);
+  GPUd() void setPID(const PID pid, bool passCharge = false);
 
   /// calculate cos^2 and cos of track direction in rphi-tracking
   GPUd() value_t getCsp2() const;
@@ -185,19 +199,25 @@ class TrackParametrization
   GPUd() value_t getPhi() const;
   GPUd() value_t getPhiPos() const;
 
+  GPUd() value_t getQ2P2() const;
   GPUd() value_t getPtInv() const;
   GPUd() value_t getP2Inv() const;
   GPUd() value_t getP2() const;
   GPUd() value_t getPInv() const;
   GPUd() value_t getP() const;
   GPUd() value_t getPt() const;
+  GPUd() value_t getE2() const;
+  GPUd() value_t getE() const;
+  GPUdi() static value_t getdEdxBB(value_t betagamma) { return BetheBlochSolid(betagamma); }
+  GPUdi() static value_t getdEdxBBOpt(value_t betagamma) { return BetheBlochSolidOpt(betagamma); }
+  GPUdi() static value_t getBetheBlochSolidDerivativeApprox(value_T dedx, value_T bg) { return BetheBlochSolidDerivative(dedx, bg); }
 
   GPUd() value_t getTheta() const;
   GPUd() value_t getEta() const;
   GPUd() math_utils::Point3D getXYZGlo() const;
   GPUd() void getXYZGlo(dim3_t& xyz) const;
   GPUd() bool getPxPyPzGlo(dim3_t& pxyz) const;
-  GPUd() bool getPosDirGlo(gpu::gpustd::array& posdirp) const;
+  GPUd() bool getPosDirGlo(std::array& posdirp) const;
 
   // methods for track params estimate at other point
   GPUd() bool getYZAt(value_t xk, value_t b, value_t& y, value_t& z) const;
@@ -205,11 +225,16 @@ class TrackParametrization
   GPUd() value_t getYAt(value_t xk, value_t b) const;
   GPUd() value_t getSnpAt(value_t xk, value_t b) const;
   GPUd() value_t getSnpAt(value_t alpha, value_t xk, value_t b) const;
+  GPUd() value_t getPhiAt(value_t xk, value_t b) const;
+  GPUd() value_t getPhiPosAt(value_t xk, value_t b) const;
+  GPUd() value_t getDCAYtoMV(value_t b, value_t xmv = 0.f, value_t ymv = 0.f, value_t zmv = 0.f) const;
+  GPUd() value_t getDCAZtoMV(value_t b, value_t xmv = 0.f, value_t ymv = 0.f, value_t zmv = 0.f) const;
   GPUd() math_utils::Point3D getXYZGloAt(value_t xk, value_t b, bool& ok) const;
 
   // parameters manipulation
-  GPUd() bool correctForELoss(value_t xrho, bool anglecorr = false, value_t dedx = kCalcdEdxAuto);
+  GPUd() bool correctForELoss(value_t xrho, bool anglecorr = false);
   GPUd() bool rotateParam(value_t alpha);
+  GPUd() bool rotateParam(value_t& alpha, value_t& ca, value_t& sa);
   GPUd() bool propagateParamTo(value_t xk, value_t b);
   GPUd() bool propagateParamTo(value_t xk, const dim3_t& b);
   GPUd() void invertParam();
@@ -228,8 +253,13 @@ class TrackParametrization
   GPUhd() void setUserField(uint16_t v);
 
   GPUd() void printParam() const;
+  GPUd() void printParamHexadecimal();
 #ifndef GPUCA_ALIGPUCODE
+  void toFwdTrackPar(TrackParFwd& t) const;
   std::string asString() const;
+  std::string asStringHexadecimal();
+  size_t hash() const { return hash(getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt()); }
+  static size_t hash(float x, float alp, float y, float z, float snp, float tgl, float q2pt);
 #endif
 
   GPUd() void updateParam(value_t delta, int i);
@@ -257,6 +287,7 @@ GPUdi() TrackParametrization::TrackParametrization(value_t x, value_t a
   : mX{x}, mAlpha{alpha}, mAbsCharge{char(gpu::CAMath::Abs(charge))}, mPID{pid}
 {
   // explicit constructor
+  math_utils::detail::bringToPMPi(mAlpha);
   for (int i = 0; i < kNParams; i++) {
     mP[i] = par[i];
   }
@@ -275,6 +306,7 @@ GPUdi() void TrackParametrization::set(value_t x, value_t alpha, const
 {
   mX = x;
   mAlpha = alpha;
+  math_utils::detail::bringToPMPi(mAlpha);
   mAbsCharge = char(gpu::CAMath::Abs(charge));
   for (int i = 0; i < kNParams; i++) {
     mP[i] = par[i];
@@ -340,7 +372,7 @@ GPUdi() auto TrackParametrization::getTgl() const -> value_t
 
 //____________________________________________________________
 template 
-GPUdi() auto TrackParametrization::getQ2Pt() const -> value_t
+GPUhdi() auto TrackParametrization::getQ2Pt() const -> value_t
 {
   return mP[kQ2Pt];
 }
@@ -352,6 +384,20 @@ GPUdi() auto TrackParametrization::getCharge2Pt() const -> value_t
   return mAbsCharge ? mP[kQ2Pt] : 0.f;
 }
 
+//____________________________________________________________
+template 
+GPUdi() auto TrackParametrization::getR2() const -> value_t
+{
+  return mX * mX + mP[kY] * mP[kY];
+}
+
+//____________________________________________________________
+template 
+GPUdi() auto TrackParametrization::getR() const -> value_t
+{
+  return gpu::CAMath::Sqrt(getR2());
+}
+
 //____________________________________________________________
 template 
 GPUdi() int TrackParametrization::getAbsCharge() const
@@ -368,10 +414,12 @@ GPUdi() PID TrackParametrization::getPID() const
 
 //____________________________________________________________
 template 
-GPUdi() void TrackParametrization::setPID(const PID pid)
+GPUdi() void TrackParametrization::setPID(const PID pid, bool passCharge)
 {
   mPID = pid;
-  //  setAbsCharge(pid.getCharge()); // If needed, user should change the charge via corr. setter
+  if (passCharge) {
+    setAbsCharge(pid.getCharge()); // If needed, user should change the charge via corr. setter
+  }
 }
 
 //____________________________________________________________
@@ -408,6 +456,7 @@ template 
 GPUdi() void TrackParametrization::setAlpha(value_t v)
 {
   mAlpha = v;
+  math_utils::detail::bringToPMPi(mAlpha);
 }
 
 //____________________________________________________________
@@ -536,12 +585,27 @@ GPUdi() auto TrackParametrization::getPhiPos() const -> value_t
   return phi;
 }
 
+//____________________________________________________________
+template 
+GPUdi() auto TrackParametrization::getQ2P2() const -> value_t
+{
+  // return the (q/p)^2
+  value_t q2pt2 = mP[kQ2Pt] * mP[kQ2Pt];
+  if (q2pt2 < MinPTInv * MinPTInv) {
+    q2pt2 = MinPTInv * MinPTInv;
+  }
+  return q2pt2 / (1.f + getTgl() * getTgl());
+}
+
 //____________________________________________________________
 template 
 GPUdi() auto TrackParametrization::getPtInv() const -> value_t
 {
   // return the inverted track pT
-  const value_t ptInv = gpu::CAMath::Abs(mP[kQ2Pt]);
+  value_t ptInv = gpu::CAMath::Abs(mP[kQ2Pt]);
+  if (ptInv < MinPTInv) {
+    ptInv = MinPTInv;
+  }
   return (mAbsCharge > 1) ? ptInv / mAbsCharge : ptInv;
 }
 
@@ -550,8 +614,8 @@ template 
 GPUdi() auto TrackParametrization::getP2Inv() const -> value_t
 {
   // return the inverted track momentum^2
-  const value_t p2 = mP[kQ2Pt] * mP[kQ2Pt] / (1.f + getTgl() * getTgl());
-  return (mAbsCharge > 1) ? p2 * mAbsCharge * mAbsCharge : p2;
+  value_t p2 = getPtInv();
+  return p2 * p2 / (1.f + getTgl() * getTgl());
 }
 
 //____________________________________________________________
@@ -559,17 +623,15 @@ template 
 GPUdi() auto TrackParametrization::getP2() const -> value_t
 {
   // return the track momentum^2
-  const value_t p2inv = getP2Inv();
-  return (p2inv > o2::constants::math::Almost0) ? 1.f / p2inv : o2::constants::math::VeryBig;
+  return 1.f / getP2Inv(); // getP2Inv is protected against being 0, full charge accounted
 }
 
 //____________________________________________________________
 template 
 GPUdi() auto TrackParametrization::getPInv() const -> value_t
 {
-  // return the inverted track momentum^2
-  const value_t pInv = gpu::CAMath::Abs(mP[kQ2Pt]) / gpu::CAMath::Sqrt(1.f + getTgl() * getTgl());
-  return (mAbsCharge > 1) ? pInv / mAbsCharge : pInv;
+  // return the inverted track momentum
+  return getPtInv() / gpu::CAMath::Sqrt(1.f + getTgl() * getTgl()); // getPtInv() is protected against being 0, full charge accounted
 }
 
 //____________________________________________________________
@@ -577,8 +639,23 @@ template 
 GPUdi() auto TrackParametrization::getP() const -> value_t
 {
   // return the track momentum
-  const value_t pInv = getPInv();
-  return (pInv > o2::constants::math::Almost0) ? 1.f / pInv : o2::constants::math::VeryBig;
+  return 1.f / getPInv(); // getPInv is already protected against being 0
+}
+
+//____________________________________________________________
+template 
+GPUdi() auto TrackParametrization::getE2() const -> value_t
+{
+  // return the track energy^2
+  return getP2() + getPID().getMass2();
+}
+
+//____________________________________________________________
+template 
+GPUdi() auto TrackParametrization::getE() const -> value_t
+{
+  // return the track energy
+  return gpu::CAMath::Sqrt(getE2());
 }
 
 //____________________________________________________________
@@ -586,11 +663,7 @@ template 
 GPUdi() auto TrackParametrization::getPt() const -> value_t
 {
   // return the track transverse momentum
-  value_t ptI = gpu::CAMath::Abs(mP[kQ2Pt]);
-  if (mAbsCharge > 1) {
-    ptI /= mAbsCharge;
-  }
-  return (ptI > o2::constants::math::Almost0) ? 1.f / ptI : o2::constants::math::VeryBig;
+  return 1.f / getPtInv(); // getPtInv is already protected against being 0
 }
 
 //____________________________________________________________
@@ -616,7 +689,7 @@ GPUdi() auto TrackParametrization::getXYZGlo() const -> math_utils::Poi
 #else // mockup on GPU without ROOT
   float sina, cosa;
   gpu::CAMath::SinCos(getAlpha(), sina, cosa);
-  return math_utils::Point3D(cosa * getX() + sina * getY(), cosa * getY() - sina * getX(), getZ());
+  return math_utils::Point3D(cosa * getX() - sina * getY(), cosa * getY() + sina * getX(), getZ());
 #endif
 }
 
@@ -646,7 +719,7 @@ GPUdi() auto TrackParametrization::getXYZGloAt(value_t xk, value_t b, b
 #else // mockup on GPU without ROOT
     float sina, cosa;
     gpu::CAMath::SinCos(getAlpha(), sina, cosa);
-    return math_utils::Point3D(cosa * xk + sina * y, cosa * y - sina * xk, z);
+    return math_utils::Point3D(cosa * xk - sina * y, cosa * y + sina * xk, z);
 #endif
   } else {
     return math_utils::Point3D();
@@ -708,6 +781,21 @@ GPUdi() void TrackParametrization::updateParams(const value_t* delta)
   }
 }
 
+#ifndef GPUCA_ALIGPUCODE
+template 
+size_t TrackParametrization::hash(float x, float alp, float y, float z, float snp, float tgl, float q2pt)
+{
+  size_t h = std::hash{}(o2::math_utils::detail::truncateFloatFraction(x, 0xFFFFFFF0));
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(alp, 0xFFFFFFF0)) << 1;
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(y, 0xFFFFFFF0)) << 1;
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(z, 0xFFFFFFF0)) << 1;
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(snp, 0xFFFFFF00)) << 1;
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(tgl, 0xFFFFFF00)) << 1;
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(q2pt, 0xFFFFFC00)) << 1;
+  return h;
+}
+#endif
+
 } // namespace track
 } // namespace o2
 
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h
index a33e8c287101e..436dc42cff749 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h
@@ -18,12 +18,15 @@
 #define INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKPARAMETRIZATIONWITHERROR_H_
 
 #include "ReconstructionDataFormats/TrackParametrization.h"
+#include 
 
 namespace o2
 {
 namespace track
 {
 
+class TrackParCovFwd; // fwd declaration for conversion method
+
 template 
 class TrackParametrizationWithError : public TrackParametrization
 { // track+error parameterization
@@ -37,26 +40,26 @@ class TrackParametrizationWithError : public TrackParametrization
   static_assert(std::is_floating_point_v);
 #endif
 
-  using covMat_t = gpu::gpustd::array;
-  using MatrixDSym5 = ROOT::Math::SMatrix>;
-  using MatrixD5 = ROOT::Math::SMatrix>;
+  using covMat_t = std::array;
+  using MatrixDSym5 = o2::math_utils::SMatrix>;
+  using MatrixD5 = o2::math_utils::SMatrix>;
 
-  GPUd() TrackParametrizationWithError();
+  GPUhd() TrackParametrizationWithError();
   GPUd() TrackParametrizationWithError(value_t x, value_t alpha, const params_t& par, const covMat_t& cov, int charge = 1, const PID pid = PID::Pion);
   GPUd() TrackParametrizationWithError(const dim3_t& xyz, const dim3_t& pxpypz,
-                                       const gpu::gpustd::array& cv, int sign, bool sectorAlpha = true, const PID pid = PID::Pion);
+                                       const std::array& cv, int sign, bool sectorAlpha = true, const PID pid = PID::Pion);
 
-  GPUdDefault() TrackParametrizationWithError(const TrackParametrizationWithError& src) = default;
+  GPUhdDefault() TrackParametrizationWithError(const TrackParametrizationWithError& src) = default;
   GPUdDefault() TrackParametrizationWithError(TrackParametrizationWithError&& src) = default;
-  GPUdDefault() TrackParametrizationWithError& operator=(const TrackParametrizationWithError& src) = default;
-  GPUdDefault() TrackParametrizationWithError& operator=(TrackParametrizationWithError&& src) = default;
+  GPUhdDefault() TrackParametrizationWithError& operator=(const TrackParametrizationWithError& src) = default;
+  GPUhdDefault() TrackParametrizationWithError& operator=(TrackParametrizationWithError&& src) = default;
   GPUdDefault() ~TrackParametrizationWithError() = default;
   using TrackParametrization::TrackParametrization;
 
   using TrackParametrization::set;
   GPUd() void set(value_t x, value_t alpha, const params_t& par, const covMat_t& cov, int charge = 1, const PID pid = PID::Pion);
   GPUd() void set(value_t x, value_t alpha, const value_t* par, const value_t* cov, int charge = 1, const PID pid = PID::Pion);
-  GPUd() void set(const dim3_t& xyz, const dim3_t& pxpypz, const gpu::gpustd::array& cv, int sign, bool sectorAlpha = true, const PID pid = PID::Pion);
+  GPUd() void set(const dim3_t& xyz, const dim3_t& pxpypz, const std::array& cv, int sign, bool sectorAlpha = true, const PID pid = PID::Pion);
   GPUd() const covMat_t& getCov() const;
   GPUd() value_t getSigmaY2() const;
   GPUd() value_t getSigmaZY() const;
@@ -76,31 +79,44 @@ class TrackParametrizationWithError : public TrackParametrization
   GPUd() value_t getCovarElem(int i, int j) const;
   GPUd() value_t getDiagError2(int i) const;
 
-  GPUd() bool getCovXYZPxPyPzGlo(gpu::gpustd::array& c) const;
+  GPUd() bool getCovXYZPxPyPzGlo(std::array& c) const;
 
   GPUd() void print() const;
-#ifndef GPUCA_ALIGPUCODE
+  GPUd() void printHexadecimal();
+#ifndef GPUCA_GPUCODE_DEVICE
+  bool toFwdTrackParCov(TrackParCovFwd& t) const;
   std::string asString() const;
+  std::string asStringHexadecimal();
 #endif
 
   // parameters + covmat manipulation
+  GPUd() bool testRotate(value_t alpha) const;
   GPUd() bool rotate(value_t alpha);
-  GPUd() bool propagateTo(value_t xk, value_t b);
+  GPUd() bool rotate(value_t alpha, TrackParametrization& linRef, value_t bz);
+  GPUd() bool propagateTo(value_t xk, value_t bz);
+  GPUd() bool propagateTo(value_t xk, TrackParametrization& linRef, value_t bz);
+  GPUd() bool propagateTo(value_t xk, value_t bz, TrackParametrization* linRef) { return linRef ? propagateTo(xk, *linRef, bz) : propagateTo(xk, bz); }
   GPUd() bool propagateTo(value_t xk, const dim3_t& b);
-  GPUd() bool propagateToDCA(const o2::dataformats::VertexBase& vtx, value_t b, o2::dataformats::DCA* dca = nullptr, value_t maxD = 999.f);
+  GPUd() bool propagateTo(value_t xk, TrackParametrization& linRef, const dim3_t& b);
+  GPUd() bool propagateTo(value_t xk, const dim3_t& b, TrackParametrization* linRef) { return linRef ? propagateTo(xk, *linRef, b) : propagateTo(xk, b); }
+  GPUd() bool propagateToDCA(const o2::dataformats::VertexBase& vtx, value_t bz, o2::dataformats::DCA* dca = nullptr, value_t maxD = 999.f);
   GPUd() void invert();
-
   GPUd() value_t getPredictedChi2(const dim2_t& p, const dim3_t& cov) const;
+  GPUd() value_t getPredictedChi2Quiet(const dim2_t& p, const dim3_t& cov) const;
   GPUd() value_t getPredictedChi2(const value_t* p, const value_t* cov) const;
+  GPUd() value_t getPredictedChi2Quiet(const value_t* p, const value_t* cov) const;
 
   template 
   GPUd() value_t getPredictedChi2(const BaseCluster& p) const;
+  template 
+  GPUd() value_t getPredictedChi2Quiet(const BaseCluster& p) const;
 
-  void buildCombinedCovMatrix(const TrackParametrizationWithError& rhs, MatrixDSym5& cov) const;
-  value_t getPredictedChi2(const TrackParametrizationWithError& rhs, MatrixDSym5& covToSet) const;
-  value_t getPredictedChi2(const TrackParametrizationWithError& rhs) const;
-  bool update(const TrackParametrizationWithError& rhs, const MatrixDSym5& covInv);
-  bool update(const TrackParametrizationWithError& rhs);
+  GPUd() void buildCombinedCovMatrix(const TrackParametrizationWithError& rhs, MatrixDSym5& cov) const;
+  GPUd() value_t getPredictedChi2(const TrackParametrizationWithError& rhs, MatrixDSym5& covToSet) const;
+  GPUd() value_t getPredictedChi2(const TrackParametrizationWithError& rhs) const;
+  GPUd() value_t getPredictedChi2Quiet(const TrackParametrizationWithError& rhs) const;
+  GPUd() bool update(const TrackParametrizationWithError& rhs, const MatrixDSym5& covInv);
+  GPUd() bool update(const TrackParametrizationWithError& rhs);
 
   GPUd() bool update(const dim2_t& p, const dim3_t& cov);
   GPUd() bool update(const value_t* p, const value_t* cov);
@@ -109,8 +125,8 @@ class TrackParametrizationWithError : public TrackParametrization
   template 
   GPUd() bool update(const BaseCluster& p);
 
-  GPUd() bool correctForMaterial(value_t x2x0, value_t xrho, bool anglecorr = false, value_t dedx = kCalcdEdxAuto);
-
+  GPUd() bool correctForMaterial(value_t x2x0, value_t xrho, bool anglecorr = false);
+  GPUd() bool correctForMaterial(TrackParametrization& linRef, value_t x2x0, value_t xrho, bool anglecorr = false);
   GPUd() void resetCovariance(value_t s2 = 0);
   GPUd() void checkCovariance();
   GPUd() void checkCorrelations();
@@ -139,7 +155,7 @@ class TrackParametrizationWithError : public TrackParametrization
 
 //__________________________________________________________________________
 template 
-GPUdi() TrackParametrizationWithError::TrackParametrizationWithError() : TrackParametrization{}
+GPUhdi() TrackParametrizationWithError::TrackParametrizationWithError() : TrackParametrization{}
 {
 }
 
@@ -147,7 +163,8 @@ GPUdi() TrackParametrizationWithError::TrackParametrizationWithError()
 template 
 GPUdi() TrackParametrizationWithError::TrackParametrizationWithError(value_t x, value_t alpha, const params_t& par,
                                                                               const covMat_t& cov, int charge, const PID pid)
-  : TrackParametrization{x, alpha, par, charge, pid}
+  : TrackParametrization{
+      x, alpha, par, charge, pid}
 {
   // explicit constructor
   for (int i = 0; i < kCovMatSize; i++) {
@@ -303,11 +320,21 @@ template 
 template 
 GPUdi() auto TrackParametrizationWithError::getPredictedChi2(const BaseCluster& p) const -> value_t
 {
-  const dim2_t pyz = {p.getY(), p.getZ()};
-  const dim3_t cov = {p.getSigmaY2(), p.getSigmaYZ(), p.getSigmaZ2()};
+  const dim2_t pyz = {value_T(p.getY()), value_T(p.getZ())};
+  const dim3_t cov = {value_T(p.getSigmaY2()), value_T(p.getSigmaYZ()), value_T(p.getSigmaZ2())};
   return getPredictedChi2(pyz, cov);
 }
 
+//__________________________________________________________________________
+template 
+template 
+GPUdi() auto TrackParametrizationWithError::getPredictedChi2Quiet(const BaseCluster& p) const -> value_t
+{
+  const dim2_t pyz = {value_T(p.getY()), value_T(p.getZ())};
+  const dim3_t cov = {value_T(p.getSigmaY2()), value_T(p.getSigmaYZ()), value_T(p.getSigmaZ2())};
+  return getPredictedChi2Quiet(pyz, cov);
+}
+
 //______________________________________________
 template 
 GPUdi() auto TrackParametrizationWithError::getPredictedChi2(const dim2_t& p, const dim3_t& cov) const -> value_t
@@ -315,6 +342,13 @@ GPUdi() auto TrackParametrizationWithError::getPredictedChi2(const dim2
   return getPredictedChi2(p.data(), cov.data());
 }
 
+//______________________________________________
+template 
+GPUdi() auto TrackParametrizationWithError::getPredictedChi2Quiet(const dim2_t& p, const dim3_t& cov) const -> value_t
+{
+  return getPredictedChi2Quiet(p.data(), cov.data());
+}
+
 //______________________________________________
 template 
 GPUdi() bool TrackParametrizationWithError::update(const dim2_t& p, const dim3_t& cov)
@@ -327,8 +361,8 @@ template 
 template 
 GPUdi() bool TrackParametrizationWithError::update(const BaseCluster& p)
 {
-  const dim2_t pyz = {p.getY(), p.getZ()};
-  const dim3_t cov = {p.getSigmaY2(), p.getSigmaYZ(), p.getSigmaZ2()};
+  const dim2_t pyz = {value_T(p.getY()), value_T(p.getZ())};
+  const dim3_t cov = {value_T(p.getSigmaY2()), value_T(p.getSigmaYZ()), value_T(p.getSigmaZ2())};
   return update(pyz, cov);
 }
 
@@ -413,7 +447,7 @@ GPUdi() void TrackParametrizationWithError::updateCovCorr(const value_t
     auto diagI = DiagMap[i];
     oldDiag[i] = mC[diagI];
     mC[diagI] += delta2[i];
-    for (int j = 0; j < i; i++) {
+    for (int j = 0; j < i; j++) {
       mC[CovarMap[i][j]] *= gpu::CAMath::Sqrt(mC[diagI] * mC[DiagMap[j]] / (oldDiag[i] * oldDiag[j]));
     }
   }
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h
index 8e8dd9033bdf8..8a79130d64eda 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h
@@ -18,9 +18,9 @@
 #define INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKUTILS_H_
 
 #include "GPUCommonRtypes.h"
-#include "GPUCommonArray.h"
 
 #ifndef GPUCA_GPUCODE_DEVICE
+#include 
 #include 
 #endif
 
@@ -33,14 +33,17 @@ namespace track
 {
 // helper function
 template 
-GPUd() value_T BetheBlochSolid(value_T bg, value_T rho = 2.33, value_T kp1 = 0.20, value_T kp2 = 3.00, value_T meanI = 173e-9,
-                               value_T meanZA = 0.49848);
+GPUd() value_T BetheBlochSolid(value_T bg, value_T rho = 2.33, value_T kp1 = 0.20, value_T kp2 = 3.00, value_T meanI = 173e-9, value_T meanZA = 0.49848);
+
+template 
+GPUd() value_T BetheBlochSolidOpt(value_T bg);
+
 template 
-GPUd() void g3helx3(value_T qfield, value_T step, gpu::gpustd::array& vect);
+GPUd() void g3helx3(value_T qfield, value_T step, std::array& vect);
 
 //____________________________________________________
 template 
-GPUd() void g3helx3(value_T qfield, value_T step, gpu::gpustd::array& vect)
+GPUd() void g3helx3(value_T qfield, value_T step, std::array& vect)
 {
   /******************************************************************
    *                                                                *
@@ -121,11 +124,11 @@ GPUd() value_T BetheBlochSolid(value_T bg, value_T rho, value_T kp1, value_T kp2
   static_assert(std::is_floating_point_v);
 #endif
 
-  constexpr value_T mK = 0.307075e-3f; // [GeV*cm^2/g]
-  constexpr value_T me = 0.511e-3f;    // [GeV/c^2]
+  constexpr value_T mK = 0.307075e-3; // [GeV*cm^2/g]
+  constexpr value_T me = 0.511e-3;    // [GeV/c^2]
   kp1 *= 2.303f;
   kp2 *= 2.303f;
-  value_T bg2 = bg * bg;
+  value_T bg2 = bg * bg, beta2 = bg2 / (1 + bg2);
   value_T maxT = 2.f * me * bg2; // neglecting the electron mass
 
   //*** Density effect
@@ -138,7 +141,75 @@ GPUd() value_T BetheBlochSolid(value_T bg, value_T rho, value_T kp1, value_T kp2
     double r = (kp2 - x) / (kp2 - kp1);
     d2 = lhwI + x - 0.5f + (0.5f - lhwI - kp1) * r * r * r;
   }
-  return mK * meanZA * (1 + bg2) / bg2 * (0.5f * gpu::CAMath::Log(2 * me * bg2 * maxT / (meanI * meanI)) - bg2 / (1 + bg2) - d2);
+  auto dedx = mK * meanZA / beta2 * (0.5f * gpu::CAMath::Log(2 * me * bg2 * maxT / (meanI * meanI)) - beta2 - d2);
+  return dedx > 0. ? dedx : 0.;
+}
+
+//____________________________________________________
+template 
+GPUd() value_T BetheBlochSolidOpt(value_T bg)
+{
+  //
+  // This is the parameterization of the Bethe-Bloch formula inspired by Geant with hardcoded constants and better optimization
+  //
+  // bg  - beta*gamma
+  // rho - density [g/cm^3]
+  // kp1 - density effect first junction point
+  // kp2 - density effect second junction point
+  // meanI - mean excitation energy [GeV]
+  // meanZA - mean Z/A
+  //
+  // The default values for the kp* parameters are for silicon.
+  // The returned value is in [GeV/(g/cm^2)].
+  //
+  //  constexpr value_T rho = 2.33;
+  //  constexpr value_T meanI = 173e-9;
+  //  constexpr value_T me = 0.511e-3;    // [GeV/c^2]
+
+  constexpr value_T mK = 0.307075e-3; // [GeV*cm^2/g]
+  constexpr value_T kp1 = 0.20 * 2.303;
+  constexpr value_T kp2 = 3.00 * 2.303;
+  constexpr value_T meanZA = 0.49848;
+  constexpr value_T lhwI = -1.7175226;         // gpu::CAMath::Log(28.816 * 1e-9 * gpu::CAMath::Sqrt(rho * meanZA) / meanI);
+  constexpr value_T log2muTomeanI = 8.6839805; // gpu::CAMath::Log( 2. * me / meanI);
+
+  value_T bg2 = bg * bg, beta2 = bg2 / (1. + bg2);
+
+  //*** Density effect
+  value_T d2 = 0.;
+  const value_T x = gpu::CAMath::Log(bg);
+  if (x > kp2) {
+    d2 = lhwI - 0.5f + x;
+  } else if (x > kp1) {
+    value_T r = (kp2 - x) / (kp2 - kp1);
+    d2 = lhwI - 0.5 + x + (0.5 - lhwI - kp1) * r * r * r;
+  }
+  auto dedx = mK * meanZA / beta2 * (log2muTomeanI + x + x - beta2 - d2);
+  return dedx > 0. ? dedx : 0.;
+}
+
+//____________________________________________________
+template 
+GPUdi() value_T BetheBlochSolidDerivative(value_T dedx, value_T bg)
+{
+  //
+  // This is approximate derivative of the BB over betagamm, NO check for the consistency of the provided dedx and bg is done
+  // Charge 1 particle is assumed for the provied dedx. For charge > 1 particles dedx/q^2 should be provided and obtained value must be scaled by q^2
+  // The call should be usually done as
+  // auto dedx = BetheBlochSolidOpt(bg);
+  // // if derivative needed
+  // auto ddedx = BetheBlochSolidDerivative(dedx, bg, bg*bg)
+  //
+  // dedx - precalculate dedx for bg
+  // bg  - beta*gamma
+  //
+  constexpr value_T mK = 0.307075e-3; // [GeV*cm^2/g]
+  constexpr value_T meanZA = 0.49848;
+  auto bg2 = bg * bg;
+  auto t1 = 1 + bg2;
+  //  auto derH = (mK * meanZA * (t1+bg2) - dedx*bg2)/(bg*t1);
+  auto derH = (mK * meanZA * (t1 + 1. / bg2) - dedx) / (bg * t1);
+  return derH + derH;
 }
 
 } // namespace track
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h
index 9686a7af98056..a96ba1dd1e5a0 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h
@@ -15,6 +15,7 @@
 #include "ReconstructionDataFormats/VtxTrackIndex.h"
 #include "ReconstructionDataFormats/Track.h"
 #include "ReconstructionDataFormats/PID.h"
+#include "ReconstructionDataFormats/DecayNBodyIndex.h" // RS Remove after dropping indices in O2Physics
 #include 
 #include 
 
@@ -31,12 +32,9 @@ class V0 : public o2::track::TrackParCov
   using PID = o2::track::PID;
 
   V0() = default;
-  V0(const std::array& xyz, const std::array& pxyz, const std::array& covxyz,
-     const o2::track::TrackParCov& trPos, const o2::track::TrackParCov& trNeg,
-     GIndex trPosID, GIndex trNegID, o2::track::PID pid = o2::track::PID::K0);
 
-  GIndex getProngID(int i) const { return mProngIDs[i]; }
-  void setProngID(int i, GIndex gid) { mProngIDs[i] = gid; }
+  V0(const std::array& xyz, const std::array& pxyz, const std::array& covxyz,
+     const o2::track::TrackParCov& trPos, const o2::track::TrackParCov& trNeg, o2::track::PID pid = o2::track::PID::K0);
 
   const Track& getProng(int i) const { return mProngs[i]; }
   Track& getProng(int i) { return mProngs[i]; }
@@ -48,23 +46,28 @@ class V0 : public o2::track::TrackParCov
   float getDCA() const { return mDCA; }
   void setDCA(float d) { mDCA = d; }
 
-  int getVertexID() const { return mVertexID; }
-  void setVertexID(int id) { mVertexID = id; }
-
-  float calcMass2() const { return calcMass2(mProngs[0].getPID(), mProngs[1].getPID()); }
-  float calcMass2(PID pidPos, PID pidNeg) const { return calcMass2(pidPos.getMass2(), pidNeg.getMass2()); }
+  float calcMass2() const { return calcMass2PID(mProngs[0].getPID(), mProngs[1].getPID()); }
+  float calcMass2PID(PID pidPos, PID pidNeg) const { return calcMass2(PID::getMass2(pidPos), PID::getMass2(pidNeg)); }
   float calcMass2(float massPos2, float massNeg2) const;
 
+  float calcMass2AsPhoton() const { return calcMass2PID(PID::Electron, PID::Electron); }
+  float calcMass2AsK0() const { return calcMass2PID(PID::Pion, PID::Pion); }
+  float calcMass2AsLambda() const { return calcMass2PID(PID::Proton, PID::Pion); }
+  float calcMass2AsAntiLambda() const { return calcMass2PID(PID::Pion, PID::Proton); }
+  float calcMass2AsHyperTriton() const { return calcMass2PID(PID::Helium3, PID::Pion); }
+  float calcMass2AsAntiHyperTriton() const { return calcMass2PID(PID::Pion, PID::Helium3); }
+  float calcMass2AsHyperhydrog4() const { return calcMass2PID(PID::Alpha, PID::Pion); }
+  float calcMass2AsAntiHyperhydrog4() const { return calcMass2PID(PID::Pion, PID::Alpha); }
+  float calcAPQt() const;
+  float calcAPAlpha() const;
   float calcR2() const { return getX() * getX() + getY() * getY(); }
 
  protected:
-  std::array mProngIDs; // global IDs of prongs
   std::array mProngs;    // prongs kinematics at vertex
   float mCosPA = 0;                // cos of pointing angle
   float mDCA = 9990;               // distance of closest approach of prongs
-  int mVertexID = -1;              // id of parent vertex
 
-  ClassDefNV(V0, 1);
+  ClassDefNV(V0, 2);
 };
 
 } // namespace dataformats
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h
index fd7ea85b7bbbe..588a23d25a000 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h
@@ -14,14 +14,17 @@
 
 #include "GPUCommonDef.h"
 #include "GPUCommonMath.h"
-#include "GPUCommonArray.h"
 #include 
 
 #include "CommonDataFormat/TimeStamp.h"
 #ifndef GPUCA_GPUCODE_DEVICE
-#include 
-#include 
 #include 
+#include 
+#ifndef GPUCA_NO_FMT
+#include 
+#include 
+#include 
+#endif
 #endif
 
 namespace o2
@@ -42,9 +45,17 @@ class VertexBase
   static constexpr int kNCov = 6;
   GPUhdDefault() VertexBase() = default;
   GPUhdDefault() ~VertexBase() = default;
-  GPUhd() VertexBase(const math_utils::Point3D& pos, const gpu::gpustd::array& cov) : mPos(pos), mCov(cov)
+  GPUhd() VertexBase(const float* pos, const float* cov)
   {
+    mPos = math_utils::Point3D(pos[0], pos[1], pos[2]);
+    mCov[kCovXX] = cov[kCovXX];
+    mCov[kCovXY] = cov[kCovXY];
+    mCov[kCovXZ] = cov[kCovXZ];
+    mCov[kCovYY] = cov[kCovYY];
+    mCov[kCovYZ] = cov[kCovYZ];
+    mCov[kCovZZ] = cov[kCovZZ];
   }
+  GPUhd() VertexBase(const math_utils::Point3D& pos, const std::array& cov) : mPos(pos), mCov(cov) {}
 
 #if !defined(GPUCA_NO_FMT) && !defined(GPUCA_GPUCODE_DEVICE)
   void print() const;
@@ -55,6 +66,7 @@ class VertexBase
   GPUhd() float getX() const { return mPos.X(); }
   GPUhd() float getY() const { return mPos.Y(); }
   GPUhd() float getZ() const { return mPos.Z(); }
+  GPUhd() float getR() const { return gpu::CAMath::Hypot(mPos.X(), mPos.Y()); }
   GPUd() float getSigmaX2() const { return mCov[kCovXX]; }
   GPUd() float getSigmaY2() const { return mCov[kCovYY]; }
   GPUd() float getSigmaZ2() const { return mCov[kCovZZ]; }
@@ -65,7 +77,8 @@ class VertexBase
   GPUd() float getSigmaY() const { return gpu::CAMath::Sqrt(getSigmaY2()); }
   GPUd() float getSigmaZ() const { return gpu::CAMath::Sqrt(getSigmaZ2()); }
 
-  GPUd() const gpu::gpustd::array& getCov() const { return mCov; }
+  GPUd() const std::array& getCov() const { return mCov; }
+  GPUd() float getCov(int e) const { return mCov[e]; }
 
   GPUd() math_utils::Point3D getXYZ() const { return mPos; }
   GPUd() math_utils::Point3D& getXYZ() { return mPos; }
@@ -101,14 +114,15 @@ class VertexBase
     setSigmaXZ(sxz);
     setSigmaYZ(syz);
   }
-  GPUd() void setCov(const gpu::gpustd::array& cov) { mCov = cov; }
+  GPUd() void setCov(const std::array& cov) { mCov = cov; }
+  GPUd() void setCov(float c, int e) { mCov[e] = c; }
 
   bool operator==(const VertexBase& other) const;
   bool operator!=(const VertexBase& other) const { return !(*this == other); }
 
  protected:
   math_utils::Point3D mPos{0., 0., 0.}; ///< cartesian position
-  gpu::gpustd::array mCov{};     ///< errors, see CovElems enum
+  std::array mCov{};             ///< errors, see CovElems enum
 
   ClassDefNV(VertexBase, 1);
 };
@@ -124,30 +138,37 @@ class Vertex : public VertexBase
   using ushort = unsigned short;
   enum Flags : ushort {
     TimeValidated = 0x1 << 0, // Flag that the vertex was validated by external time measurement (e.g. FIT)
+    UPCMode = 0x1 << 1,       // vertex is found in the UPC mode ITS ROF
     FlagsMask = 0xffff
   };
 
   GPUhdDefault() Vertex() = default;
   GPUhdDefault() ~Vertex() = default;
-  GPUhd() Vertex(const math_utils::Point3D& pos, const gpu::gpustd::array& cov, ushort nCont, float chi2)
-    : VertexBase(pos, cov), mChi2(chi2), mNContributors(nCont)
+  GPUhd() Vertex(const float* pos, const float* cov, ushort nCont, float chi2) : VertexBase(pos, cov), mChi2(chi2), mNContributors(nCont) {}
+  GPUhd() Vertex(const math_utils::Point3D& pos, const std::array& cov, ushort nCont, float chi2) : VertexBase(pos, cov), mChi2(chi2), mNContributors(nCont) {}
+
+#if !defined(GPUCA_NO_FMT) && !defined(GPUCA_GPUCODE_DEVICE)
+  void print() const;
+  std::string asString() const;
+#endif
+
+  GPUd() ushort getNContributors() const
   {
+    return mNContributors;
   }
-
-  GPUd() ushort getNContributors() const { return mNContributors; }
   GPUd() void setNContributors(ushort v) { mNContributors = v; }
   GPUd() void addContributor() { mNContributors++; }
 
   GPUd() ushort getFlags() const { return mBits; }
   GPUd() bool isFlagSet(uint f) const { return mBits & (FlagsMask & f); }
   GPUd() void setFlags(ushort f) { mBits |= FlagsMask & f; }
-  GPUd() void resetFrags(ushort f = FlagsMask) { mBits &= ~(FlagsMask & f); }
+  GPUd() void resetFlags(ushort f = FlagsMask) { mBits &= ~(FlagsMask & f); }
 
   GPUd() void setChi2(float v) { mChi2 = v; }
   GPUd() float getChi2() const { return mChi2; }
 
-  GPUd() const Stamp& getTimeStamp() const { return mTimeStamp; }
-  GPUd() Stamp& getTimeStamp() { return mTimeStamp; }
+  GPUhd() const Stamp& getTimeStamp() const { return mTimeStamp; }
+  GPUhd() Stamp& getTimeStamp() { return mTimeStamp; }
   GPUd() void setTimeStamp(const Stamp& v) { mTimeStamp = v; }
 
  protected:
@@ -161,6 +182,65 @@ class Vertex : public VertexBase
 
 #if !defined(GPUCA_GPUCODE_DEVICE) && !defined(GPUCA_NO_FMT)
 std::ostream& operator<<(std::ostream& os, const o2::dataformats::VertexBase& v);
+
+namespace detail
+{
+template 
+concept Streamable = requires(std::ostream& os, const T& a) {
+  {
+    os << a
+  }
+  -> std::same_as;
+};
+
+template 
+concept HasFormattableTimeStamp = requires(const T& t) {
+  {
+    fmt::format("{}", t.getTimeStamp())
+  }
+  -> std::convertible_to;
+};
+
+template 
+concept HasFormattableTimeStampWithError = requires(const T& t) {
+  {
+    fmt::format("{}+-{}", t.getTimeStamp(), t.getTimeStampError())
+  }
+  -> std::convertible_to;
+};
+} // namespace detail
+
+template 
+inline std::string Vertex::asString() const
+{
+  const std::string stamp = [&]() -> std::string {
+    if constexpr (detail::Streamable) {
+      std::ostringstream oss;
+      oss << mTimeStamp;
+      return oss.str();
+    } else if constexpr (detail::HasFormattableTimeStampWithError) {
+      return fmt::format("{}+-{}", mTimeStamp.getTimeStamp(), mTimeStamp.getTimeStampError());
+    } else if constexpr (detail::HasFormattableTimeStamp) {
+      return fmt::format("{}", mTimeStamp.getTimeStamp());
+    } else {
+      return "X";
+    }
+  }();
+  return fmt::format("{} NContrib:{} Chi2:{:.2f} Flags:{:b} Stamp:{}", VertexBase::asString(), mNContributors, mChi2, mBits, stamp);
+}
+
+template 
+inline std::ostream& operator<<(std::ostream& os, const o2::dataformats::Vertex& v)
+{
+  os << v.asString();
+  return os;
+}
+
+template 
+inline void Vertex::print() const
+{
+  std::cout << *this << '\n';
+}
 #endif
 
 } // namespace dataformats
@@ -178,9 +258,6 @@ template <>
 struct is_messageable>> : std::true_type {
 };
 template <>
-struct is_messageable>> : std::true_type {
-};
-template <>
 struct is_messageable>> : std::true_type {
 };
 } // namespace framework
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/VtxTrackRef.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/VtxTrackRef.h
index 5cdc9aafefb66..93b572376e443 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/VtxTrackRef.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/VtxTrackRef.h
@@ -80,12 +80,17 @@ class VtxTrackRef : public RangeReference
   // set the last +1 element index and finalize all references
   void setEnd(int end);
 
+  const RangeReference& getITSGloContributors() const { return mITSGloContributors; }
+  RangeReference& getITSGloContributors() { return mITSGloContributors; }
+
  private:
   using RangeReference::RangeReference;
   int mVtxID = -1; // vertex ID. The reference for unassigned tracks will have it negative!
   std::array mFirstEntrySource{0};
 
-  ClassDefNV(VtxTrackRef, 2);
+  RangeReference mITSGloContributors; // optionally filled to keep ITS parts of all contributors (including ITS-only ones)
+
+  ClassDefNV(VtxTrackRef, 3);
 };
 
 std::ostream& operator<<(std::ostream& os, const o2::dataformats::VtxTrackRef& v);
diff --git a/DataFormats/Reconstruction/src/Cascade.cxx b/DataFormats/Reconstruction/src/Cascade.cxx
index dbc2c4cbae011..f5d7a017a89f8 100644
--- a/DataFormats/Reconstruction/src/Cascade.cxx
+++ b/DataFormats/Reconstruction/src/Cascade.cxx
@@ -12,23 +12,21 @@
 #include "ReconstructionDataFormats/Cascade.h"
 
 using namespace o2::dataformats;
-
+/*
 Cascade::Cascade(const std::array& xyz, const std::array& pxyz, const std::array& covxyz,
-                 const o2::track::TrackParCov& v0, const o2::track::TrackParCov& bachelor,
-                 int v0ID, GIndex bachelorID, o2::track::PID pid)
+                 const o2::track::TrackParCov& v0, const o2::track::TrackParCov& bachelor, o2::track::PID pid) : mProngs{v0, bachelor}
 {
-  std::array covV{}, covB{};
+  std::array covC{0.}, covV{}, covB{};
   v0.getCovXYZPxPyPzGlo(covV);
   bachelor.getCovXYZPxPyPzGlo(covB);
-  for (int i = 0; i < 21; i++) {
-    covV[i] += covB[i];
-  }
+  constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component
   for (int i = 0; i < 6; i++) {
-    covV[i] = covxyz[i];
+    covC[i] = covxyz[i];
+    covC[MomInd[i]] = covV[MomInd[i]] + covB[MomInd[i]];
   }
-  this->set(xyz, pxyz, covV, v0.getCharge() + bachelor.getCharge(), true, pid);
-  setV0ID(v0ID);
-  setBachelorID(bachelorID);
+  this->set(xyz, pxyz, covC, v0.getCharge() + bachelor.getCharge(), true, pid);
+  this->checkCorrelations();
   setV0Track(v0);
   setBachelorTrack(bachelor);
 }
+*/
diff --git a/DataFormats/Reconstruction/src/DCA.cxx b/DataFormats/Reconstruction/src/DCA.cxx
index 9bb324c8df3a9..a3291f59c86b6 100644
--- a/DataFormats/Reconstruction/src/DCA.cxx
+++ b/DataFormats/Reconstruction/src/DCA.cxx
@@ -9,6 +9,8 @@
 // granted to it by virtue of its status as an Intergovernmental Organization
 // or submit itself to any jurisdiction.
 
+#include "GPUCommonMath.h"
+#include "CommonConstants/MathConstants.h"
 #include "ReconstructionDataFormats/DCA.h"
 #include 
 
@@ -20,20 +22,26 @@ namespace o2
 namespace dataformats
 {
 
-#ifndef GPUCA_GPUCODE_DEVICE
+float DCA::calcChi2() const
+{
+  // Estimate the chi2 for DCA
+  const auto sdd = mCov[0], sdz = mCov[1], szz = mCov[2], det = sdd * szz - sdz * sdz;
+  if (o2::gpu::CAMath::Abs(det) < o2::constants::math::Almost0) {
+    return constants::math::VeryBig;
+  }
+  return (mY * (szz * mY - sdz * mZ) + mZ * (sdd * mZ - mY * sdz)) / det;
+}
+
 std::ostream& operator<<(std::ostream& os, const o2::dataformats::DCA& d)
 {
   // stream itself
   os << "DCA YZ {" << d.getY() << ", " << d.getZ() << "} Cov {" << d.getSigmaY2() << ", " << d.getSigmaYZ() << ", " << d.getSigmaZ2() << "}";
   return os;
 }
-#endif
 
 void DCA::print() const
 {
-#ifndef GPUCA_GPUCODE_DEVICE
   std::cout << *this << '\n';
-#endif
 }
 
 } // namespace dataformats
diff --git a/DataFormats/Reconstruction/src/Decay3Body.cxx b/DataFormats/Reconstruction/src/Decay3Body.cxx
new file mode 100644
index 0000000000000..eb1b7ea1fd57d
--- /dev/null
+++ b/DataFormats/Reconstruction/src/Decay3Body.cxx
@@ -0,0 +1,36 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#include "ReconstructionDataFormats/Decay3Body.h"
+
+using namespace o2::dataformats;
+
+Decay3Body::Decay3Body(const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2, o2::track::PID pid)
+  : mProngs{tr0, tr1, tr2}
+{
+  std::array cov{}, cov0{}, cov1{}, cov2{};
+  tr0.getCovXYZPxPyPzGlo(cov0);
+  tr1.getCovXYZPxPyPzGlo(cov1);
+  tr2.getCovXYZPxPyPzGlo(cov2);
+  constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component
+  for (int i = 0; i < 6; i++) {
+    cov[i] = covxyz[i];
+    cov[MomInd[i]] = cov0[MomInd[i]] + cov1[MomInd[i]] + cov2[MomInd[i]];
+  }
+  this->set(xyz, pxyz, cov, tr0.getCharge() + tr1.getCharge() + tr2.getCharge(), true, pid);
+}
+
+float Decay3Body::calcMass2(float mass0, float mass1, float mass2) const
+{
+  auto p2 = getP2();
+  auto energy = std::sqrt(mass0 + mProngs[0].getP2()) + std::sqrt(mass1 + mProngs[1].getP2()) + std::sqrt(mass1 + mProngs[2].getP2());
+  return energy * energy - p2;
+}
diff --git a/DataFormats/Reconstruction/src/DecayNBodyIndex.cxx b/DataFormats/Reconstruction/src/DecayNBodyIndex.cxx
new file mode 100644
index 0000000000000..bd012091aad3f
--- /dev/null
+++ b/DataFormats/Reconstruction/src/DecayNBodyIndex.cxx
@@ -0,0 +1,18 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#include "ReconstructionDataFormats/DecayNBodyIndex.h"
+
+namespace o2::dataformats
+{
+template class DecayNBodyIndex<2>;
+template class DecayNBodyIndex<3>;
+} // namespace o2::dataformats
diff --git a/DataFormats/Reconstruction/src/DecayNbody.cxx b/DataFormats/Reconstruction/src/DecayNbody.cxx
deleted file mode 100644
index 701c710e7ab46..0000000000000
--- a/DataFormats/Reconstruction/src/DecayNbody.cxx
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
-// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
-// All rights not expressly granted are reserved.
-//
-// This software is distributed under the terms of the GNU General Public
-// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
-//
-// In applying this license CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-#include "ReconstructionDataFormats/DecayNbody.h"
-
-using namespace o2::dataformats;
-
-DecayNbody::DecayNbody(PID pid, const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2, GIndex trID0, GIndex trID1, GIndex trID2)
-  : mProngIDs{trID0, trID1, trID2}, mProngs{tr0, tr1, tr2}
-{
-  std::array cov{}, cov1{}, cov2{};
-  tr0.getCovXYZPxPyPzGlo(cov);
-  tr1.getCovXYZPxPyPzGlo(cov1);
-  tr2.getCovXYZPxPyPzGlo(cov2);
-  for (int i = 0; i < 21; i++) {
-    cov[i] += cov1[i] + cov2[i];
-  }
-  for (int i = 0; i < 6; i++) {
-    cov[i] = covxyz[i];
-  }
-  this->set(xyz, pxyz, cov, tr0.getCharge() + tr1.getCharge() + tr2.getCharge(), true, pid);
-}
-
-float DecayNbody::calcMass2(float mass0, float mass1, float mass2) const
-{
-  auto p2 = getP2();
-  auto energy = std::sqrt(mass0 + mProngs[0].getP2()) + std::sqrt(mass1 + mProngs[1].getP2()) + std::sqrt(mass1 + mProngs[2].getP2());
-  return energy * energy - p2;
-}
diff --git a/DataFormats/Reconstruction/src/MatchInfoHMP.cxx b/DataFormats/Reconstruction/src/MatchInfoHMP.cxx
index 1058d39752ed7..77983c4355584 100644
--- a/DataFormats/Reconstruction/src/MatchInfoHMP.cxx
+++ b/DataFormats/Reconstruction/src/MatchInfoHMP.cxx
@@ -20,5 +20,6 @@ ClassImp(o2::dataformats::MatchInfoHMP);
 
 void MatchInfoHMP::print() const
 {
-  printf("Match of GlobalID %s and HMPID cl %d ", getTrackRef().asString().c_str(), getIdxHMPClus());
+  // printf("Match of GlobalID %s and HMPID cl %d ", getTrackRef().asString().c_str(), getIdxHMPClus());
+  printf("Match of GlobalID and HMPID cl");
 }
diff --git a/DataFormats/Reconstruction/src/PrimaryVertex.cxx b/DataFormats/Reconstruction/src/PrimaryVertex.cxx
index f1b1a8ff01181..f6f589fcb038a 100644
--- a/DataFormats/Reconstruction/src/PrimaryVertex.cxx
+++ b/DataFormats/Reconstruction/src/PrimaryVertex.cxx
@@ -19,8 +19,6 @@ namespace o2
 namespace dataformats
 {
 
-#ifndef GPUCA_ALIGPUCODE
-
 std::string PrimaryVertex::asString() const
 {
   auto str = o2::utils::Str::concat_string(VertexBase::asString(),
@@ -44,7 +42,5 @@ void PrimaryVertex::print() const
   std::cout << *this << std::endl;
 }
 
-#endif
-
 } // namespace dataformats
 } // namespace o2
diff --git a/DataFormats/Reconstruction/src/PrimaryVertexExt.cxx b/DataFormats/Reconstruction/src/PrimaryVertexExt.cxx
new file mode 100644
index 0000000000000..31f0eff191591
--- /dev/null
+++ b/DataFormats/Reconstruction/src/PrimaryVertexExt.cxx
@@ -0,0 +1,47 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#include "ReconstructionDataFormats/PrimaryVertexExt.h"
+#include 
+#include 
+#include "CommonUtils/StringUtils.h"
+
+namespace o2
+{
+namespace dataformats
+{
+using GTrackID = o2::dataformats::GlobalTrackID;
+
+std::string PrimaryVertexExt::asString() const
+{
+  auto str = o2::utils::Str::concat_string(PrimaryVertex::asString(), fmt::format("VtxID={} FT0A/C={}/{} FT0T={}", VtxID, FT0A, FT0C, FT0Time));
+  for (int i = 0; i < GTrackID::Source::NSources; i++) {
+    if (getNSrc(i) > 0) {
+      str += fmt::format(" {}={}", GTrackID::getSourceName(i), getNSrc(i));
+    }
+  }
+  return str;
+}
+
+std::ostream& operator<<(std::ostream& os, const o2::dataformats::PrimaryVertexExt& v)
+{
+  // stream itself
+  os << v.asString();
+  return os;
+}
+
+void PrimaryVertexExt::print() const
+{
+  std::cout << *this << std::endl;
+}
+
+} // namespace dataformats
+} // namespace o2
diff --git a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h
index e26510b5723d5..b386830d9872d 100644
--- a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h
+++ b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h
@@ -30,6 +30,7 @@
 #pragma link C++ class o2::track::TrackParFwd + ;
 #pragma link C++ class o2::track::PID + ;
 #pragma link C++ class o2::track::TrackLTIntegral + ;
+#pragma link C++ class std::vector < o2::track::TrackLTIntegral> + ;
 
 #pragma link C++ class o2::track::TrackParCovFwd + ;
 #pragma link C++ class std::vector < o2::track::TrackParCovFwd> + ;
@@ -46,6 +47,9 @@
 #pragma link C++ class o2::dataformats::TrackTPCTOF + ;
 #pragma link C++ class std::vector < o2::dataformats::TrackTPCTOF> + ;
 
+#pragma link C++ class o2::dataformats::TrackHMP + ;
+#pragma link C++ class std::vector < o2::dataformats::TrackHMP> + ;
+
 #pragma link C++ class o2::dataformats::TrackCosmics + ;
 #pragma link C++ class std::vector < o2::dataformats::TrackCosmics> + ;
 
@@ -70,10 +74,12 @@
 #pragma link C++ class o2::dataformats::Vertex < o2::dataformats::TimeStamp < int>> + ;
 #pragma link C++ class o2::dataformats::Vertex < o2::dataformats::TimeStampWithError < float, float>> + ;
 #pragma link C++ class o2::dataformats::PrimaryVertex + ;
+#pragma link C++ class o2::dataformats::PrimaryVertexExt + ;
 
 #pragma link C++ class std::vector < o2::dataformats::Vertex < o2::dataformats::TimeStamp < int>>> + ;
 #pragma link C++ class std::vector < o2::dataformats::Vertex < o2::dataformats::TimeStampWithError < float, float>>> + ;
 #pragma link C++ class std::vector < o2::dataformats::PrimaryVertex> + ;
+#pragma link C++ class std::vector < o2::dataformats::PrimaryVertexExt> + ;
 
 #pragma link C++ class o2::dataformats::GlobalTrackID + ;
 #pragma link C++ class std::vector < o2::dataformats::GlobalTrackID> + ;
@@ -87,13 +93,31 @@
 
 #pragma link C++ class o2::dataformats::DCA + ;
 
+#pragma link C++ class o2::dataformats::DecayNBodyIndex < 2> + ;
+#pragma link C++ class o2::dataformats::DecayNBodyIndex < 3> + ;
+#pragma link C++ class o2::dataformats::Decay3BodyIndex + ;
+#pragma link C++ class o2::dataformats::V0Index + ;
+#pragma link C++ class o2::dataformats::CascadeIndex + ;
+
+#pragma link C++ class std::vector < o2::dataformats::DecayNBodyIndex < 2>> + ;
+#pragma link C++ class std::vector < o2::dataformats::DecayNBodyIndex < 3>> + ;
+#pragma link C++ class std::vector < o2::dataformats::Decay3BodyIndex> + ;
+#pragma link C++ class std::vector < o2::dataformats::V0Index> + ;
+#pragma link C++ class std::vector < o2::dataformats::CascadeIndex> + ;
+
 #pragma link C++ class o2::dataformats::V0 + ;
 #pragma link C++ class std::vector < o2::dataformats::V0> + ;
 
 #pragma link C++ class o2::dataformats::Cascade + ;
 #pragma link C++ class std::vector < o2::dataformats::Cascade> + ;
 
-#pragma link C++ class o2::dataformats::DecayNbody + ;
-#pragma link C++ class std::vector < o2::dataformats::DecayNbody> + ;
+#pragma link C++ class o2::dataformats::Decay3Body + ;
+#pragma link C++ class std::vector < o2::dataformats::Decay3Body> + ;
+
+#pragma link C++ class o2::dataformats::StrangeTrack + ;
+#pragma link C++ class std::vector < o2::dataformats::StrangeTrack> + ;
+
+#pragma link C++ class o2::track::TrackAuxPar + ;
+#pragma link C++ class o2::track::CrossInfo + ;
 
 #endif
diff --git a/DataFormats/Reconstruction/src/StrangeTrack.cxx b/DataFormats/Reconstruction/src/StrangeTrack.cxx
new file mode 100644
index 0000000000000..70c3c8d1c2d80
--- /dev/null
+++ b/DataFormats/Reconstruction/src/StrangeTrack.cxx
@@ -0,0 +1,19 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+/// \file StrangeTrack.cxx
+/// \brief
+
+#include "ReconstructionDataFormats/StrangeTrack.h"
+
+using namespace o2::dataformats;
+
+ClassImp(o2::dataformats::StrangeTrack);
diff --git a/DataFormats/Reconstruction/src/TrackFwd.cxx b/DataFormats/Reconstruction/src/TrackFwd.cxx
index f567b47f18038..511ba122b2822 100644
--- a/DataFormats/Reconstruction/src/TrackFwd.cxx
+++ b/DataFormats/Reconstruction/src/TrackFwd.cxx
@@ -10,7 +10,10 @@
 // or submit itself to any jurisdiction.
 
 #include "ReconstructionDataFormats/TrackFwd.h"
+#include "ReconstructionDataFormats/TrackParametrization.h"
+#include "ReconstructionDataFormats/TrackParametrizationWithError.h"
 #include "Math/MatrixFunctions.h"
+#include 
 
 namespace o2
 {
@@ -40,7 +43,7 @@ void TrackParFwd::propagateParamToZlinear(double zEnd)
   auto dZ = (zEnd - getZ());
   auto phi0 = getPhi();
   auto [sinphi0, cosphi0] = o2::math_utils::sincosd(phi0);
-  auto invtanl0 = 1.0 / getTanl();
+  auto invtanl0 = 1.0 / getTgl();
   auto n = dZ * invtanl0;
   mParameters(0) += n * cosphi0;
   mParameters(1) += n * sinphi0;
@@ -244,6 +247,12 @@ void TrackParCovFwd::propagateToZhelix(double zEnd, double zField)
 //__________________________________________________________________________
 void TrackParCovFwd::propagateToZ(double zEnd, double zField)
 {
+  // Security for zero B field
+  if (zField == 0.0) {
+    propagateToZlinear(zEnd);
+    return;
+  }
+
   // Extrapolate track parameters and covariances matrix to "zEnd"
   // Parameters: helix track model; Error propagation: Quadratic
 
@@ -433,6 +442,211 @@ void TrackParFwd::getCircleParams(float bz, o2::math_utils::CircleXY& c,
     c.yC = getY();
   }
 }
+//________________________________________________________________
+bool TrackParCovFwd::propagateToVtxhelixWithMCS(double z, const std::array& p, const std::array& cov, double field, double x_over_X0)
+{
+  // Propagate fwd track to vertex using helix model, adding MCS effects
+  addMCSEffect(x_over_X0);
+  propagateToZhelix(z, field);
+  return update(p, cov);
+}
+//________________________________________________________________
+bool TrackParCovFwd::propagateToVtxlinearWithMCS(double z, const std::array& p, const std::array& cov, double x_over_X0)
+{
+  // Propagate fwd track to vertex using linear model, adding MCS effects
+  addMCSEffect(x_over_X0);
+  propagateToZlinear(z);
+  return update(p, cov);
+}
+
+bool TrackParCovFwd::getCovXYZPxPyPzGlo(std::array& cv) const
+{
+  //---------------------------------------------------------------------
+  // This function returns the global covariance matrix of the fwdtrack params
+  //
+  // Cov(x,x) ... :   cv[0]
+  // Cov(y,x) ... :   cv[1]  cv[2]
+  // Cov(z,x) ... :   cv[3]  cv[4]  cv[5]
+  // Cov(px,x)... :   cv[6]  cv[7]  cv[8]  cv[9]
+  // Cov(py,x)... :   cv[10] cv[11] cv[12] cv[13] cv[14]
+  // Cov(pz,x)... :   cv[15] cv[16] cv[17] cv[18] cv[19] cv[20]
+  //---------------------------------------------------------------------
+  auto pt = getPt();
+  auto cp = std::sqrt((1. - getSnp()) * (1. + getSnp()));
+  auto sp = getSnp();
+  auto tgl = getTgl();
+
+  auto px = pt * std::sqrt((1. - getSnp()) * (1. + getSnp()));
+  auto py = pt * getSnp();
+  auto pz = pt * getTgl();
+  auto q = getCharge();
+
+  cv[0] = mCovariances(0, 0);
+  cv[1] = mCovariances(1, 0);
+  cv[2] = mCovariances(1, 1);
+  cv[3] = 0;
+  cv[4] = 0;
+  cv[5] = 0;
+  cv[6] = -mCovariances(0, 2) * py - mCovariances(0, 4) * px * pt * q;
+  cv[7] = -mCovariances(1, 2) * py - mCovariances(1, 4) * px * pt * q;
+  cv[8] = 0;
+  cv[9] = 2 * mCovariances(2, 4) * px * py * q * pt + mCovariances(2, 2) * py * py + mCovariances(4, 4) * px * px * pt * pt;
+  cv[10] = mCovariances(0, 2) * px - mCovariances(0, 4) * py * pt * q;
+  cv[11] = mCovariances(1, 2) * px - mCovariances(1, 4) * py * pt * q;
+  cv[12] = 0;
+  cv[13] = mCovariances(2, 4) * (py * py - px * px) * q * pt - mCovariances(2, 2) * px * py + mCovariances(4, 4) * px * py * pt * pt;
+  cv[14] = -2 * mCovariances(2, 4) * px * py * q * pt + mCovariances(2, 2) * px * px + mCovariances(4, 4) * py * py * pt * pt;
+  cv[15] = mCovariances(0, 3) * pt - mCovariances(0, 4) * pt * pz * q;
+  cv[16] = mCovariances(1, 3) * pt - mCovariances(1, 4) * pt * pz * q;
+  cv[17] = 0;
+  cv[18] = -mCovariances(2, 3) * py * pt - mCovariances(3, 4) * px * q * pt * pt + mCovariances(2, 4) * py * pz * q * pt + mCovariances(4, 4) * px * pz * pt * pt;
+  cv[19] = mCovariances(2, 3) * px * pt - mCovariances(3, 4) * q * pt * pt * py - mCovariances(2, 4) * px * pz * q * pt + mCovariances(4, 4) * py * pz * pt * pt;
+  cv[20] = -2 * mCovariances(3, 4) * pz * q * pt * pt + mCovariances(3, 3) * pt * pt + mCovariances(4, 4) * pz * pz * pt * pt;
+
+  return true;
+}
+
+//________________________________________________________________
+
+void TrackParCovFwd::propagateToDCAhelix(double zField, const std::array& p, std::array& dca)
+{
+  // Computing DCA of fwd track w.r.t vertex in helix track model, using Newton-Raphson minimization
+
+  auto x0 = mParameters(0);
+  auto y0 = mParameters(1);
+  auto z0 = mZ;
+  auto phi0 = mParameters(2);
+  auto tanl = mParameters(3);
+  auto qOverPt = mParameters(4);
+  auto k = TMath::Abs(o2::constants::math::B2C * zField);
+  auto qpt = 1.0 / qOverPt;
+  auto qR = qpt / std::fabs(k);
+  auto invtanl = 1.0 / tanl;
+  auto Hz = std::copysign(1, zField);
+
+  auto xPV = p[0];
+  auto yPV = p[1];
+  auto zPV = p[2];
+
+  auto qRtanl = qR * tanl;
+  auto invqRtanl = 1.0 / qRtanl;
+  auto [sinp, cosp] = o2::math_utils::sincosd(phi0);
+
+  auto z = zPV;
+  double tol = 1e-4;
+  int max_iter = 10;
+  int iter = 0;
+
+  while (iter++ < max_iter) {
+    double theta = (z0 - z) * invqRtanl;
+    double phi_theta = phi0 + Hz * theta;
+    double sin_phi_theta = sin(phi_theta);
+    double cos_phi_theta = cos(phi_theta);
+
+    double DX = x0 - Hz * qR * (sin_phi_theta - sinp) - xPV;
+    double DY = y0 + Hz * qR * (cos_phi_theta - cosp) - yPV;
+    double DZ = z - zPV;
+
+    double dD2_dZ =
+      2 * DX * cos_phi_theta * invtanl +
+      2 * DY * sin_phi_theta * invtanl +
+      2 * DZ;
+
+    double d2D2_dZ2 =
+      2 * invtanl * invtanl +
+      2 * invtanl * (DX * Hz * sin_phi_theta - DY * Hz * cos_phi_theta) * invqRtanl +
+      2;
+
+    double z_new = z - dD2_dZ / d2D2_dZ2;
+
+    if (std::abs(z_new - z) < tol) {
+      z = z_new;
+      this->propagateToZhelix(z, zField);
+      dca[0] = this->getX() - xPV;
+      dca[1] = this->getY() - yPV;
+      dca[2] = this->getZ() - zPV;
+      LOG(debug) << "Converged after " << iter << " iterations for vertex X=" << p[0] << ", Y=" << p[1] << ", Z = " << p[2];
+      return;
+    }
+    z = z_new;
+  }
+  LOG(debug) << "Failed to converge after " << iter << " iterations for vertex X=" << p[0] << ", Y=" << p[1] << ", Z = " << p[2];
+  return;
+}
+
+template 
+void TrackParFwd::toBarrelTrackPar(TrackParametrization& t) const
+{
+  // we select the barrel frame with alpha = phi, then by construction the snp is 0
+  auto alpha = getPhi();
+  auto csa = TMath::Cos(alpha), sna = TMath::Sin(alpha);
+  t.setAlpha(alpha);
+  t.setX(csa * getX() + sna * getY());
+  t.setY(-sna * getX() + csa * getY());
+  t.setZ(getZ());
+  t.setSnp(0);
+  t.setTgl(getTanl());
+  t.setQ2Pt(getInvQPt());
+}
+
+template 
+void TrackParCovFwd::toBarrelTrackParCov(TrackParametrizationWithError& t) const
+{
+  // We select the barrel frame with alpha = phi, then by construction the snp is 0
+  auto alpha = getPhi();
+  auto csa = TMath::Cos(alpha), sna = TMath::Sin(alpha);
+  t.setAlpha(alpha);
+  t.setX(csa * getX() + sna * getY());
+  t.setY(-sna * getX() + csa * getY());
+  t.setZ(getZ());
+  t.setSnp(0);
+  t.setTgl(getTgl());
+  t.setQ2Pt(getInvQPt());
+  /*
+    The standard Jacobian d{barrel_param} / d{fwd_param} should be augmented by the "forward" uncertainty
+    in X_barrel translated to uncertainty in Z, i.e:
+    A fwd param variation delta_x, delta_y leads to barrel frame coordinate variaion
+    delta_xb =  csa delta_x + sna delta_y
+    delta_yb = -sna delta_x + csa delta_y
+    with dx_b/dz = csp/tgL = 1/tgL, dy_b/dz = snp/tgL = 0  (for phi 0 in the tracking frame) the variation of delta_xb would require
+    a shift in delta_zb = -tgL delta_xb to stat at the same X.
+    So, for alpha=phi (-> snp=0) choice the full Jacobian fwd->barrel is:
+    / -sna       csa      0 0 0  \
+    | -tgL*csa  -tgL*sna  0 0 0  |
+    |  0          0       1 0 0  |
+    |  0          0       0 1 0  |
+    \  0          0       0 0 1 /
+  */
+  const T a1 = -sna;
+  const T a2 = csa;
+  const T b1 = -getTgl() * csa;
+  const T b2 = -getTgl() * sna;
+  const T cphi = 1;
+  const auto& C = getCovariances();
+  typename TrackParametrizationWithError::covMat_t covBarrel = {
+    T(a1 * a1 * C(0, 0) + 2 * a1 * a2 * C(0, 1) + a2 * a2 * C(1, 1)),         // kSigY2
+    T(a1 * b1 * C(0, 0) + (a1 * b2 + a2 * b1) * C(0, 1) + a2 * b2 * C(1, 1)), // kSigZY
+    T(b1 * b1 * C(0, 0) + 2 * b1 * b2 * C(0, 1) + b2 * b2 * C(1, 1)),         // kSigZ2
+    T(csa * (a1 * C(0, 2) + a2 * C(1, 2))),                                   // kSigSnpY
+    T(csa * (b1 * C(0, 2) + b2 * C(1, 2))),                                   // kSigSnpZ
+    T(csa * csa * C(2, 2)),                                                   // kSigSnp2
+    T(a1 * C(0, 3) + a2 * C(1, 3)),                                           // kSigTglY
+    T(b1 * C(0, 3) + b2 * C(1, 3)),                                           // kSigTglZ
+    T(csa * C(2, 3)),                                                         // kSigTglSnp
+    T(C(3, 3)),                                                               // kSigTgl2
+    T(a1 * C(0, 4) + a2 * C(1, 4)),                                           // kSigQ2PtY
+    T(b1 * C(0, 4) + b2 * C(1, 4)),                                           // kSigQ2PtZ
+    T(csa * C(2, 4)),                                                         // kSigQ2PtSnp
+    T(C(3, 4)),                                                               // kSigQ2PtTgl
+    T(C(4, 4))                                                                // kSigQ2Pt2
+  };
+  t.setCov(covBarrel);
+}
+
+template void TrackParFwd::toBarrelTrackPar(TrackParametrization&) const;
+template void TrackParFwd::toBarrelTrackPar(TrackParametrization&) const;
+template void TrackParCovFwd::toBarrelTrackParCov(TrackParametrizationWithError&) const;
+template void TrackParCovFwd::toBarrelTrackParCov(TrackParametrizationWithError&) const;
 
 } // namespace track
 } // namespace o2
diff --git a/DataFormats/Reconstruction/src/TrackHMP.cxx b/DataFormats/Reconstruction/src/TrackHMP.cxx
new file mode 100644
index 0000000000000..dd5865d5bc363
--- /dev/null
+++ b/DataFormats/Reconstruction/src/TrackHMP.cxx
@@ -0,0 +1,146 @@
+// Copyright 2020-2022 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#include "ReconstructionDataFormats/TrackParametrizationWithError.h"
+#include "ReconstructionDataFormats/TrackParametrization.h"
+#include "ReconstructionDataFormats/Track.h"
+#include "ReconstructionDataFormats/TrackLTIntegral.h"
+#include "ReconstructionDataFormats/GlobalTrackID.h"
+#include "ReconstructionDataFormats/TrackHMP.h"
+#include "Field/MagneticField.h"
+// #include "DetectorsBase/Propagator.h"
+
+// #include "Field/MagFieldFast.h"
+
+#include 
+#include 
+
+#include  //fields
+// #include "Math/Vector3D.h" //fields
+
+const Double_t kB2C = -0.299792458e-3;
+
+ClassImp(o2::dataformats::TrackHMP)
+
+  using XYZVector = ROOT::Math::XYZVector;
+
+namespace o2
+{
+namespace dataformats
+{
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+TrackHMP::TrackHMP() = default;
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+TrackHMP& TrackHMP::operator=(const o2::track::TrackParCov& t)
+{
+  // ass. op.
+
+  return *this;
+}
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+bool TrackHMP::intersect(Double_t pnt[3], Double_t norm[3], double bz) const
+{
+  //+++++++++++++++++++++++++++++++++++++++++
+  // Origin: K. Shileev (Kirill.Shileev@cern.ch)
+  // Finds point of intersection (if exists) of the helix with the plane.
+  // Stores result in fX and fP.
+  // Arguments: planePoint,planeNorm - the plane defined by any plane's point
+  // and vector, normal to the plane
+  // Returns: kTrue if helix intersects the plane, kFALSE otherwise.
+  //+++++++++++++++++++++++++++++++++++++++++
+
+  std::array x0;
+  getXYZGlo(x0); // get track position in MARS
+
+  // estimates initial helix length up to plane
+  Double_t s = (pnt[0] - x0[0]) * norm[0] + (pnt[1] - x0[1]) * norm[1] + (pnt[2] - x0[2]) * norm[2];
+
+  Double_t dist = 99999, distPrev = dist;
+  // Double_t p[3];
+
+  std::array x, p;
+
+  while (TMath::Abs(dist) > 0.00001) {
+
+    // calculates helix at the distance s from x0 ALONG the helix
+
+    propagate(s, x, p, bz);
+    // distance between current helix position and plane
+
+    dist = (x[0] - pnt[0]) * norm[0] + (x[1] - pnt[1]) * norm[1] + (x[2] - pnt[2]) * norm[2];
+    if (TMath::Abs(dist) >= TMath::Abs(distPrev)) { /*Printf("***********************dist > distPrev******************");*/
+      return kFALSE;
+    }
+    distPrev = dist;
+    s -= dist;
+  }
+  // on exit pnt is intersection point,norm is track vector at that point,
+  // all in MARS
+  for (Int_t i = 0; i < 3; i++) {
+    pnt[i] = x.at(i);
+    norm[i] = p.at(i);
+  }
+
+  return kTRUE;
+} // Intersect()
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+void TrackHMP::propagate(Double_t len, std::array& x, std::array& p, double bz) const
+{
+  //+++++++++++++++++++++++++++++++++++++++++
+  // Origin: K. Shileev (Kirill.Shileev@cern.ch)
+  // Extrapolate track along simple helix in magnetic field
+  // Arguments: len -distance alogn helix, [cm]
+  //            bz  - mag field, [kGaus]
+  // Returns: x and p contain extrapolated positon and momentum
+  // The momentum returned for straight-line tracks is meaningless !
+  //+++++++++++++++++++++++++++++++++++++++++
+
+  std::array x0;
+  getXYZGlo(x0); // get track position in MARS
+
+  x.at(0) = x0.at(0);
+  x.at(1) = x0.at(1);
+  x.at(2) = x0.at(2);
+
+  if (getPtInv() < o2::constants::math::Almost0 || TMath::Abs(bz) < o2::constants::math::Almost0) { // straight-line tracks
+
+    TVector3 trackDirection(1., 1., 1.);
+    trackDirection.SetMag(1);
+    trackDirection.SetTheta(getTheta());
+    trackDirection.SetPhi(getPhi());
+
+    // Double_t unit[3]; GetDirection(unit);
+    x.at(0) += trackDirection.X() * len;
+    x.at(1) += trackDirection.Y() * len;
+    x.at(2) += trackDirection.Z() * len;
+
+    p.at(0) = trackDirection.X() / o2::constants::math::Almost0;
+    p.at(1) = trackDirection.Y() / o2::constants::math::Almost0;
+    p.at(2) = trackDirection.Z() / o2::constants::math::Almost0;
+  } else {
+
+    getPxPyPzGlo(p);
+    Double_t pp = getP();
+
+    Double_t a = -kB2C * bz * getSign(); ////////// what is kB2C
+    Double_t rho = a / pp;
+    x.at(0) += p.at(0) * TMath::Sin(rho * len) / a - p.at(1) * (1 - TMath::Cos(rho * len)) / a;
+    x.at(1) += p.at(1) * TMath::Sin(rho * len) / a + p.at(0) * (1 - TMath::Cos(rho * len)) / a;
+    x.at(2) += p.at(2) * len / pp;
+    Double_t p0 = p.at(0);
+    p.at(0) = p0 * TMath::Cos(rho * len) - p.at(1) * TMath::Sin(rho * len);
+    p.at(1) = p.at(1) * TMath::Cos(rho * len) + p0 * TMath::Sin(rho * len);
+  }
+
+} // Propagate()
+} // namespace dataformats
+} // namespace o2
diff --git a/DataFormats/Reconstruction/src/TrackLTIntegral.cxx b/DataFormats/Reconstruction/src/TrackLTIntegral.cxx
index 52a77ce92a4da..426c3da04726c 100644
--- a/DataFormats/Reconstruction/src/TrackLTIntegral.cxx
+++ b/DataFormats/Reconstruction/src/TrackLTIntegral.cxx
@@ -9,6 +9,10 @@
 // granted to it by virtue of its status as an Intergovernmental Organization
 // or submit itself to any jurisdiction.
 
+#ifndef GPUCA_GPUCODE_DEVICE
+#include 
+#endif
+
 #include "ReconstructionDataFormats/TrackLTIntegral.h"
 #include "CommonConstants/PhysicsConstants.h"
 #include "MathUtils/Utils.h"
@@ -35,9 +39,9 @@ GPUd() void TrackLTIntegral::print() const
 }
 
 //_____________________________________________________
-GPUd() void TrackLTIntegral::addStep(float dL, float p2Inv)
+GPUd() void TrackLTIntegral::addStep(float dL, float q2p2)
 {
-  ///< add step in cm to integrals
+  ///< add step in cm to integrals, q2p2 is (q/p)^2.
   mL += dL;
   if (isTimeNotNeeded()) {
     return;
@@ -45,7 +49,7 @@ GPUd() void TrackLTIntegral::addStep(float dL, float p2Inv)
   const float dTns = dL * 1000.f / o2::constants::physics::LightSpeedCm2NS; // time change in ps for beta = 1 particle
   for (int id = 0; id < getNTOFs(); id++) {
     const float m2z = track::PID::getMass2Z(id);
-    const float betaInv = math_utils::sqrt(1.f + m2z * m2z * p2Inv);
+    const float betaInv = math_utils::sqrt(1.f + m2z * m2z * q2p2);
     mT[id] += dTns * betaInv;
   }
 }
diff --git a/DataFormats/Reconstruction/src/TrackParametrization.cxx b/DataFormats/Reconstruction/src/TrackParametrization.cxx
index 24229dd493541..c238b087d5086 100644
--- a/DataFormats/Reconstruction/src/TrackParametrization.cxx
+++ b/DataFormats/Reconstruction/src/TrackParametrization.cxx
@@ -14,17 +14,6 @@
 /// @since  Oct 1, 2020
 /// @brief
 
-// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
-// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
-// All rights not expressly granted are reserved.
-//
-// This software is distributed under the terms of the GNU General Public
-// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
-//
-// In applying this license CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
 #include "ReconstructionDataFormats/TrackParametrization.h"
 #include "ReconstructionDataFormats/Vertex.h"
 #include "ReconstructionDataFormats/DCA.h"
@@ -37,6 +26,7 @@
 
 #ifndef GPUCA_ALIGPUCODE
 #include 
+#include "ReconstructionDataFormats/TrackFwd.h"
 #endif
 
 using namespace o2::gpu;
@@ -141,12 +131,12 @@ GPUd() bool TrackParametrization::getPxPyPzGlo(dim3_t& pxyz) const
 
 //____________________________________________________
 template 
-GPUd() bool TrackParametrization::getPosDirGlo(gpu::gpustd::array& posdirp) const
+GPUd() bool TrackParametrization::getPosDirGlo(std::array& posdirp) const
 {
   // fill vector with lab x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha
-  value_t ptI = gpu::CAMath::Abs(getQ2Pt());
+  value_t ptI = getPtInv();
   value_t snp = getSnp();
-  if (ptI < constants::math::Almost0 || gpu::CAMath::Abs(snp) > constants::math::Almost1) {
+  if (gpu::CAMath::Abs(snp) > constants::math::Almost1) {
     return false;
   }
   value_t &sn = posdirp[7], &cs = posdirp[8];
@@ -199,6 +189,38 @@ GPUd() bool TrackParametrization::rotateParam(value_t alpha)
   return true;
 }
 
+//______________________________________________________________
+template 
+GPUd() bool TrackParametrization::rotateParam(value_t& alpha, value_t& ca, value_t& sa)
+{
+  // rotate to alpha frame
+  if (gpu::CAMath::Abs(getSnp()) > constants::math::Almost1) {
+    LOGP(debug, "Precondition is not satisfied: |sin(phi)|>1 ! {:f}", getSnp());
+    return false;
+  }
+  //
+  math_utils::detail::bringToPMPi(alpha);
+  math_utils::detail::sincos(alpha - getAlpha(), sa, ca);
+  value_t snp = getSnp(), csp = gpu::CAMath::Sqrt((1.f - snp) * (1.f + snp)); // Improve precision
+  // RS: check if rotation does no invalidate track model (cos(local_phi)>=0, i.e. particle direction in local frame is along the X axis
+  if ((csp * ca + snp * sa) < 0) {
+    // LOGF(warning,"Rotation failed: local cos(phi) would become {:.2f}", csp * ca + snp * sa);
+    return false;
+  }
+  //
+  value_t tmp = snp * ca - csp * sa;
+  if (gpu::CAMath::Abs(tmp) > constants::math::Almost1) {
+    LOGP(debug, "Rotation failed: new snp {:.2f}", tmp);
+    return false;
+  }
+  value_t xold = getX(), yold = getY();
+  mAlpha = alpha;
+  mX = xold * ca + yold * sa;
+  mP[kY] = -xold * sa + yold * ca;
+  mP[kSnp] = tmp;
+  return true;
+}
+
 //____________________________________________________________
 template 
 GPUd() bool TrackParametrization::propagateParamTo(value_t xk, const dim3_t& b)
@@ -242,7 +264,7 @@ GPUd() bool TrackParametrization::propagateParamTo(value_t xk, const di
   step *= gpu::CAMath::Sqrt(1.f + getTgl() * getTgl());
   //
   // get the track x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha in the Global System
-  gpu::gpustd::array vecLab{0.f};
+  std::array vecLab{0.f};
   if (!getPosDirGlo(vecLab)) {
     return false;
   }
@@ -261,7 +283,7 @@ GPUd() bool TrackParametrization::propagateParamTo(value_t xk, const di
     costet = b[2] / bb;
     sintet = bt / bb;
   }
-  gpu::gpustd::array vect{costet * cosphi * vecLab[0] + costet * sinphi * vecLab[1] - sintet * vecLab[2],
+  std::array vect{costet * cosphi * vecLab[0] + costet * sinphi * vecLab[1] - sintet * vecLab[2],
                                       -sinphi * vecLab[0] + cosphi * vecLab[1],
                                       sintet * cosphi * vecLab[0] + sintet * sinphi * vecLab[1] + costet * vecLab[2],
                                       costet * cosphi * vecLab[3] + costet * sinphi * vecLab[4] - sintet * vecLab[5],
@@ -293,14 +315,14 @@ GPUd() bool TrackParametrization::propagateParamTo(value_t xk, const di
 
   // Do the final correcting step to the target plane (linear approximation)
   value_t x = vecLab[0], y = vecLab[1], z = vecLab[2];
-  if (gpu::CAMath::Abs(dx) > constants::math::Almost0) {
+  if (gpu::CAMath::Abs(x - xk) > constants::math::Almost0) {
     if (gpu::CAMath::Abs(vecLab[3]) < constants::math::Almost0) {
       return false;
     }
-    dx = xk - vecLab[0];
-    x += dx;
-    y += vecLab[4] / vecLab[3] * dx;
-    z += vecLab[5] / vecLab[3] * dx;
+    auto dxFin = xk - vecLab[0];
+    x += dxFin;
+    y += vecLab[4] / vecLab[3] * dxFin;
+    z += vecLab[5] / vecLab[3] * dxFin;
   }
 
   // Calculate the track parameters
@@ -389,6 +411,10 @@ GPUd() bool TrackParametrization::propagateParamToDCA(const math_utils:
   // Estimate the impact parameter neglecting the track curvature
   value_t d = gpu::CAMath::Abs(x * snp - y * csp);
   if (d > maxD) {
+    if (dca) { // provide default DCA for failed propag
+      (*dca)[0] = o2::track::DefaultDCA;
+      (*dca)[1] = o2::track::DefaultDCA;
+    }
     return false;
   }
   value_t crv = getCurvature(b);
@@ -410,6 +436,10 @@ GPUd() bool TrackParametrization::propagateParamToDCA(const math_utils:
 #else
     LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z();
 #endif
+    if (dca) { // provide default DCA for failed propag
+      (*dca)[0] = o2::track::DefaultDCA;
+      (*dca)[1] = o2::track::DefaultDCA;
+    }
     return false;
   }
   *this = tmpT;
@@ -520,6 +550,40 @@ GPUd() typename TrackParametrization::value_t TrackParametrization
+GPUd() typename TrackParametrization::value_t TrackParametrization::getPhiAt(value_t xk, value_t b) const
+{
+  ///< this method is just an alias for obtaining phi @ X in the tree->Draw()
+  value_t dx = xk - getX();
+  if (gpu::CAMath::Abs(dx) < constants::math::Almost0) {
+    return getPhi();
+  }
+  value_t crv = (gpu::CAMath::Abs(b) < constants::math::Almost0) ? 0.f : getCurvature(b);
+  value_t x2r = crv * dx;
+  value_t snp = mP[kSnp] + x2r;
+  value_t phi = 999.;
+  if (gpu::CAMath::Abs(snp) < constants::math::Almost1) {
+    phi = gpu::CAMath::ASin(snp) + getAlpha();
+    math_utils::detail::bringTo02Pi(phi);
+  }
+  return phi;
+}
+
+//______________________________________________________________
+template 
+GPUd() typename TrackParametrization::value_t TrackParametrization::getPhiPosAt(value_t xk, value_t b) const
+{
+  ///< this method is just an alias for obtaining phiPos @ X in the tree->Draw()
+  value_t phi = 999.;
+  auto y = getYAt(xk, b);
+  if (y > -9998.) {
+    phi = gpu::CAMath::ATan2(y, xk) + getAlpha();
+    math_utils::detail::bringTo02Pi(phi);
+  }
+  return phi;
+}
+
 //______________________________________________________________
 template 
 GPUd() typename TrackParametrization::value_t TrackParametrization::getSnpAt(value_t alpha, value_t xk, value_t b) const
@@ -555,6 +619,31 @@ std::string TrackParametrization::asString() const
   return fmt::format("X:{:+.4e} Alp:{:+.3e} Par: {:+.4e} {:+.4e} {:+.4e} {:+.4e} {:+.4e} |Q|:{:d} {:s}",
                      getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt(), getAbsCharge(), getPID().getName());
 }
+
+//_____________________________________________________________
+template 
+std::string TrackParametrization::asStringHexadecimal()
+{
+  auto _X = getX();
+  auto _Alpha = getAlpha();
+  auto _Y = getY();
+  auto _Z = getZ();
+  auto _Snp = getSnp();
+  auto _Tgl = getTgl();
+  float _Q2Pt = getQ2Pt();
+  float _AbsCharge = getAbsCharge();
+  // print parameters as string
+  return fmt::format("X:{:x} Alp:{:x} Par: {:x} {:x} {:x} {:x} {:x} |Q|:{:x} {:s}\n",
+                     reinterpret_cast(_X),
+                     reinterpret_cast(_Alpha),
+                     reinterpret_cast(_Y),
+                     reinterpret_cast(_Z),
+                     reinterpret_cast(_Snp),
+                     reinterpret_cast(_Tgl),
+                     reinterpret_cast(_Q2Pt),
+                     reinterpret_cast(_AbsCharge),
+                     getPID().getName());
+}
 #endif
 
 //______________________________________________________________
@@ -564,9 +653,30 @@ GPUd() void TrackParametrization::printParam() const
   // print parameters
 #ifndef GPUCA_ALIGPUCODE
   printf("%s\n", asString().c_str());
-#else
-  printf("X:%+.4e Alp:%+.3e Par: %+.4e %+.4e %+.4e %+.4e %+.4e |Q|:%d",
-         getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt(), getAbsCharge());
+#elif !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT))
+  printf("X:%+.4e Alp:%+.3e Par: %+.4e %+.4e %+.4e %+.4e %+.4e |Q|:%d %s\n",
+         getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt(), getAbsCharge(), getPID().getName());
+#endif
+}
+
+//______________________________________________________________
+template 
+GPUd() void TrackParametrization::printParamHexadecimal()
+{
+  // print parameters
+#ifndef GPUCA_ALIGPUCODE
+  printf("%s\n", asStringHexadecimal().c_str());
+#elif !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT))
+  printf("X:%x Alp:%x Par: %x %x %x %x %x |Q|:%x %s\n",
+         gpu::CAMath::Float2UIntReint(getX()),
+         gpu::CAMath::Float2UIntReint(getAlpha()),
+         gpu::CAMath::Float2UIntReint(getY()),
+         gpu::CAMath::Float2UIntReint(getZ()),
+         gpu::CAMath::Float2UIntReint(getSnp()),
+         gpu::CAMath::Float2UIntReint(getTgl()),
+         gpu::CAMath::Float2UIntReint(getQ2Pt()),
+         gpu::CAMath::Float2UIntReint(getAbsCharge()),
+         getPID().getName());
 #endif
 }
 
@@ -582,30 +692,37 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val
   // DirOutward (==1) - go along the track (increasing mX)
   // DirInward (==-1) - go backward (decreasing mX)
   //
-  const auto fy = mP[0], sn = mP[2];
+  const double fy = mP[0], sn = mP[2];
   const value_t kEps = 1.e-6;
   //
+  if (gpu::CAMath::Abs(getSnp()) > constants::math::Almost1) {
+    return false;
+  }
   auto crv = getCurvature(bz);
-  if (gpu::CAMath::Abs(crv) > constants::math::Almost0) { // helix
+  while (gpu::CAMath::Abs(crv) > constants::math::Almost0) { // helix ?
     // get center of the track circle
-    math_utils::CircleXY circle;
+    math_utils::CircleXY circle{};
     getCircleParamsLoc(bz, circle);
+    if (circle.rC == 0.) {
+      crv = 0.;
+      break;
+    }
     value_t r0 = gpu::CAMath::Sqrt(circle.getCenterD2());
     if (r0 <= constants::math::Almost0) {
       return false; // the track is concentric to circle
     }
-    value_t tR2r0 = 1.f, g = 0.f, tmp = 0.f;
+    double tR2r0 = 1., g = 0., tmp = 0.;
     if (gpu::CAMath::Abs(circle.rC - r0) > kEps) {
       tR2r0 = circle.rC / r0;
       g = 0.5f * (r * r / (r0 * circle.rC) - tR2r0 - 1.f / tR2r0);
       tmp = 1.f + g * tR2r0;
     } else {
       tR2r0 = 1.0;
-      g = 0.5f * r * r / (r0 * circle.rC) - 1.f;
-      tmp = 0.5f * r * r / (r0 * r0);
+      g = 0.5 * r * r / (r0 * circle.rC) - 1.;
+      tmp = 0.5 * r * r / (r0 * r0);
     }
-    value_t det = (1.f - g) * (1.f + g);
-    if (det < 0.f) {
+    auto det = (1. - g) * (1. + g);
+    if (det < 0.) {
       return false; // does not reach raduis r
     }
     det = gpu::CAMath::Sqrt(det);
@@ -615,25 +732,26 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val
     // where s0 and c0 make direction for the circle center (=circle.xC/r0 and circle.yC/r0)
     //
     x = circle.xC * tmp;
-    value_t y = circle.yC * tmp;
+    auto y = circle.yC * tmp;
     if (gpu::CAMath::Abs(circle.yC) > constants::math::Almost0) { // when circle.yC==0 the x,y is unique
-      value_t dfx = tR2r0 * gpu::CAMath::Abs(circle.yC) * det;
-      value_t dfy = tR2r0 * circle.xC * (circle.yC > 0.f ? det : -det);
+      auto dfx = tR2r0 * gpu::CAMath::Abs(circle.yC) * det;
+      auto dfy = tR2r0 * circle.xC * (circle.yC > 0. ? det : -det);
       if (dir == DirAuto) {                              // chose the one which corresponds to smallest step
-        value_t delta = (x - mX) * dfx - (y - fy) * dfy; // the choice of + in C will lead to smaller step if delta<0
-        x += delta < 0.f ? dfx : -dfx;
+        auto delta = (x - mX) * dfx - (y - fy) * dfy;    // the choice of + in C will lead to smaller step if delta<0
+        x += delta < 0. ? dfx : -dfx;
       } else if (dir == DirOutward) { // along track direction: x must be > mX
         x -= dfx;                     // try the smallest step (dfx is positive)
-        value_t dfeps = mX - x;       // handle special case of very small step
+        auto dfeps = mX - x;          // handle special case of very small step
         if (dfeps < -kEps) {
           return true;
         }
         if (gpu::CAMath::Abs(dfeps) < kEps && gpu::CAMath::Abs(mX * mX + fy * fy - r * r) < kEps) { // are we already in right r?
-          return mX;
+          x = mX;
+          return true;
         }
         x += dfx + dfx;
-        value_t dxm = x - mX;
-        if (dxm > 0.f) {
+        auto dxm = x - mX;
+        if (dxm > 0.) {
           return true;
         } else if (dxm < -kEps) {
           return false;
@@ -641,16 +759,17 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val
         x = mX;                 // don't move
       } else {                  // backward: x must be < mX
         x += dfx;               // try the smallest step (dfx is positive)
-        value_t dfeps = x - mX; // handle special case of very small step
+        auto dfeps = x - mX;    // handle special case of very small step
         if (dfeps < -kEps) {
           return true;
         }
         if (gpu::CAMath::Abs(dfeps) < kEps && gpu::CAMath::Abs(mX * mX + fy * fy - r * r) < kEps) { // are we already in right r?
-          return mX;
+          x = mX;
+          return true;
         }
         x -= dfx + dfx;
-        value_t dxm = x - mX;
-        if (dxm < 0.f) {
+        auto dxm = x - mX;
+        if (dxm < 0.) {
           return true;
         }
         if (dxm > kEps) {
@@ -663,85 +782,86 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val
         return false;
       }
     }
-  } else {                                                  // this is a straight track
-    if (gpu::CAMath::Abs(sn) >= constants::math::Almost1) { // || to Y axis
-      value_t det = (r - mX) * (r + mX);
-      if (det < 0.f) {
-        return false; // does not reach raduis r
-      }
-      x = mX;
-      if (dir == DirAuto) {
-        return true;
-      }
-      det = gpu::CAMath::Sqrt(det);
-      if (dir == DirOutward) { // along the track direction
-        if (sn > 0.f) {
-          if (fy > det) {
-            return false; // track is along Y axis and above the circle
-          }
-        } else {
-          if (fy < -det) {
-            return false; // track is against Y axis amd belo the circle
-          }
+    return true;
+  }
+  // this is a straight track
+  if (gpu::CAMath::Abs(sn) >= constants::math::Almost1) { // || to Y axis
+    double det = (r - mX) * (r + mX);
+    if (det < 0.f) {
+      return false; // does not reach raduis r
+    }
+    x = mX;
+    if (dir == DirAuto) {
+      return true;
+    }
+    det = gpu::CAMath::Sqrt(det);
+    if (dir == DirOutward) { // along the track direction
+      if (sn > 0.) {
+        if (fy > det) {
+          return false; // track is along Y axis and above the circle
         }
-      } else if (dir == DirInward) { // against track direction
-        if (sn > 0.f) {
-          if (fy < -det) {
-            return false; // track is along Y axis
-          }
-        } else if (fy > det) {
-          return false; // track is against Y axis
+      } else {
+        if (fy < -det) {
+          return false; // track is against Y axis amd belo the circle
         }
       }
-    } else if (gpu::CAMath::Abs(sn) <= constants::math::Almost0) { // || to X axis
-      value_t det = (r - fy) * (r + fy);
-      if (det < 0.f) {
-        return false; // does not reach raduis r
-      }
-      det = gpu::CAMath::Sqrt(det);
-      if (dir == DirAuto) {
-        x = mX > 0.f ? det : -det; // choose the solution requiring the smalest step
-        return true;
-      } else if (dir == DirOutward) { // along the track direction
-        if (mX > det) {
-          return false; // current point is in on the right from the circle
-        } else {
-          x = (mX < -det) ? -det : det; // on the left : within the circle
-        }
-      } else { // against the track direction
-        if (mX < -det) {
-          return false;
-        } else {
-          x = mX > det ? det : -det;
+    } else if (dir == DirInward) { // against track direction
+      if (sn > 0.) {
+        if (fy < -det) {
+          return false; // track is along Y axis
         }
+      } else if (fy > det) {
+        return false; // track is against Y axis
       }
-    } else { // general case of straight line
-      value_t cs = gpu::CAMath::Sqrt((1.f - sn) * (1.f + sn));
-      value_t xsyc = mX * sn - fy * cs;
-      value_t det = (r - xsyc) * (r + xsyc);
-      if (det < 0.f) {
-        return false; // does not reach raduis r
+    }
+  } else if (gpu::CAMath::Abs(sn) <= constants::math::Almost0) { // || to X axis
+    double det = (r - fy) * (r + fy);
+    if (det < 0.) {
+      return false; // does not reach raduis r
+    }
+    det = gpu::CAMath::Sqrt(det);
+    if (dir == DirAuto) {
+      x = mX > 0. ? det : -det; // choose the solution requiring the smalest step
+      return true;
+    } else if (dir == DirOutward) { // along the track direction
+      if (mX > det) {
+        return false; // current point is in on the right from the circle
+      } else {
+        x = (mX < -det) ? -det : det; // on the left : within the circle
       }
-      det = gpu::CAMath::Sqrt(det);
-      value_t xcys = mX * cs + fy * sn;
-      value_t t = -xcys;
-      if (dir == DirAuto) {
-        t += t > 0.f ? -det : det; // chose the solution requiring the smalest step
-      } else if (dir > 0) {        // go in increasing mX direction. ( t+-det > 0)
-        if (t >= -det) {
-          t += det; // take minimal step giving t>0
-        } else {
-          return false; // both solutions have negative t
-        }
-      } else { // go in decreasing mX direction. (t+-det < 0)
-        if (t < det) {
-          t -= det; // take minimal step giving t<0
-        } else {
-          return false; // both solutions have positive t
-        }
+    } else { // against the track direction
+      if (mX < -det) {
+        return false;
+      } else {
+        x = mX > det ? det : -det;
       }
-      x = mX + cs * t;
     }
+  } else { // general case of straight line
+    auto cs = gpu::CAMath::Sqrt((1. - sn) * (1. + sn));
+    auto xsyc = mX * sn - fy * cs;
+    auto det = (r - xsyc) * (r + xsyc);
+    if (det < 0.) {
+      return false; // does not reach raduis r
+    }
+    det = gpu::CAMath::Sqrt(det);
+    auto xcys = mX * cs + fy * sn;
+    auto t = -xcys;
+    if (dir == DirAuto) {
+      t += t > 0. ? -det : det;  // chose the solution requiring the smalest step
+    } else if (dir > 0) {        // go in increasing mX direction. ( t+-det > 0)
+      if (t >= -det) {
+        t += det; // take minimal step giving t>0
+      } else {
+        return false; // both solutions have negative t
+      }
+    } else { // go in decreasing mX direction. (t+-det < 0)
+      if (t < det) {
+        t -= det; // take minimal step giving t<0
+      } else {
+        return false; // both solutions have positive t
+      }
+    }
+    x = mX + cs * t;
   }
   //
   return true;
@@ -749,7 +869,7 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val
 
 //______________________________________________
 template 
-GPUd() bool TrackParametrization::correctForELoss(value_t xrho, bool anglecorr, value_t dedx)
+GPUd() bool TrackParametrization::correctForELoss(value_t xrho, bool anglecorr)
 {
   //------------------------------------------------------------------
   // This function corrects the track parameters for the energy loss in crossed material.
@@ -759,41 +879,77 @@ GPUd() bool TrackParametrization::correctForELoss(value_t xrho, bool an
   // "dedx" - mean enery loss (GeV/(g/cm^2), if <=kCalcdEdxAuto : calculate on the fly
   // "anglecorr" - switch for the angular correction
   //------------------------------------------------------------------
-  constexpr value_t kMaxELossFrac = 0.3f; // max allowed fractional eloss
-  constexpr value_t kMinP = 0.01f;        // kill below this momentum
-
-  // Apply angle correction, if requested
-  if (anglecorr) {
-    value_t csp2 = (1.f - getSnp()) * (1.f + getSnp()); // cos(phi)^2
-    value_t cst2I = (1.f + getTgl() * getTgl());        // 1/cos(lambda)^2
-    value_t angle = gpu::CAMath::Sqrt(cst2I / (csp2));
-    xrho *= angle;
-  }
-  value_t p = getP();
-  value_t p2 = p * p;
-  value_t e2 = p2 + getPID().getMass2();
-  value_t beta2 = p2 / e2;
-
-  // Calculating the energy loss corrections************************
-  if ((xrho != 0.f) && (beta2 < 1.f)) {
-    if (dedx < kCalcdEdxAuto + constants::math::Almost1) { // request to calculate dedx on the fly
-      dedx = BetheBlochSolid(p / getPID().getMass());
-      if (mAbsCharge != 1) {
-        dedx *= mAbsCharge * mAbsCharge;
-      }
+  constexpr value_t kMinP = 0.01f; // kill below this momentum
+
+  auto m = getPID().getMass();
+  if (m > 0 && xrho != 0.f) {
+    // Apply angle correction, if requested
+    if (anglecorr) {
+      value_t csp2 = (1.f - getSnp()) * (1.f + getSnp()); // cos(phi)^2
+      value_t cst2I = (1.f + getTgl() * getTgl());        // 1/cos(lambda)^2
+      value_t angle = gpu::CAMath::Sqrt(cst2I / (csp2));
+      xrho *= angle;
+    }
+    int charge2 = getAbsCharge() * getAbsCharge();
+    value_t p = getP(), p0 = p, p2 = p * p, e2 = p2 + getPID().getMass2(), massInv = 1. / m, bg = p * massInv;
+    value_t e = gpu::CAMath::Sqrt(e2), ekin = e - m, dedx = getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+    value_t dedxDer = 0., dedx1 = dedx;
+#endif
+    if (charge2 != 1) {
+      dedx *= charge2;
     }
-
     value_t dE = dedx * xrho;
-    value_t e = gpu::CAMath::Sqrt(e2);
-    if (gpu::CAMath::Abs(dE) > kMaxELossFrac * e) {
-      return false; // 30% energy loss is too much!
+    int na = 1 + int(gpu::CAMath::Abs(dE) / ekin * ELoss2EKinThreshInv);
+    if (na > MaxELossIter) {
+      na = MaxELossIter;
+    }
+    if (na > 1) {
+      dE /= na;
+      xrho /= na;
+#ifdef _BB_NONCONST_CORR_
+      dedxDer = getBetheBlochSolidDerivativeApprox(dedx1, bg); // require correction for non-constantness of dedx vs betagamma
+      if (charge2 != 1) {
+        dedxDer *= charge2;
+      }
+#endif
     }
-    value_t eupd = e + dE;
-    value_t pupd2 = eupd * eupd - getPID().getMass2();
-    if (pupd2 < kMinP * kMinP) {
+    while (na--) {
+#ifdef _BB_NONCONST_CORR_
+      if (dedxDer != 0.) { // correction for non-constantness of dedx vs beta*gamma (in linear approximation): for a single step dE -> dE * [(exp(dedxDer) - 1)/dedxDer]
+        if (xrho < 0) {
+          dedxDer = -dedxDer; // E.loss ( -> positive derivative)
+        }
+        auto corrC = (gpu::CAMath::Exp(dedxDer) - 1.) / dedxDer;
+        dE *= corrC;
+      }
+#endif
+      e += dE;
+      if (e > m) { // stopped
+        p = gpu::CAMath::Sqrt(e * e - getPID().getMass2());
+      } else {
+        return false;
+      }
+      if (na) {
+        bg = p * massInv;
+        dedx = getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+        dedxDer = getBetheBlochSolidDerivativeApprox(dedx, bg);
+#endif
+        if (charge2 != 1) {
+          dedx *= charge2;
+#ifdef _BB_NONCONST_CORR_
+          dedxDer *= charge2;
+#endif
+        }
+        dE = dedx * xrho;
+      }
+    }
+
+    if (p < kMinP) {
       return false;
     }
-    setQ2Pt(getQ2Pt() * p / gpu::CAMath::Sqrt(pupd2));
+    setQ2Pt(getQ2Pt() * p0 / p);
   }
 
   return true;
@@ -812,10 +968,45 @@ GPUd() typename o2::track::TrackParametrization::yzerr_t TrackParametri
           {v.getSigmaX2() * sn2 + dsxysncs + v.getSigmaY2() * cs2, (sn + cs) * v.getSigmaYZ(), v.getSigmaZ2()}};
 }
 
+//______________________________________________
+template 
+GPUd() typename TrackParametrization::value_t TrackParametrization::getDCAYtoMV(value_t b, value_t xmv, value_t ymv, value_t zmv) const
+{
+  auto ttmp = *this;
+  dim2_t dca;
+  return ttmp.propagateParamToDCA({xmv, ymv, zmv}, b, &dca) ? dca[0] : -9999.;
+}
+
+//______________________________________________
+template 
+GPUd() typename TrackParametrization::value_t TrackParametrization::getDCAZtoMV(value_t b, value_t xmv, value_t ymv, value_t zmv) const
+{
+  auto ttmp = *this;
+  dim2_t dca;
+  return ttmp.propagateParamToDCA({xmv, ymv, zmv}, b, &dca) ? dca[1] : -9999.;
+}
+
+#ifndef GPUCA_ALIGPUCODE
+//______________________________________________
+template 
+void TrackParametrization::toFwdTrackPar(TrackParFwd& t) const
+{
+  auto p = getXYZGlo();
+  t.setZ(p.Z());
+  t.setX(p.X());
+  t.setY(p.Y());
+  t.setPhi(getPhi());
+  t.setTanl(getTgl());
+  t.setInvQPt(getQ2Pt());
+}
+#endif
+
 namespace o2::track
 {
+#if !defined(GPUCA_GPUCODE) || defined(GPUCA_GPUCODE_DEVICE) // FIXME: DR: WORKAROUND to avoid CUDA bug creating host symbols for device code.
 template class TrackParametrization;
-#ifndef GPUCA_GPUCODE_DEVICE
+#endif
+#ifndef GPUCA_GPUCODE
 template class TrackParametrization;
 #endif
 } // namespace o2::track
diff --git a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx
index 7bb9491c612e8..eb8071ec0073d 100644
--- a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx
+++ b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx
@@ -12,17 +12,16 @@
 #include "ReconstructionDataFormats/TrackParametrizationWithError.h"
 #include "ReconstructionDataFormats/Vertex.h"
 #include "ReconstructionDataFormats/DCA.h"
+#include "CommonConstants/MathConstants.h"
 #include 
 
 #ifndef GPUCA_GPUCODE_DEVICE
 #include 
-#ifndef GPUCA_STANDALONE
-#include "Math/SMatrix.h"
-#endif
 #endif
 
 #ifndef GPUCA_ALIGPUCODE
 #include 
+#include "ReconstructionDataFormats/TrackFwd.h"
 #endif
 
 using namespace o2::track;
@@ -45,7 +44,7 @@ GPUd() void TrackParametrizationWithError::invert()
 
 //______________________________________________________________
 template 
-GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, value_t b)
+GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, value_t bz)
 {
   //----------------------------------------------------------------
   // propagate this track to the plane X=xk (cm) in the field "b" (kG)
@@ -54,7 +53,7 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, valu
   if (gpu::CAMath::Abs(dx) < constants::math::Almost0) {
     return true;
   }
-  value_t crv = this->getCurvature(b);
+  value_t crv = this->getCurvature(bz);
   value_t x2r = crv * dx;
   value_t f1 = this->getSnp(), f2 = f1 + x2r;
   if ((gpu::CAMath::Abs(f1) > constants::math::Almost1) || (gpu::CAMath::Abs(f2) > constants::math::Almost1)) {
@@ -68,7 +67,8 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, valu
   if (gpu::CAMath::Abs(r2) < constants::math::Almost0) {
     return false;
   }
-  double dy2dx = (f1 + f2) / (r1 + r2);
+  double r1pr2Inv = 1. / (r1 + r2);
+  double dy2dx = (f1 + f2) * r1pr2Inv;
   bool arcz = gpu::CAMath::Abs(x2r) > 0.05f;
   params_t dP{0.f};
   if (arcz) {
@@ -108,14 +108,110 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, valu
           &c44 = mC[kSigQ2Pt2];
 
   // evaluate matrix in double prec.
-  double rinv = 1. / r1;
-  double r3inv = rinv * rinv * rinv;
-  double f24 = dx * b * constants::math::B2C; // x2r/mP[kQ2Pt];
-  double f02 = dx * r3inv;
-  double f04 = 0.5 * f24 * f02;
-  double f12 = f02 * this->getTgl() * f1;
-  double f14 = 0.5 * f24 * f12; // 0.5*f24*f02*getTgl()*f1;
-  double f13 = dx * rinv;
+  value_t kb = bz * constants::math::B2C;
+  double r2inv = 1. / r2, r1inv = 1. / r1;
+  double dx2r1pr2 = dx * r1pr2Inv;
+
+  double hh = dx2r1pr2 * r2inv * (1. + r1 * r2 + f1 * f2), jj = dx * (dy2dx - f2 * r2inv);
+  double f02 = hh * r1inv;
+  double f04 = hh * dx2r1pr2 * kb;
+  double f24 = dx * kb; // x2r/mP[kQ2Pt];
+  double f12 = this->getTgl() * (f02 * f2 + jj);
+  double f13 = dx * (r2 + f2 * dy2dx);
+  double f14 = this->getTgl() * (f04 * f2 + jj * f24);
+
+  // b = C*ft
+  double b00 = f02 * c20 + f04 * c40, b01 = f12 * c20 + f14 * c40 + f13 * c30;
+  double b02 = f24 * c40;
+  double b10 = f02 * c21 + f04 * c41, b11 = f12 * c21 + f14 * c41 + f13 * c31;
+  double b12 = f24 * c41;
+  double b20 = f02 * c22 + f04 * c42, b21 = f12 * c22 + f14 * c42 + f13 * c32;
+  double b22 = f24 * c42;
+  double b40 = f02 * c42 + f04 * c44, b41 = f12 * c42 + f14 * c44 + f13 * c43;
+  double b42 = f24 * c44;
+  double b30 = f02 * c32 + f04 * c43, b31 = f12 * c32 + f14 * c43 + f13 * c33;
+  double b32 = f24 * c43;
+
+  // a = f*b = f*C*ft
+  double a00 = f02 * b20 + f04 * b40, a01 = f02 * b21 + f04 * b41, a02 = f02 * b22 + f04 * b42;
+  double a11 = f12 * b21 + f14 * b41 + f13 * b31, a12 = f12 * b22 + f14 * b42 + f13 * b32;
+  double a22 = f24 * b42;
+
+  // F*C*Ft = C + (b + bt + a)
+  c00 += b00 + b00 + a00;
+  c10 += b10 + b01 + a01;
+  c20 += b20 + b02 + a02;
+  c30 += b30;
+  c40 += b40;
+  c11 += b11 + b11 + a11;
+  c21 += b21 + b12 + a12;
+  c31 += b31;
+  c41 += b41;
+  c22 += b22 + b22 + a22;
+  c32 += b32;
+  c42 += b42;
+
+  checkCovariance();
+
+  return true;
+}
+
+//______________________________________________________________
+template 
+GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, TrackParametrization& linRef0, value_t bz)
+{
+  //----------------------------------------------------------------
+  // propagate this track to the plane X=xk (cm) in the field "b" (kG), using linRef as linearization point
+  //----------------------------------------------------------------
+  if (this->getAbsCharge() == 0) {
+    bz = 0;
+  }
+  value_t dx = xk - this->getX();
+  if (gpu::CAMath::Abs(dx) < constants::math::Almost0) {
+    this->setX(xk);
+    linRef0.setX(xk);
+    return true;
+  }
+  // propagate reference track
+  TrackParametrization linRef1 = linRef0;
+  if (!linRef1.propagateTo(xk, bz)) {
+    return false;
+  }
+  value_t kb = bz * constants::math::B2C;
+  // evaluate in double prec.
+  double snpRef0 = linRef0.getSnp(), cspRef0 = gpu::CAMath::Sqrt((1 - snpRef0) * (1 + snpRef0));
+  double snpRef1 = linRef1.getSnp(), cspRef1 = gpu::CAMath::Sqrt((1 - snpRef1) * (1 + snpRef1));
+  double cspRef0Inv = 1 / cspRef0, cspRef1Inv = 1 / cspRef1, cc = cspRef0 + cspRef1, ccInv = 1 / cc, dy2dx = (snpRef0 + snpRef1) * ccInv;
+  double dxccInv = dx * ccInv, hh = dxccInv * cspRef1Inv * (1 + cspRef0 * cspRef1 + snpRef0 * snpRef1), jj = dx * (dy2dx - snpRef1 * cspRef1Inv);
+
+  double f02 = hh * cspRef0Inv;
+  double f04 = hh * dxccInv * kb;
+  double f24 = dx * kb;
+  double f12 = linRef0.getTgl() * (f02 * snpRef1 + jj);
+  double f13 = dx * (cspRef1 + snpRef1 * dy2dx); // dS
+  double f14 = linRef0.getTgl() * (f04 * snpRef1 + jj * f24);
+
+  // difference between the current and reference state
+  value_t diff[5];
+  for (int i = 0; i < 5; i++) {
+    diff[i] = this->getParam(i) - linRef0.getParam(i);
+  }
+  value_t snpUpd = snpRef1 + diff[kSnp] + f24 * diff[kQ2Pt];
+  if (gpu::CAMath::Abs(snpUpd) > constants::math::Almost1) {
+    return false;
+  }
+  linRef0 = linRef1; // update reference track
+  this->setX(xk);
+  this->setY(linRef1.getY() + diff[kY] + f02 * diff[kSnp] + f04 * diff[kQ2Pt]);
+  this->setZ(linRef1.getZ() + diff[kZ] + f13 * diff[kTgl] + f14 * diff[kQ2Pt]);
+  this->setSnp(snpUpd);
+  this->setTgl(linRef1.getTgl() + diff[kTgl]);
+  this->setQ2Pt(linRef1.getQ2Pt() + diff[kQ2Pt]);
+
+  value_t &c00 = mC[kSigY2], &c10 = mC[kSigZY], &c11 = mC[kSigZ2], &c20 = mC[kSigSnpY], &c21 = mC[kSigSnpZ],
+          &c22 = mC[kSigSnp2], &c30 = mC[kSigTglY], &c31 = mC[kSigTglZ], &c32 = mC[kSigTglSnp], &c33 = mC[kSigTgl2],
+          &c40 = mC[kSigQ2PtY], &c41 = mC[kSigQ2PtZ], &c42 = mC[kSigQ2PtSnp], &c43 = mC[kSigQ2PtTgl],
+          &c44 = mC[kSigQ2Pt2];
 
   // b = C*ft
   double b00 = f02 * c20 + f04 * c40, b01 = f12 * c20 + f14 * c40 + f13 * c30;
@@ -153,6 +249,14 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, valu
   return true;
 }
 
+//______________________________________________________________
+template 
+GPUd() bool TrackParametrizationWithError::testRotate(value_t) const
+{
+  // no ops
+  return true;
+}
+
 //______________________________________________________________
 template 
 GPUd() bool TrackParametrizationWithError::rotate(value_t alpha)
@@ -208,6 +312,101 @@ GPUd() bool TrackParametrizationWithError::rotate(value_t alpha)
   return true;
 }
 
+//______________________________________________________________
+template 
+GPUd() bool TrackParametrizationWithError::rotate(value_t alpha, TrackParametrization& linRef0, value_t bz)
+{
+  // RS: similar to int32_t GPUTPCGMPropagator::RotateToAlpha(float newAlpha), i.e. rotate the track to new frame alpha, using linRef as linearization point
+  // rotate to alpha frame the reference (linearization point) trackParam, then align the current track to it
+  if (gpu::CAMath::Abs(this->getSnp()) > constants::math::Almost1) {
+    LOGP(debug, "Precondition is not satisfied: |sin(phi)|>1 ! {:f}", this->getSnp());
+    return false;
+  }
+  //
+  math_utils::detail::bringToPMPi(alpha);
+  //
+  value_t ca = 0, sa = 0;
+  TrackParametrization linRef1 = linRef0;
+  // rotate the reference, adjusting alpha to +-pi, return precalculated cos and sin of alpha - alphaOld
+  if (!linRef1.rotateParam(alpha, ca, sa)) {
+    return false;
+  }
+
+  value_t trackX = this->getX() * ca + this->getY() * sa; // X of the rotated current track
+  if (!linRef1.propagateParamTo(trackX, bz)) {
+    return false;
+  }
+
+  // now rotate the current track
+  value_t snp = this->getSnp(), csp = gpu::CAMath::Sqrt((1.f - snp) * (1.f + snp)), updSnp = snp * ca - csp * sa;
+  if ((csp * ca + snp * sa) < 0 || gpu::CAMath::Abs(updSnp) > constants::math::Almost1) {
+    // LOGP(warning,"Rotation failed: local cos(phi) would become {:.2f}", csp * ca + snp * sa);
+    return false;
+  }
+  this->setY(-sa * this->getX() + ca * this->getY());
+  this->setX(trackX);
+  this->setSnp(updSnp);
+  this->setAlpha(alpha);
+
+  // rotate covariance, accounting for the extra error from the rotated X
+  value_t snpRef0 = linRef0.getSnp(), cspRef0 = gpu::CAMath::Sqrt((value_t(1) - snpRef0) * (value_t(1) + snpRef0)); // original reference
+  value_t snpRef1 = linRef1.getSnp(), cspRef1 = ca * cspRef0 + sa * snpRef0;                                        // rotated reference
+  value_t rr = cspRef1 / cspRef0;                                                                                   // cos1_ref / cos0_ref
+
+  // "extra row" of the lower triangle of cov. matrix
+  value_t cXSigY = mC[kSigY2] * ca * sa;
+  value_t cXSigZ = mC[kSigZY] * sa;
+  value_t cXSigSnp = mC[kSigSnpY] * rr * sa;
+  value_t cXSigTgl = mC[kSigTglY] * sa;
+  value_t cXSigQ2Pt = mC[kSigQ2PtY] * sa;
+  value_t cSigX2 = mC[kSigY2] * sa * sa;
+
+  // plane rotation of existing cov matrix
+  mC[kSigY2] *= ca * ca;
+  mC[kSigZY] *= ca;
+  mC[kSigSnpY] *= ca * rr;
+  mC[kSigSnpZ] *= rr;
+  mC[kSigSnp2] *= rr * rr;
+  mC[kSigTglY] *= ca;
+  mC[kSigTglSnp] *= rr;
+  mC[kSigQ2PtY] *= ca;
+  mC[kSigQ2PtSnp] *= rr;
+
+  // transport covariance from pseudo 6x6 matrix to usual 5x5, Jacobian (trust to Sergey):
+  auto cspRef1Inv = value_t(1) / cspRef1;
+  auto j3 = -snpRef1 * cspRef1Inv;          // -pYmod/pXmod = -tg_pho = -sin_phi_mod / cos_phi_mod
+  auto j4 = -linRef1.getTgl() * cspRef1Inv; // -pZmod/pXmod = -tgl_mod / cos_phi_mod
+  auto j5 = linRef1.getCurvature(bz);
+  //       Y  Z Sin DzDs q/p  X
+  //  { {  1, 0, 0,  0,  0,  j3 }, // Y
+  //    {  0, 1, 0,  0,  0,  j4 }, // Z
+  //    {  0, 0, 1,  0,  0,  j5 }, // snp
+  //    {  0, 0, 0,  1,  0,   0 }, // tgl
+  //    {  0, 0, 0,  0,  1,   0 } }; // q/pt
+  auto hXSigY = cXSigY + cSigX2 * j3;
+  auto hXSigZ = cXSigZ + cSigX2 * j4;
+  auto hXSigSnp = cXSigSnp + cSigX2 * j5;
+
+  mC[kSigY2] += j3 * (cXSigY + hXSigY);
+  mC[kSigZ2] += j4 * (cXSigZ + hXSigZ);
+  mC[kSigSnpY] += cXSigSnp * j3 + hXSigY * j5;
+  mC[kSigSnp2] += j5 * (cXSigSnp + hXSigSnp);
+  mC[kSigTglZ] += cXSigTgl * j4;
+  mC[kSigQ2PtY] += cXSigQ2Pt * j3;
+  mC[kSigQ2PtSnp] += cXSigQ2Pt * j5;
+
+  mC[kSigZY] += cXSigZ * j3 + hXSigY * j4;
+  mC[kSigSnpZ] += cXSigSnp * j4 + hXSigZ * j5;
+  mC[kSigTglY] += cXSigTgl * j3;
+  mC[kSigTglSnp] += cXSigTgl * j5;
+  mC[kSigQ2PtZ] += cXSigQ2Pt * j4;
+
+  checkCovariance();
+  linRef0 = linRef1;
+
+  return true;
+}
+
 //_______________________________________________________________________
 template 
 GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dataformats::VertexBase& vtx, value_t b, o2::dataformats::DCA* dca, value_t maxD)
@@ -222,6 +421,10 @@ GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dat
   // Estimate the impact parameter neglecting the track curvature
   value_t d = gpu::CAMath::Abs(x * snp - y * csp);
   if (d > maxD) {
+    if (dca) { // provide default DCA for failed propag
+      dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA,
+               o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov);
+    }
     return false;
   }
   value_t crv = this->getCurvature(b);
@@ -239,9 +442,11 @@ GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dat
   if (!tmpT.rotate(alp) || !tmpT.propagateTo(xv, b)) {
 #if !defined(GPUCA_ALIGPUCODE)
     LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx << " | Track is: " << tmpT.asString();
-#elif !defined(GPUCA_NO_FMT)
-    LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx;
 #endif
+    if (dca) { // provide default DCA for failed propag
+      dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA,
+               o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov);
+    }
     return false;
   }
   *this = tmpT;
@@ -256,7 +461,7 @@ GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dat
 //______________________________________________________________
 template 
 GPUd() TrackParametrizationWithError::TrackParametrizationWithError(const dim3_t& xyz, const dim3_t& pxpypz,
-                                                                             const gpu::gpustd::array& cv, int charge, bool sectorAlpha, const PID pid)
+                                                                             const std::array& cv, int charge, bool sectorAlpha, const PID pid)
 {
   // construct track param and covariance from kinematics and lab errors
   set(xyz, pxpypz, cv, charge, sectorAlpha, pid);
@@ -265,7 +470,7 @@ GPUd() TrackParametrizationWithError::TrackParametrizationWithError(con
 //______________________________________________________________
 template 
 GPUd() void TrackParametrizationWithError::set(const dim3_t& xyz, const dim3_t& pxpypz,
-                                                        const gpu::gpustd::array& cv, int charge, bool sectorAlpha, const PID pid)
+                                                        const std::array& cv, int charge, bool sectorAlpha, const PID pid)
 {
   // set track param and covariance from kinematics and lab errors
 
@@ -322,8 +527,9 @@ GPUd() void TrackParametrizationWithError::set(const dim3_t& xyz, const
   math_utils::detail::rotateZ(ver, -alp);
   math_utils::detail::rotateZ(mom, -alp);
   //
-  value_t pt = gpu::CAMath::Sqrt(mom[0] * mom[0] + mom[1] * mom[1]);
-  value_t ptI = 1.f / pt;
+  const value_t pt2 = mom[0] * mom[0] + mom[1] * mom[1];
+  const value_t pt = gpu::CAMath::Sqrt(pt2);
+  const value_t ptI = 1.f / pt;
   this->setX(ver[0]);
   this->setAlpha(alp);
   this->setY(ver[1]);
@@ -340,89 +546,53 @@ GPUd() void TrackParametrizationWithError::set(const dim3_t& xyz, const
     this->setSnp(-1.f + kSafe); // Protection
   }
   //
-  // Covariance matrix (formulas to be simplified)
-  value_t r = mom[0] * ptI; // cos(phi)
-  value_t cv34 = gpu::CAMath::Sqrt(cv[3] * cv[3] + cv[4] * cv[4]);
-  //
-  int special = 0;
-  value_t sgcheck = r * sn + this->getSnp() * cs;
-  if (gpu::CAMath::Abs(sgcheck) > 1 - kSafe) { // special case: lab phi is +-pi/2
-    special = 1;
-    sgcheck = sgcheck < 0 ? -1.f : 1.f;
-  } else if (gpu::CAMath::Abs(sgcheck) < kSafe) {
-    sgcheck = cs < 0 ? -1.0f : 1.0f;
-    special = 2; // special case: lab phi is 0
+  // Covariance matrix from the fixed-alpha Jacobian
+  // d(Y,Z,snp,tgl,q/pt) / d(X,Y,Z,Px,Py,Pz).
+  const value_t pt3I = ptI / pt2;
+  const value_t qeff = charge ? static_cast(charge) : 1.f;
+
+  value_t cLab[6][6] = {};
+  int idx = 0;
+  for (int i = 0; i < 6; ++i) {
+    for (int j = 0; j <= i; ++j) {
+      cLab[i][j] = cLab[j][i] = cv[idx++];
+    }
   }
-  //
-  mC[kSigY2] = cv[0] + cv[2];
-  mC[kSigZY] = (-cv[3] * sn) < 0 ? -cv34 : cv34;
-  mC[kSigZ2] = cv[5];
-  //
-  value_t ptI2 = ptI * ptI;
-  value_t tgl2 = this->getTgl() * this->getTgl();
-  if (special == 1) {
-    mC[kSigSnpY] = cv[6] * ptI;
-    mC[kSigSnpZ] = -sgcheck * cv[8] * r * ptI;
-    mC[kSigSnp2] = gpu::CAMath::Abs(cv[9] * r * r * ptI2);
-    mC[kSigTglY] = (cv[10] * this->getTgl() - sgcheck * cv[15]) * ptI / r;
-    mC[kSigTglZ] = (cv[17] - sgcheck * cv[12] * this->getTgl()) * ptI;
-    mC[kSigTglSnp] = (-sgcheck * cv[18] + cv[13] * this->getTgl()) * r * ptI2;
-    mC[kSigTgl2] = gpu::CAMath::Abs(cv[20] - 2 * sgcheck * cv[19] * mC[4] + cv[14] * tgl2) * ptI2;
-    mC[kSigQ2PtY] = cv[10] * ptI2 / r * charge;
-    mC[kSigQ2PtZ] = -sgcheck * cv[12] * ptI2 * charge;
-    mC[kSigQ2PtSnp] = cv[13] * r * ptI * ptI2 * charge;
-    mC[kSigQ2PtTgl] = (-sgcheck * cv[19] + cv[14] * this->getTgl()) * r * ptI2 * ptI;
-    mC[kSigQ2Pt2] = gpu::CAMath::Abs(cv[14] * ptI2 * ptI2);
-  } else if (special == 2) {
-    mC[kSigSnpY] = -cv[10] * ptI * cs / sn;
-    mC[kSigSnpZ] = cv[12] * cs * ptI;
-    mC[kSigSnp2] = gpu::CAMath::Abs(cv[14] * cs * cs * ptI2);
-    mC[kSigTglY] = (sgcheck * cv[6] * this->getTgl() - cv[15]) * ptI / sn;
-    mC[kSigTglZ] = (cv[17] - sgcheck * cv[8] * this->getTgl()) * ptI;
-    mC[kSigTglSnp] = (cv[19] - sgcheck * cv[13] * this->getTgl()) * cs * ptI2;
-    mC[kSigTgl2] = gpu::CAMath::Abs(cv[20] - 2 * sgcheck * cv[18] * this->getTgl() + cv[9] * tgl2) * ptI2;
-    mC[kSigQ2PtY] = sgcheck * cv[6] * ptI2 / sn * charge;
-    mC[kSigQ2PtZ] = -sgcheck * cv[8] * ptI2 * charge;
-    mC[kSigQ2PtSnp] = -sgcheck * cv[13] * cs * ptI * ptI2 * charge;
-    mC[kSigQ2PtTgl] = (-sgcheck * cv[18] + cv[9] * this->getTgl()) * ptI2 * ptI * charge;
-    mC[kSigQ2Pt2] = gpu::CAMath::Abs(cv[9] * ptI2 * ptI2);
-  } else {
-    double m00 = -sn; // m10=cs;
-    double m23 = -pt * (sn + this->getSnp() * cs / r), m43 = -pt * pt * (r * cs - this->getSnp() * sn);
-    double m24 = pt * (cs - this->getSnp() * sn / r), m44 = -pt * pt * (r * sn + this->getSnp() * cs);
-    double m35 = pt, m45 = -pt * pt * this->getTgl();
-    //
-    if (charge) { // RS: this is a hack, proper treatment to be implemented
-      m43 *= charge;
-      m44 *= charge;
-      m45 *= charge;
+
+  value_t jac[5][6] = {};
+  jac[kY][0] = -sn;
+  jac[kY][1] = cs;
+  jac[kZ][2] = 1.;
+
+  const value_t u = mom[0];
+  const value_t v = mom[1];
+  const value_t w = mom[2];
+  const value_t dSnpDu = -u * v * pt3I;
+  const value_t dSnpDv = u * u * pt3I;
+  const value_t dTglDu = -w * u * pt3I;
+  const value_t dTglDv = -w * v * pt3I;
+  const value_t dTglDw = ptI;
+  const value_t dQ2PtDu = -qeff * u * pt3I;
+  const value_t dQ2PtDv = -qeff * v * pt3I;
+
+  jac[kSnp][3] = dSnpDu * cs - dSnpDv * sn;
+  jac[kSnp][4] = dSnpDu * sn + dSnpDv * cs;
+  jac[kTgl][3] = dTglDu * cs - dTglDv * sn;
+  jac[kTgl][4] = dTglDu * sn + dTglDv * cs;
+  jac[kTgl][5] = dTglDw;
+  jac[kQ2Pt][3] = dQ2PtDu * cs - dQ2PtDv * sn;
+  jac[kQ2Pt][4] = dQ2PtDu * sn + dQ2PtDv * cs;
+
+  for (int i = 0; i < kNParams; ++i) {
+    for (int j = 0; j <= i; ++j) {
+      value_t cij = 0.;
+      for (int k = 0; k < 6; ++k) {
+        for (int l = 0; l < 6; ++l) {
+          cij += jac[i][k] * cLab[k][l] * jac[j][l];
+        }
+      }
+      mC[CovarMap[i][j]] = cij;
     }
-    //
-    double a1 = cv[13] - cv[9] * (m23 * m44 + m43 * m24) / m23 / m43;
-    double a2 = m23 * m24 - m23 * (m23 * m44 + m43 * m24) / m43;
-    double a3 = m43 * m44 - m43 * (m23 * m44 + m43 * m24) / m23;
-    double a4 = cv[14] + 2. * cv[9];
-    double a5 = m24 * m24 - 2. * m24 * m44 * m23 / m43;
-    double a6 = m44 * m44 - 2. * m24 * m44 * m43 / m23;
-    //
-    mC[kSigSnpY] = (cv[10] * m43 - cv[6] * m44) / (m24 * m43 - m23 * m44) / m00;
-    mC[kSigQ2PtY] = (cv[6] / m00 - mC[kSigSnpY] * m23) / m43;
-    mC[kSigTglY] = (cv[15] / m00 - mC[kSigQ2PtY] * m45) / m35;
-    mC[kSigSnpZ] = (cv[12] * m43 - cv[8] * m44) / (m24 * m43 - m23 * m44);
-    mC[kSigQ2PtZ] = (cv[8] - mC[kSigSnpZ] * m23) / m43;
-    mC[kSigTglZ] = cv[17] / m35 - mC[kSigQ2PtZ] * m45 / m35;
-    mC[kSigSnp2] = gpu::CAMath::Abs((a4 * a3 - a6 * a1) / (a5 * a3 - a6 * a2));
-    mC[kSigQ2Pt2] = gpu::CAMath::Abs((a1 - a2 * mC[kSigSnp2]) / a3);
-    mC[kSigQ2PtSnp] = (cv[9] - mC[kSigSnp2] * m23 * m23 - mC[kSigQ2Pt2] * m43 * m43) / m23 / m43;
-    double b1 = cv[18] - mC[kSigQ2PtSnp] * m23 * m45 - mC[kSigQ2Pt2] * m43 * m45;
-    double b2 = m23 * m35;
-    double b3 = m43 * m35;
-    double b4 = cv[19] - mC[kSigQ2PtSnp] * m24 * m45 - mC[kSigQ2Pt2] * m44 * m45;
-    double b5 = m24 * m35;
-    double b6 = m44 * m35;
-    mC[kSigTglSnp] = (b4 - b6 * b1 / b3) / (b5 - b6 * b2 / b3);
-    mC[kSigQ2PtTgl] = b1 / b3 - b2 * mC[kSigTglSnp] / b3;
-    mC[kSigTgl2] = gpu::CAMath::Abs((cv[20] - mC[kSigQ2Pt2] * (m45 * m45) - mC[kSigQ2PtTgl] * 2.f * m35 * m45) / (m35 * m35));
   }
   checkCovariance();
 }
@@ -465,14 +635,14 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
   if (gpu::CAMath::Abs(r2) < constants::math::Almost0) {
     return false;
   }
-
-  value_t dy2dx = (f1 + f2) / (r1 + r2);
+  double r1pr2Inv = 1. / (r1 + r2), r2inv = 1. / r2, r1inv = 1. / r1;
+  double dy2dx = (f1 + f2) * r1pr2Inv, dx2r1pr2 = dx * r1pr2Inv;
   value_t step = (gpu::CAMath::Abs(x2r) < 0.05f) ? dx * gpu::CAMath::Abs(r2 + f2 * dy2dx)                                                   // chord
                                                  : 2.f * gpu::CAMath::ASin(0.5f * dx * gpu::CAMath::Sqrt(1.f + dy2dx * dy2dx) * crv) / crv; // arc
   step *= gpu::CAMath::Sqrt(1.f + this->getTgl() * this->getTgl());
   //
   // get the track x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha in the Global System
-  gpu::gpustd::array vecLab{0.f};
+  std::array vecLab{0.f};
   if (!this->getPosDirGlo(vecLab)) {
     return false;
   }
@@ -482,15 +652,16 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
           &c22 = mC[kSigSnp2], &c30 = mC[kSigTglY], &c31 = mC[kSigTglZ], &c32 = mC[kSigTglSnp], &c33 = mC[kSigTgl2],
           &c40 = mC[kSigQ2PtY], &c41 = mC[kSigQ2PtZ], &c42 = mC[kSigQ2PtSnp], &c43 = mC[kSigQ2PtTgl],
           &c44 = mC[kSigQ2Pt2];
+
   // evaluate matrix in double prec.
-  double rinv = 1. / r1;
-  double r3inv = rinv * rinv * rinv;
-  double f24 = dx * b[2] * constants::math::B2C; // x2r/track[kQ2Pt];
-  double f02 = dx * r3inv;
-  double f04 = 0.5 * f24 * f02;
-  double f12 = f02 * this->getTgl() * f1;
-  double f14 = 0.5 * f24 * f12; // 0.5*f24*f02*getTgl()*f1;
-  double f13 = dx * rinv;
+  value_t kb = b[2] * constants::math::B2C;
+  double hh = dx2r1pr2 * r2inv * (1. + r1 * r2 + f1 * f2), jj = dx * (dy2dx - f2 * r2inv);
+  double f02 = hh * r1inv;
+  double f04 = hh * dx2r1pr2 * kb;
+  double f24 = dx * kb; // x2r/mP[kQ2Pt];
+  double f12 = this->getTgl() * (f02 * f2 + jj);
+  double f13 = dx * (r2 + f2 * dy2dx);
+  double f14 = this->getTgl() * (f04 * f2 + jj * f24);
 
   // b = C*ft
   double b00 = f02 * c20 + f04 * c40, b01 = f12 * c20 + f14 * c40 + f13 * c30;
@@ -539,7 +710,7 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
     costet = b[2] / bb;
     sintet = bt / bb;
   }
-  gpu::gpustd::array vect{costet * cosphi * vecLab[0] + costet * sinphi * vecLab[1] - sintet * vecLab[2],
+  std::array vect{costet * cosphi * vecLab[0] + costet * sinphi * vecLab[1] - sintet * vecLab[2],
                                       -sinphi * vecLab[0] + cosphi * vecLab[1],
                                       sintet * cosphi * vecLab[0] + sintet * sinphi * vecLab[1] + costet * vecLab[2],
                                       costet * cosphi * vecLab[3] + costet * sinphi * vecLab[4] - sintet * vecLab[5],
@@ -548,8 +719,8 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
                                       vecLab[6]};
 
   // Do the helix step
-  value_t sgn = this->getSign();
-  g3helx3(sgn * bb, step, vect);
+  value_t q = this->getCharge();
+  g3helx3(q * bb, step, vect);
 
   // Rotate back to the Global System
   vecLab[0] = cosphi * costet * vect[0] - sinphi * vect[1] + cosphi * sintet * vect[2];
@@ -571,14 +742,14 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
 
   // Do the final correcting step to the target plane (linear approximation)
   value_t x = vecLab[0], y = vecLab[1], z = vecLab[2];
-  if (gpu::CAMath::Abs(dx) > constants::math::Almost0) {
+  if (gpu::CAMath::Abs(x - xk) > constants::math::Almost0) {
     if (gpu::CAMath::Abs(vecLab[3]) < constants::math::Almost0) {
       return false;
     }
-    dx = xk - vecLab[0];
-    x += dx;
-    y += vecLab[4] / vecLab[3] * dx;
-    z += vecLab[5] / vecLab[3] * dx;
+    auto dxFin = xk - vecLab[0];
+    x += dxFin;
+    y += vecLab[4] / vecLab[3] * dxFin;
+    z += vecLab[5] / vecLab[3] * dxFin;
   }
 
   // Calculate the track parameters
@@ -588,7 +759,199 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
   this->setZ(z);
   this->setSnp(vecLab[4] * t);
   this->setTgl(vecLab[5] * t);
-  this->setQ2Pt(sgn * t / vecLab[6]);
+  this->setQ2Pt(q * t / vecLab[6]);
+
+  return true;
+}
+
+//____________________________________________________________
+template 
+GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, TrackParametrization& linRef0, const dim3_t& b)
+{
+  //----------------------------------------------------------------
+  // Extrapolate this track to the plane X=xk in the field b[].
+  //
+  // X [cm] is in the "tracking coordinate system" of this track.
+  // b[]={Bx,By,Bz} [kG] is in the Global coordidate system.
+  //----------------------------------------------------------------
+
+  value_t dx = xk - this->getX();
+  if (gpu::CAMath::Abs(dx) < constants::math::Almost0) {
+    return true;
+  }
+  // Do not propagate tracks outside the ALICE detector
+  if (gpu::CAMath::Abs(dx) > 1e5 || gpu::CAMath::Abs(this->getY()) > 1e5 || gpu::CAMath::Abs(this->getZ()) > 1e5) {
+    LOG(warning) << "Anomalous track, target X:" << xk;
+    //    print();
+    return false;
+  }
+  if (gpu::CAMath::Abs(dx) < constants::math::Almost0) {
+    this->setX(xk);
+    linRef0.setX(xk);
+    return true;
+  }
+  // preliminary calculations to find the step size
+  value_t crv = (gpu::CAMath::Abs(b[2]) < constants::math::Almost0) ? 0.f : linRef0.getCurvature(b[2]);
+  if (gpu::CAMath::Abs(crv) < constants::math::Almost0) {
+    return propagateTo(xk, linRef0, 0.);
+  }
+  value_t kb = b[2] * constants::math::B2C, x2r = crv * dx;
+  // evaluate in double prec.
+  value_t snpRef0 = linRef0.getSnp(), snpRef1 = snpRef0 + x2r;
+  if ((gpu::CAMath::Abs(snpRef0) > constants::math::Almost1) || (gpu::CAMath::Abs(snpRef1) > constants::math::Almost1)) {
+    return false;
+  }
+  value_t cspRef0 = gpu::CAMath::Sqrt((1 - snpRef0) * (1 + snpRef0)), cspRef1 = gpu::CAMath::Sqrt((1 - snpRef1) * (1 + snpRef1));
+  if (gpu::CAMath::Abs(cspRef0) < constants::math::Almost0 || gpu::CAMath::Abs(cspRef1) < constants::math::Almost0) {
+    return false;
+  }
+  value_t cspRef0Inv = value_t(1) / cspRef0, cspRef1Inv = value_t(1) / cspRef1, cc = cspRef0 + cspRef1, ccInv = value_t(1) / cc, dy2dx = (snpRef0 + snpRef1) * ccInv;
+  value_t step = (gpu::CAMath::Abs(crv * dx) < 0.05f) ? dx * (cspRef1 + snpRef1 * dy2dx) : 2. * gpu::CAMath::ASin(0.5 * dx * gpu::CAMath::Sqrt(1.f + dy2dx * dy2dx) * crv) / crv; // arc
+  step *= gpu::CAMath::Sqrt(1.f + linRef0.getTgl() * linRef0.getTgl());
+
+  //
+  // get the track x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha in the Global System
+  std::array vecLab{0.f};
+  if (!linRef0.getPosDirGlo(vecLab)) {
+    return false;
+  }
+  //
+  // Rotate to the system where Bx=By=0.
+  value_t bxy2 = b[0] * b[0] + b[1] * b[1];
+  value_t bt = gpu::CAMath::Sqrt(bxy2);
+  value_t cosphi = 1.f, sinphi = 0.f;
+  if (bt > constants::math::Almost0) {
+    cosphi = b[0] / bt;
+    sinphi = b[1] / bt;
+  }
+  value_t bb = gpu::CAMath::Sqrt(bxy2 + b[2] * b[2]);
+  value_t costet = 1., sintet = 0.;
+  if (bb > constants::math::Almost0) {
+    costet = b[2] / bb;
+    sintet = bt / bb;
+  }
+  std::array vect{costet * cosphi * vecLab[0] + costet * sinphi * vecLab[1] - sintet * vecLab[2],
+                              -sinphi * vecLab[0] + cosphi * vecLab[1],
+                              sintet * cosphi * vecLab[0] + sintet * sinphi * vecLab[1] + costet * vecLab[2],
+                              costet * cosphi * vecLab[3] + costet * sinphi * vecLab[4] - sintet * vecLab[5],
+                              -sinphi * vecLab[3] + cosphi * vecLab[4],
+                              sintet * cosphi * vecLab[3] + sintet * sinphi * vecLab[4] + costet * vecLab[5],
+                              vecLab[6]};
+
+  // Do the helix step
+  value_t q = this->getCharge();
+  g3helx3(q * bb, step, vect);
+
+  // Rotate back to the Global System
+  vecLab[0] = cosphi * costet * vect[0] - sinphi * vect[1] + cosphi * sintet * vect[2];
+  vecLab[1] = sinphi * costet * vect[0] + cosphi * vect[1] + sinphi * sintet * vect[2];
+  vecLab[2] = -sintet * vect[0] + costet * vect[2];
+
+  vecLab[3] = cosphi * costet * vect[3] - sinphi * vect[4] + cosphi * sintet * vect[5];
+  vecLab[4] = sinphi * costet * vect[3] + cosphi * vect[4] + sinphi * sintet * vect[5];
+  vecLab[5] = -sintet * vect[3] + costet * vect[5];
+
+  // Rotate back to the Tracking System
+  value_t sinalp = -vecLab[7], cosalp = vecLab[8];
+  value_t t = cosalp * vecLab[0] - sinalp * vecLab[1];
+  vecLab[1] = sinalp * vecLab[0] + cosalp * vecLab[1];
+  vecLab[0] = t;
+  t = cosalp * vecLab[3] - sinalp * vecLab[4];
+  vecLab[4] = sinalp * vecLab[3] + cosalp * vecLab[4];
+  vecLab[3] = t;
+
+  // Do the final correcting step to the target plane (linear approximation)
+  value_t x = vecLab[0], y = vecLab[1], z = vecLab[2];
+  if (gpu::CAMath::Abs(x - xk) > constants::math::Almost0) {
+    if (gpu::CAMath::Abs(vecLab[3]) < constants::math::Almost0) {
+      return false;
+    }
+    auto dxFin = xk - vecLab[0];
+    x += dxFin;
+    y += vecLab[4] / vecLab[3] * dxFin;
+    z += vecLab[5] / vecLab[3] * dxFin;
+  }
+
+  // Calculate the track parameters
+  auto linRef1 = linRef0;
+  t = 1.f / gpu::CAMath::Sqrt(vecLab[3] * vecLab[3] + vecLab[4] * vecLab[4]);
+  linRef1.setX(xk);
+  linRef1.setY(y);
+  linRef1.setZ(z);
+  linRef1.setSnp(snpRef1 = vecLab[4] * t); // reassign snpRef1
+  linRef1.setTgl(vecLab[5] * t);
+  linRef1.setQ2Pt(q * t / vecLab[6]);
+
+  // recalculate parameters of the transported ref track needed for transport of this:
+  cspRef1 = gpu::CAMath::Sqrt((1 - snpRef1) * (1 + snpRef1));
+  cspRef1Inv = value_t(1) / cspRef1;
+  cc = cspRef0 + cspRef1;
+  ccInv = value_t(1) / cc;
+  dy2dx = (snpRef0 + snpRef1) * ccInv;
+  double dxccInv = dx * ccInv, hh = dxccInv * cspRef1Inv * (1 + cspRef0 * cspRef1 + snpRef0 * snpRef1), jj = dx * (dy2dx - snpRef1 * cspRef1Inv);
+  double f02 = hh * cspRef0Inv;
+  double f04 = hh * dxccInv * kb;
+  double f24 = dx * kb;
+  double f12 = linRef0.getTgl() * (f02 * snpRef1 + jj);
+  double f13 = dx * (cspRef1 + snpRef1 * dy2dx); // dS
+  double f14 = linRef0.getTgl() * (f04 * snpRef1 + jj * f24);
+
+  // difference between the current and reference state
+  value_t diff[5];
+  for (int i = 0; i < 5; i++) {
+    diff[i] = this->getParam(i) - linRef0.getParam(i);
+  }
+  value_t snpUpd = snpRef1 + diff[kSnp] + f24 * diff[kQ2Pt];
+  if (gpu::CAMath::Abs(snpUpd) > constants::math::Almost1) {
+    return false;
+  }
+  this->setX(xk);
+  this->setY(linRef1.getY() + diff[kY] + f02 * diff[kSnp] + f04 * diff[kQ2Pt]);
+  this->setZ(linRef1.getZ() + diff[kZ] + f13 * diff[kTgl] + f14 * diff[kQ2Pt]);
+  this->setSnp(snpUpd);
+  this->setTgl(linRef1.getTgl() + diff[kTgl]);
+  this->setQ2Pt(linRef1.getQ2Pt() + diff[kQ2Pt]);
+
+  linRef0 = linRef1; // update reference track
+
+  // matrix transformed with Bz component only
+  value_t &c00 = mC[kSigY2], &c10 = mC[kSigZY], &c11 = mC[kSigZ2], &c20 = mC[kSigSnpY], &c21 = mC[kSigSnpZ],
+          &c22 = mC[kSigSnp2], &c30 = mC[kSigTglY], &c31 = mC[kSigTglZ], &c32 = mC[kSigTglSnp], &c33 = mC[kSigTgl2],
+          &c40 = mC[kSigQ2PtY], &c41 = mC[kSigQ2PtZ], &c42 = mC[kSigQ2PtSnp], &c43 = mC[kSigQ2PtTgl],
+          &c44 = mC[kSigQ2Pt2];
+
+  // b = C*ft
+  double b00 = f02 * c20 + f04 * c40, b01 = f12 * c20 + f14 * c40 + f13 * c30;
+  double b02 = f24 * c40;
+  double b10 = f02 * c21 + f04 * c41, b11 = f12 * c21 + f14 * c41 + f13 * c31;
+  double b12 = f24 * c41;
+  double b20 = f02 * c22 + f04 * c42, b21 = f12 * c22 + f14 * c42 + f13 * c32;
+  double b22 = f24 * c42;
+  double b40 = f02 * c42 + f04 * c44, b41 = f12 * c42 + f14 * c44 + f13 * c43;
+  double b42 = f24 * c44;
+  double b30 = f02 * c32 + f04 * c43, b31 = f12 * c32 + f14 * c43 + f13 * c33;
+  double b32 = f24 * c43;
+
+  // a = f*b = f*C*ft
+  double a00 = f02 * b20 + f04 * b40, a01 = f02 * b21 + f04 * b41, a02 = f02 * b22 + f04 * b42;
+  double a11 = f12 * b21 + f14 * b41 + f13 * b31, a12 = f12 * b22 + f14 * b42 + f13 * b32;
+  double a22 = f24 * b42;
+
+  // F*C*Ft = C + (b + bt + a)
+  c00 += b00 + b00 + a00;
+  c10 += b10 + b01 + a01;
+  c20 += b20 + b02 + a02;
+  c30 += b30;
+  c40 += b40;
+  c11 += b11 + b11 + a11;
+  c21 += b21 + b12 + a12;
+  c31 += b31;
+  c41 += b41;
+  c22 += b22 + b22 + a22;
+  c32 += b32;
+  c42 += b42;
+
+  checkCovariance();
 
   return true;
 }
@@ -603,7 +966,7 @@ GPUd() void TrackParametrizationWithError::checkCorrelations()
     for (int j = i; j--;) {
       auto sig2 = mC[DiagMap[i]] * mC[DiagMap[j]];
       auto& cov = mC[CovarMap[i][j]];
-      if (cov * cov >= MaxCorr) { // constrain correlation
+      if (cov * cov >= MaxCorr * sig2) { // constrain correlation
         cov = gpu::CAMath::Sqrt(sig2) * (cov > 0. ? MaxCorr : -MaxCorr);
       }
     }
@@ -721,19 +1084,45 @@ GPUd() auto TrackParametrizationWithError::getPredictedChi2(const value
   value_t z = this->getZ() - p[1];
   auto chi2 = (d * (szz * d - sdz * z) + z * (sdd * z - d * sdz)) / det;
   if (chi2 < 0.) {
-    LOGP(warning, "Negative chi2={}, Cluster: {} {} {} Dy:{} Dz:{} | sdd:{} sdz:{} szz:{} det:{}", chi2, cov[0], cov[1], cov[2], d, z, sdd, sdz, szz, det);
 #ifndef GPUCA_ALIGPUCODE
+    LOGP(warning, "Negative chi2={}, Cluster: {} {} {} Dy:{} Dz:{} | sdd:{} sdz:{} szz:{} det:{}", chi2, cov[0], cov[1], cov[2], d, z, sdd, sdz, szz, det);
     LOGP(warning, "Track: {}", asString());
 #endif
   }
   return chi2;
 }
 
-#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // Disable function relying on ROOT SMatrix on GPU
+//______________________________________________
+template 
+GPUd() auto TrackParametrizationWithError::getPredictedChi2Quiet(const value_t* p, const value_t* cov) const -> value_t
+{
+  // Estimate the chi2 of the space point "p" with the cov. matrix "cov"
+  auto sdd = static_cast(getSigmaY2()) + static_cast(cov[0]);
+  auto sdz = static_cast(getSigmaZY()) + static_cast(cov[1]);
+  auto szz = static_cast(getSigmaZ2()) + static_cast(cov[2]);
+  auto det = sdd * szz - sdz * sdz;
+
+  if (gpu::CAMath::Abs(det) < constants::math::Almost0) {
+    return constants::math::VeryBig;
+  }
+
+  value_t d = this->getY() - p[0];
+  value_t z = this->getZ() - p[1];
+
+  return (d * (szz * d - sdz * z) + z * (sdd * z - d * sdz)) / det;
+}
+
+//______________________________________________
+template 
+GPUd() auto TrackParametrizationWithError::getPredictedChi2(const TrackParametrizationWithError& rhs) const -> value_t
+{
+  MatrixDSym5 cov; // perform matrix operations in double!
+  return getPredictedChi2(rhs, cov);
+}
 
 //______________________________________________
 template 
-void TrackParametrizationWithError::buildCombinedCovMatrix(const TrackParametrizationWithError& rhs, MatrixDSym5& cov) const
+GPUd() void TrackParametrizationWithError::buildCombinedCovMatrix(const TrackParametrizationWithError& rhs, MatrixDSym5& cov) const
 {
   // fill combined cov.matrix (NOT inverted)
   cov(kY, kY) = static_cast(getSigmaY2()) + static_cast(rhs.getSigmaY2());
@@ -755,24 +1144,16 @@ void TrackParametrizationWithError::buildCombinedCovMatrix(const TrackP
 
 //______________________________________________
 template 
-auto TrackParametrizationWithError::getPredictedChi2(const TrackParametrizationWithError& rhs) const -> value_t
-{
-  MatrixDSym5 cov; // perform matrix operations in double!
-  return getPredictedChi2(rhs, cov);
-}
-
-//______________________________________________
-template 
-auto TrackParametrizationWithError::getPredictedChi2(const TrackParametrizationWithError& rhs, MatrixDSym5& covToSet) const -> value_t
+GPUd() auto TrackParametrizationWithError::getPredictedChi2(const TrackParametrizationWithError& rhs, MatrixDSym5& covToSet) const -> value_t
 {
   // get chi2 wrt other track, which must be defined at the same parameters X,alpha
   // Supplied non-initialized covToSet matrix is filled by inverse combined matrix for further use
 
-  if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > FLT_EPSILON) {
+  if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > o2::constants::math::Epsilon) {
     LOG(error) << "The reference Alpha of the tracks differ: " << this->getAlpha() << " : " << rhs.getAlpha();
     return 2.f * HugeF;
   }
-  if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > FLT_EPSILON) {
+  if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > o2::constants::math::Epsilon) {
     LOG(error) << "The reference X of the tracks differ: " << this->getX() << " : " << rhs.getX();
     return 2.f * HugeF;
   }
@@ -796,16 +1177,16 @@ auto TrackParametrizationWithError::getPredictedChi2(const TrackParamet
 
 //______________________________________________
 template 
-bool TrackParametrizationWithError::update(const TrackParametrizationWithError& rhs, const MatrixDSym5& covInv)
+GPUd() bool TrackParametrizationWithError::update(const TrackParametrizationWithError& rhs, const MatrixDSym5& covInv)
 {
   // update track with other track, the inverted combined cov matrix should be supplied
 
   // consider skipping this check, since it is usually already done upstream
-  if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > FLT_EPSILON) {
+  if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > o2::constants::math::Epsilon) {
     LOG(error) << "The reference Alpha of the tracks differ: " << this->getAlpha() << " : " << rhs.getAlpha();
     return false;
   }
-  if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > FLT_EPSILON) {
+  if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > o2::constants::math::Epsilon) {
     LOG(error) << "The reference X of the tracks differ: " << this->getX() << " : " << rhs.getX();
     return false;
   }
@@ -842,7 +1223,7 @@ bool TrackParametrizationWithError::update(const TrackParametrizationWi
   }
 
   // updated covariance: Cov0 = Cov0 - K*Cov0
-  matK *= ROOT::Math::SMatrix>(matC0);
+  matK *= o2::math_utils::SMatrix>(matC0);
   mC[kSigY2] -= matK(kY, kY);
   mC[kSigZY] -= matK(kZ, kY);
   mC[kSigZ2] -= matK(kZ, kZ);
@@ -864,7 +1245,7 @@ bool TrackParametrizationWithError::update(const TrackParametrizationWi
 
 //______________________________________________
 template 
-bool TrackParametrizationWithError::update(const TrackParametrizationWithError& rhs)
+GPUd() bool TrackParametrizationWithError::update(const TrackParametrizationWithError& rhs)
 {
   // update track with other track
   MatrixDSym5 covI; // perform matrix operations in double!
@@ -876,8 +1257,6 @@ bool TrackParametrizationWithError::update(const TrackParametrizationWi
   return update(rhs, covI);
 }
 
-#endif
-
 //______________________________________________
 template 
 GPUd() bool TrackParametrizationWithError::update(const value_t* p, const value_t* cov)
@@ -962,7 +1341,7 @@ GPUd() value_T TrackParametrizationWithError::update(const o2::dataform
 
 //______________________________________________
 template 
-GPUd() bool TrackParametrizationWithError::correctForMaterial(value_t x2x0, value_t xrho, bool anglecorr, value_t dedx)
+GPUd() bool TrackParametrizationWithError::correctForMaterial(value_t x2x0, value_t xrho, bool anglecorr)
 {
   //------------------------------------------------------------------
   // This function corrects the track parameters for the crossed material.
@@ -974,84 +1353,257 @@ GPUd() bool TrackParametrizationWithError::correctForMaterial(value_t x
   // "anglecorr" - switch for the angular correction
   //------------------------------------------------------------------
   constexpr value_t kMSConst2 = 0.0136f * 0.0136f;
-  constexpr value_t kMaxELossFrac = 0.3f; // max allowed fractional eloss
-  constexpr value_t kMinP = 0.01f;        // kill below this momentum
+  constexpr value_t kMinP = 0.01f; // kill below this momentum
 
-  value_t& fC22 = mC[kSigSnp2];
-  value_t& fC33 = mC[kSigTgl2];
-  value_t& fC43 = mC[kSigQ2PtTgl];
-  value_t& fC44 = mC[kSigQ2Pt2];
-  //
   value_t csp2 = (1.f - this->getSnp()) * (1.f + this->getSnp()); // cos(phi)^2
   value_t cst2I = (1.f + this->getTgl() * this->getTgl());        // 1/cos(lambda)^2
-  // Apply angle correction, if requested
-  if (anglecorr) {
+  if (anglecorr) {                                                // Apply angle correction, if requested
     value_t angle = gpu::CAMath::Sqrt(cst2I / csp2);
     x2x0 *= angle;
     xrho *= angle;
   }
-  value_t p = this->getP();
-  value_t p2 = p * p;
-  value_t e2 = p2 + this->getPID().getMass2();
-  value_t beta2 = p2 / e2;
+  auto m = this->getPID().getMass();
+  int charge2 = this->getAbsCharge() * this->getAbsCharge();
+  value_t p = this->getP(), p0 = p, p02 = p * p, e2 = p02 + this->getPID().getMass2(), massInv = 1. / m, bg = p * massInv, dETot = 0.;
+  value_t e = gpu::CAMath::Sqrt(e2), e0 = e;
+  if (m > 0 && xrho != 0.f) {
+    value_t ekin = e - m, dedx = this->getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+    value_t dedxDer = 0., dedx1 = dedx;
+#endif
+    if (charge2 != 1) {
+      dedx *= charge2;
+    }
+    value_t dE = dedx * xrho;
+    int na = 1 + int(gpu::CAMath::Abs(dE) / ekin * ELoss2EKinThreshInv);
+    if (na > MaxELossIter) {
+      na = MaxELossIter;
+    }
+    if (na > 1) {
+      dE /= na;
+      xrho /= na;
+#ifdef _BB_NONCONST_CORR_
+      dedxDer = this->getBetheBlochSolidDerivativeApprox(dedx1, bg); // require correction for non-constantness of dedx vs betagamma
+      if (charge2 != 1) {
+        dedxDer *= charge2;
+      }
+#endif
+    }
+    while (na--) {
+#ifdef _BB_NONCONST_CORR_
+      if (dedxDer != 0.) { // correction for non-constantness of dedx vs beta*gamma (in linear approximation): for a single step dE -> dE * [(exp(dedxDer) - 1)/dedxDer]
+        if (xrho < 0) {
+          dedxDer = -dedxDer; // E.loss ( -> positive derivative)
+        }
+        auto corrC = (gpu::CAMath::Exp(dedxDer) - 1.) / dedxDer;
+        dE *= corrC;
+      }
+#endif
+      e += dE;
+      if (e > m) { // stopped
+        p = gpu::CAMath::Sqrt(e * e - this->getPID().getMass2());
+      } else {
+        return false;
+      }
+      if (na) {
+        bg = p * massInv;
+        dedx = this->getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+        dedxDer = this->getBetheBlochSolidDerivativeApprox(dedx, bg);
+#endif
+        if (charge2 != 1) {
+          dedx *= charge2;
+#ifdef _BB_NONCONST_CORR_
+          dedxDer *= charge2;
+#endif
+        }
+        dE = dedx * xrho;
+      }
+    }
+
+    if (p < kMinP) {
+      return false;
+    }
+    dETot = e - e0;
+  } // end of e.loss correction
 
   // Calculating the multiple scattering corrections******************
+  value_t& fC22 = mC[kSigSnp2];
+  value_t& fC33 = mC[kSigTgl2];
+  value_t& fC43 = mC[kSigQ2PtTgl];
+  value_t& fC44 = mC[kSigQ2Pt2];
+  //
   value_t cC22(0.f), cC33(0.f), cC43(0.f), cC44(0.f);
   if (x2x0 != 0.f) {
-    value_t theta2 = kMSConst2 / (beta2 * p2) * gpu::CAMath::Abs(x2x0);
-    if (this->getAbsCharge() != 1) {
-      theta2 *= this->getAbsCharge() * this->getAbsCharge();
+    value_t beta2 = p02 / e2, theta2 = kMSConst2 / (beta2 * p02) * gpu::CAMath::Abs(x2x0);
+    value_t fp34 = this->getTgl();
+    if (charge2 != 1) {
+      theta2 *= charge2;
+      fp34 *= this->getCharge2Pt();
     }
     if (theta2 > constants::math::PI * constants::math::PI) {
       return false;
     }
-    value_t fp34 = this->getTgl() * this->getCharge2Pt();
     value_t t2c2I = theta2 * cst2I;
     cC22 = t2c2I * csp2;
     cC33 = t2c2I * cst2I;
     cC43 = t2c2I * fp34;
     cC44 = theta2 * fp34 * fp34;
-    // optimes this
+    // optimize this
     //    cC22 = theta2*((1.-getSnp())*(1.+getSnp()))*(1. + this->getTgl()*getTgl());
     //    cC33 = theta2*(1. + this->getTgl()*getTgl())*(1. + this->getTgl()*getTgl());
     //    cC43 = theta2*getTgl()*this->getQ2Pt()*(1. + this->getTgl()*getTgl());
     //    cC44 = theta2*getTgl()*this->getQ2Pt()*getTgl()*this->getQ2Pt();
   }
 
-  // Calculating the energy loss corrections************************
-  value_t cP4 = 1.f;
-  if ((xrho != 0.f) && (beta2 < 1.f)) {
-    if (dedx < kCalcdEdxAuto + constants::math::Almost1) { // request to calculate dedx on the fly
-      dedx = BetheBlochSolid(p / this->getPID().getMass());
-      if (this->getAbsCharge() != 1) {
-        dedx *= this->getAbsCharge() * this->getAbsCharge();
+  // the energy loss correction contribution to cov.matrix: approximate energy loss fluctuation (M.Ivanov)
+  constexpr value_t knst = 0.0007f; // To be tuned.
+  value_t sigmadE = knst * gpu::CAMath::Sqrt(gpu::CAMath::Abs(dETot)) * e0 / p02 * this->getCharge2Pt();
+  cC44 += sigmadE * sigmadE;
+
+  // Applying the corrections*****************************
+  fC22 += cC22;
+  fC33 += cC33;
+  fC43 += cC43;
+  fC44 += cC44;
+  this->setQ2Pt(this->getQ2Pt() * p0 / p);
+
+  checkCovariance();
+
+  return true;
+}
+
+//______________________________________________
+template 
+GPUd() bool TrackParametrizationWithError::correctForMaterial(TrackParametrization& linRef, value_t x2x0, value_t xrho, bool anglecorr)
+{
+  //------------------------------------------------------------------
+  // This function corrects the reference and current track parameters for the crossed material
+  // "x2x0"   - X/X0, the thickness in units of the radiation length.
+  // "xrho" - is the product length*density (g/cm^2).
+  //     It should be passed as negative when propagating tracks
+  //     from the intreaction point to the outside of the central barrel.
+  // "dedx" - mean enery loss (GeV/(g/cm^2), if <=kCalcdEdxAuto : calculate on the fly
+  // "anglecorr" - switch for the angular correction
+  //------------------------------------------------------------------
+  constexpr value_t kMSConst2 = 0.0136f * 0.0136f;
+  constexpr value_t kMinP = 0.01f; // kill below this momentum
+
+  value_t csp2 = (1.f - linRef.getSnp()) * (1.f + linRef.getSnp()); // cos(phi)^2
+  value_t cst2I = (1.f + linRef.getTgl() * linRef.getTgl());        // 1/cos(lambda)^2
+  if (anglecorr) {                                                  // Apply angle correction, if requested
+    value_t angle = gpu::CAMath::Sqrt(cst2I / csp2);
+    x2x0 *= angle;
+    xrho *= angle;
+  }
+  auto pid = linRef.getPID();
+  auto m = pid.getMass();
+  int charge2 = linRef.getAbsCharge() * linRef.getAbsCharge();
+  value_t p = linRef.getP(), p0 = p, p02 = p * p, e2 = p02 + pid.getMass2(), massInv = 1. / m, bg = p * massInv, dETot = 0.;
+  value_t e = gpu::CAMath::Sqrt(e2), e0 = e;
+  if (m > 0 && xrho != 0.f) {
+    value_t ekin = e - m, dedx = this->getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+    value_t dedxDer = 0., dedx1 = dedx;
+#endif
+    if (charge2 != 1) {
+      dedx *= charge2;
+    }
+    value_t dE = dedx * xrho;
+    int na = 1 + int(gpu::CAMath::Abs(dE) / ekin * ELoss2EKinThreshInv);
+    if (na > MaxELossIter) {
+      na = MaxELossIter;
+    }
+    if (na > 1) {
+      dE /= na;
+      xrho /= na;
+#ifdef _BB_NONCONST_CORR_
+      dedxDer = this->getBetheBlochSolidDerivativeApprox(dedx1, bg); // require correction for non-constantness of dedx vs betagamma
+      if (charge2 != 1) {
+        dedxDer *= charge2;
+      }
+#endif
+    }
+    while (na--) {
+#ifdef _BB_NONCONST_CORR_
+      if (dedxDer != 0.) { // correction for non-constantness of dedx vs beta*gamma (in linear approximation): for a single step dE -> dE * [(exp(dedxDer) - 1)/dedxDer]
+        if (xrho < 0) {
+          dedxDer = -dedxDer; // E.loss ( -> positive derivative)
+        }
+        auto corrC = (gpu::CAMath::Exp(dedxDer) - 1.) / dedxDer;
+        dE *= corrC;
+      }
+#endif
+      e += dE;
+      if (e > m) { // stopped
+        p = gpu::CAMath::Sqrt(e * e - pid.getMass2());
+      } else {
+        return false;
+      }
+      if (na) {
+        bg = p * massInv;
+        dedx = this->getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+        dedxDer = this->getBetheBlochSolidDerivativeApprox(dedx, bg);
+#endif
+        if (charge2 != 1) {
+          dedx *= charge2;
+#ifdef _BB_NONCONST_CORR_
+          dedxDer *= charge2;
+#endif
+        }
+        dE = dedx * xrho;
       }
     }
 
-    value_t dE = dedx * xrho;
-    value_t e = gpu::CAMath::Sqrt(e2);
-    if (gpu::CAMath::Abs(dE) > kMaxELossFrac * e) {
-      return false; // 30% energy loss is too much!
+    if (p < kMinP) {
+      return false;
     }
-    value_t eupd = e + dE;
-    value_t pupd2 = eupd * eupd - this->getPID().getMass2();
-    if (pupd2 < kMinP * kMinP) {
+    dETot = e - e0;
+  } // end of e.loss correction
+
+  // Calculating the multiple scattering corrections******************
+  value_t& fC22 = mC[kSigSnp2];
+  value_t& fC33 = mC[kSigTgl2];
+  value_t& fC43 = mC[kSigQ2PtTgl];
+  value_t& fC44 = mC[kSigQ2Pt2];
+  //
+  value_t cC22(0.f), cC33(0.f), cC43(0.f), cC44(0.f);
+  if (x2x0 != 0.f) {
+    value_t beta2 = p02 / e2, theta2 = kMSConst2 / (beta2 * p02) * gpu::CAMath::Abs(x2x0);
+    value_t fp34 = linRef.getTgl();
+    if (charge2 != 1) {
+      theta2 *= charge2;
+      fp34 *= linRef.getCharge2Pt();
+    }
+    if (theta2 > constants::math::PI * constants::math::PI) {
       return false;
     }
-    cP4 = p / gpu::CAMath::Sqrt(pupd2);
-    //
-    // Approximate energy loss fluctuation (M.Ivanov)
-    constexpr value_t knst = 0.07f; // To be tuned.
-    value_t sigmadE = knst * gpu::CAMath::Sqrt(gpu::CAMath::Abs(dE)) * e / p2 * this->getCharge2Pt();
-    cC44 += sigmadE * sigmadE;
+    value_t t2c2I = theta2 * cst2I;
+    cC22 = t2c2I * csp2;
+    cC33 = t2c2I * cst2I;
+    cC43 = t2c2I * fp34;
+    cC44 = theta2 * fp34 * fp34;
+    // optimize this
+    //    cC22 = theta2*((1.-getSnp())*(1.+getSnp()))*(1. + this->getTgl()*getTgl());
+    //    cC33 = theta2*(1. + this->getTgl()*getTgl())*(1. + this->getTgl()*getTgl());
+    //    cC43 = theta2*getTgl()*this->getQ2Pt()*(1. + this->getTgl()*getTgl());
+    //    cC44 = theta2*getTgl()*this->getQ2Pt()*getTgl()*this->getQ2Pt();
   }
 
+  // the energy loss correction contribution to cov.matrix: approximate energy loss fluctuation (M.Ivanov)
+  constexpr value_t knst = 0.0007f; // To be tuned.
+  value_t sigmadE = knst * gpu::CAMath::Sqrt(gpu::CAMath::Abs(dETot)) * e0 / p02 * linRef.getCharge2Pt();
+  cC44 += sigmadE * sigmadE;
+
   // Applying the corrections*****************************
   fC22 += cC22;
   fC33 += cC33;
   fC43 += cC43;
   fC44 += cC44;
-  this->setQ2Pt(this->getQ2Pt() * cP4);
+  auto pscale = p0 / p;
+  linRef.setQ2Pt(linRef.getQ2Pt() * pscale);
+  this->setQ2Pt(this->getQ2Pt() * pscale);
 
   checkCovariance();
 
@@ -1060,7 +1612,7 @@ GPUd() bool TrackParametrizationWithError::correctForMaterial(value_t x
 
 //______________________________________________________________
 template 
-GPUd() bool TrackParametrizationWithError::getCovXYZPxPyPzGlo(gpu::gpustd::array& cv) const
+GPUd() bool TrackParametrizationWithError::getCovXYZPxPyPzGlo(std::array& cv) const
 {
   //---------------------------------------------------------------------
   // This function returns the global covariance matrix of the track params
@@ -1081,42 +1633,52 @@ GPUd() bool TrackParametrizationWithError::getCovXYZPxPyPzGlo(gpu::gpus
     return false;
   }
 
-  auto pt = this->getPt();
-  value_t sn, cs;
+  const value_t pt = this->getPt();
+  const value_t q2pt = this->getQ2Pt();
+  value_t sn = 0.f, cs = 0.f;
   o2::math_utils::detail::sincos(this->getAlpha(), sn, cs);
-  auto r = gpu::CAMath::Sqrt((1. - this->getSnp()) * (1. + this->getSnp()));
-  auto m00 = -sn, m10 = cs;
-  auto m23 = -pt * (sn + this->getSnp() * cs / r), m43 = -pt * pt * (r * cs - this->getSnp() * sn);
-  auto m24 = pt * (cs - this->getSnp() * sn / r), m44 = -pt * pt * (r * sn + this->getSnp() * cs);
-  auto m35 = pt, m45 = -pt * pt * this->getTgl();
-
-  if (this->getSign() < 0) {
-    m43 = -m43;
-    m44 = -m44;
-    m45 = -m45;
-  }
-
-  cv[0] = mC[0] * m00 * m00;
-  cv[1] = mC[0] * m00 * m10;
-  cv[2] = mC[0] * m10 * m10;
-  cv[3] = mC[1] * m00;
-  cv[4] = mC[1] * m10;
-  cv[5] = mC[2];
-  cv[6] = m00 * (mC[3] * m23 + mC[10] * m43);
-  cv[7] = m10 * (mC[3] * m23 + mC[10] * m43);
-  cv[8] = mC[4] * m23 + mC[11] * m43;
-  cv[9] = m23 * (mC[5] * m23 + mC[12] * m43) + m43 * (mC[12] * m23 + mC[14] * m43);
-  cv[10] = m00 * (mC[3] * m24 + mC[10] * m44);
-  cv[11] = m10 * (mC[3] * m24 + mC[10] * m44);
-  cv[12] = mC[4] * m24 + mC[11] * m44;
-  cv[13] = m23 * (mC[5] * m24 + mC[12] * m44) + m43 * (mC[12] * m24 + mC[14] * m44);
-  cv[14] = m24 * (mC[5] * m24 + mC[12] * m44) + m44 * (mC[12] * m24 + mC[14] * m44);
-  cv[15] = m00 * (mC[6] * m35 + mC[10] * m45);
-  cv[16] = m10 * (mC[6] * m35 + mC[10] * m45);
-  cv[17] = mC[7] * m35 + mC[11] * m45;
-  cv[18] = m23 * (mC[8] * m35 + mC[12] * m45) + m43 * (mC[13] * m35 + mC[14] * m45);
-  cv[19] = m24 * (mC[8] * m35 + mC[12] * m45) + m44 * (mC[13] * m35 + mC[14] * m45);
-  cv[20] = m35 * (mC[9] * m35 + mC[13] * m45) + m45 * (mC[13] * m35 + mC[14] * m45);
+  const value_t snp = this->getSnp();
+  const value_t csp = gpu::CAMath::Sqrt((1.f - snp) * (1.f + snp));
+  const value_t pXLoc = pt * csp;
+  const value_t pYLoc = pt * snp;
+  const value_t pZ = pt * this->getTgl();
+  const value_t pX = cs * pXLoc - sn * pYLoc;
+  const value_t pY = sn * pXLoc + cs * pYLoc;
+
+  value_t cTr[5][5] = {};
+  for (int i = 0; i < kNParams; ++i) {
+    for (int j = 0; j <= i; ++j) {
+      cTr[i][j] = cTr[j][i] = mC[CovarMap[i][j]];
+    }
+  }
+
+  double jac[6][5] = {};
+  jac[0][kY] = -sn;
+  jac[1][kY] = cs;
+  jac[2][kZ] = 1.f;
+
+  const value_t dPxDSnp = -pt * (cs * snp / csp + sn);
+  const value_t dPyDSnp = pt * (cs - sn * snp / csp);
+  jac[3][kSnp] = dPxDSnp;
+  jac[4][kSnp] = dPyDSnp;
+  jac[5][kTgl] = pt;
+
+  jac[3][kQ2Pt] = -pX / q2pt;
+  jac[4][kQ2Pt] = -pY / q2pt;
+  jac[5][kQ2Pt] = -pZ / q2pt;
+
+  int idx = 0;
+  for (int i = 0; i < 6; ++i) {
+    for (int j = 0; j <= i; ++j) {
+      double cij = 0.f;
+      for (int k = 0; k < kNParams; ++k) {
+        for (int l = 0; l < kNParams; ++l) {
+          cij += jac[i][k] * cTr[k][l] * jac[j][l];
+        }
+      }
+      cv[idx++] = cij;
+    }
+  }
 
   return true;
 }
@@ -1127,15 +1689,22 @@ template 
 std::string TrackParametrizationWithError::asString() const
 {
   return TrackParametrization::asString() +
-         fmt::format(
-           "\n{:7s} {:+.3e}\n"
-           "{:7s} {:+.3e} {:+.3e}\n"
-           "{:7s} {:+.3e} {:+.3e} {:+.3e}\n"
-           "{:7s} {:+.3e} {:+.3e} {:+.3e} {:+.3e}\n"
-           "{:7s} {:+.3e} {:+.3e} {:+.3e} {:+.3e} {:+.3e}",
-           "CovMat:", mC[kSigY2], "", mC[kSigZY], mC[kSigZ2], "", mC[kSigSnpY], mC[kSigSnpZ], mC[kSigSnp2], "", mC[kSigTglY],
-           mC[kSigTglZ], mC[kSigTglSnp], mC[kSigTgl2], "", mC[kSigQ2PtY], mC[kSigQ2PtZ], mC[kSigQ2PtSnp], mC[kSigQ2PtTgl],
-           mC[kSigQ2Pt2]);
+         fmt::format(" Cov: [{:+.3e}] [{:+.3e} {:+.3e}] [{:+.3e} {:+.3e} {:+.3e}] [{:+.3e} {:+.3e} {:+.3e} {:+.3e}] [{:+.3e} {:+.3e} {:+.3e} {:+.3e} {:+.3e}]",
+                     mC[kSigY2], mC[kSigZY], mC[kSigZ2], mC[kSigSnpY], mC[kSigSnpZ], mC[kSigSnp2], mC[kSigTglY],
+                     mC[kSigTglZ], mC[kSigTglSnp], mC[kSigTgl2], mC[kSigQ2PtY], mC[kSigQ2PtZ], mC[kSigQ2PtSnp], mC[kSigQ2PtTgl],
+                     mC[kSigQ2Pt2]);
+}
+
+template 
+std::string TrackParametrizationWithError::asStringHexadecimal()
+{
+  return TrackParametrization::asStringHexadecimal() +
+         fmt::format(" Cov: [{:x}] [{:x} {:x}] [{:x} {:x} {:x}] [{:x} {:x} {:x} {:x}] [{:x} {:x} {:x} {:x} {:x}]",
+                     reinterpret_cast(mC[kSigY2]), reinterpret_cast(mC[kSigZY]), reinterpret_cast(mC[kSigZ2]),
+                     reinterpret_cast(mC[kSigSnpY]), reinterpret_cast(mC[kSigSnpZ]), reinterpret_cast(mC[kSigSnp2]),
+                     reinterpret_cast(mC[kSigTglY]), reinterpret_cast(mC[kSigTglZ]), reinterpret_cast(mC[kSigTglSnp]),
+                     reinterpret_cast(mC[kSigTgl2]), reinterpret_cast(mC[kSigQ2PtY]), reinterpret_cast(mC[kSigQ2PtZ]),
+                     reinterpret_cast(mC[kSigQ2PtSnp]), reinterpret_cast(mC[kSigQ2PtTgl]), reinterpret_cast(mC[kSigQ2Pt2]));
 }
 #endif
 
@@ -1146,24 +1715,99 @@ GPUd() void TrackParametrizationWithError::print() const
   // print parameters
 #ifndef GPUCA_ALIGPUCODE
   printf("%s\n", asString().c_str());
-#else
+#elif !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT))
   TrackParametrization::printParam();
   printf(
-    "\n%7s %+.3e\n"
-    "%7s %+.3e %+.3e\n"
-    "%7s %+.3e %+.3e %+.3e\n"
-    "%7s %+.3e %+.3e %+.3e %+.3e\n"
-    "%7s %+.3e %+.3e %+.3e %+.3e %+.3e",
-    "CovMat:", mC[kSigY2], "", mC[kSigZY], mC[kSigZ2], "", mC[kSigSnpY], mC[kSigSnpZ], mC[kSigSnp2], "", mC[kSigTglY],
-    mC[kSigTglZ], mC[kSigTglSnp], mC[kSigTgl2], "", mC[kSigQ2PtY], mC[kSigQ2PtZ], mC[kSigQ2PtSnp], mC[kSigQ2PtTgl],
+    " Cov: [%+.3e] [%+.3e %+.3e] [%+.3e %+.3e %+.3e] [%+.3e %+.3e %+.3e %+.3e] [%+.3e %+.3e %+.3e %+.3e %+.3e]\n",
+    mC[kSigY2], mC[kSigZY], mC[kSigZ2], mC[kSigSnpY], mC[kSigSnpZ], mC[kSigSnp2], mC[kSigTglY],
+    mC[kSigTglZ], mC[kSigTglSnp], mC[kSigTgl2], mC[kSigQ2PtY], mC[kSigQ2PtZ], mC[kSigQ2PtSnp], mC[kSigQ2PtTgl],
     mC[kSigQ2Pt2]);
 #endif
 }
 
+//______________________________________________________________
+template 
+GPUd() void TrackParametrizationWithError::printHexadecimal()
+{
+  // print parameters
+#ifndef GPUCA_ALIGPUCODE
+  printf("%s\n", asStringHexadecimal().c_str());
+#elif !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT))
+  TrackParametrization::printParamHexadecimal();
+  printf(
+    " Cov: [%x] [%x %x] [%x %x %x] [%x %x %x %x] [%x %x %x %x %x]\n",
+    gpu::CAMath::Float2UIntReint(mC[kSigY2]),
+    gpu::CAMath::Float2UIntReint(mC[kSigZY]), gpu::CAMath::Float2UIntReint(mC[kSigZ2]),
+    gpu::CAMath::Float2UIntReint(mC[kSigSnpY]), gpu::CAMath::Float2UIntReint(mC[kSigSnpZ]), gpu::CAMath::Float2UIntReint(mC[kSigSnp2]),
+    gpu::CAMath::Float2UIntReint(mC[kSigTglY]), gpu::CAMath::Float2UIntReint(mC[kSigTglZ]), gpu::CAMath::Float2UIntReint(mC[kSigTglSnp]), gpu::CAMath::Float2UIntReint(mC[kSigTgl2]),
+    gpu::CAMath::Float2UIntReint(mC[kSigQ2PtY]), gpu::CAMath::Float2UIntReint(mC[kSigQ2PtZ]), gpu::CAMath::Float2UIntReint(mC[kSigQ2PtSnp]), gpu::CAMath::Float2UIntReint(mC[kSigQ2PtTgl]), gpu::CAMath::Float2UIntReint(mC[kSigQ2Pt2]));
+#endif
+}
+
+#ifndef GPUCA_ALIGPUCODE
+//______________________________________________________________
+template 
+bool TrackParametrizationWithError::toFwdTrackParCov(TrackParCovFwd& t) const
+{
+  auto p = this->getXYZGlo();
+  t.setZ(p.Z());
+  t.setX(p.X());
+  t.setY(p.Y());
+  t.setPhi(this->getPhi());
+  t.setTanl(this->getTgl());
+  t.setInvQPt(this->getQ2Pt());
+  //
+  if (gpu::CAMath::Abs(this->getSnp()) >= o2::constants::math::Almost1 ||
+      gpu::CAMath::Abs(this->getTgl()) <= o2::constants::math::Almost0) {
+    return false;
+  }
+  value_T csa, sna, csP, snP, csp = gpu::CAMath::Sqrt((1. - this->getSnp()) * (1. + this->getSnp()));
+  math_utils::detail::sincos(value_T(this->getAlpha()), sna, csa);
+  math_utils::detail::sincos(value_T(t.getPhi()), snP, csP);
+  /*
+    Jacobian is
+    /-sna  -csP/tgL 0     0 0 \
+    | csa  -snP/tgL 0     0 0 |
+    | 0     0       1/csp 0 0 |
+    | 0     0       0     1 0 |
+    \ 0     0       0     0 1 /
+  */
+  auto tgLI = 1 / this->getTgl();
+  const value_T d1 = -sna;
+  const value_T d2 = -csP * tgLI;
+  const value_T e1 = csa;
+  const value_T e2 = -snP * tgLI;
+  const value_T f1 = 1 / csp;
+  SMatrix55Sym C;
+  C(0, 0) = d1 * d1 * getSigmaY2() + 2 * d1 * d2 * getSigmaZY() + d2 * d2 * getSigmaZ2();
+  C(0, 1) = d1 * e1 * getSigmaY2() + (d1 * e2 + d2 * e1) * getSigmaZY() + d2 * e2 * getSigmaZ2();
+  C(1, 1) = e1 * e1 * getSigmaY2() + 2 * e1 * e2 * getSigmaZY() + e2 * e2 * getSigmaZ2();
+
+  C(0, 2) = f1 * (d1 * getSigmaSnpY() + d2 * getSigmaSnpZ());
+  C(1, 2) = f1 * (e1 * getSigmaSnpY() + e2 * getSigmaSnpZ());
+  C(2, 2) = f1 * f1 * getSigmaSnp2();
+
+  C(0, 3) = d1 * getSigmaTglY() + d2 * getSigmaTglZ();
+  C(1, 3) = e1 * getSigmaTglY() + e2 * getSigmaTglZ();
+  C(2, 3) = f1 * getSigmaTglSnp();
+  C(3, 3) = getSigmaTgl2();
+
+  C(0, 4) = d1 * getSigma1PtY() + d2 * getSigma1PtZ();
+  C(1, 4) = e1 * getSigma1PtY() + e2 * getSigma1PtZ();
+  C(2, 4) = f1 * getSigma1PtSnp();
+  C(3, 4) = getSigma1PtTgl();
+  C(4, 4) = getSigma1Pt2();
+  t.setCovariances(C);
+  return true;
+}
+#endif
+
 namespace o2::track
 {
+#if !defined(GPUCA_GPUCODE) || defined(GPUCA_GPUCODE_DEVICE) // FIXME: DR: WORKAROUND to avoid CUDA bug creating host symbols for device code.
 template class TrackParametrizationWithError;
-#ifndef GPUCA_GPUCODE_DEVICE
+#endif
+#ifndef GPUCA_GPUCODE
 template class TrackParametrizationWithError;
 #endif
 } // namespace o2::track
diff --git a/DataFormats/Reconstruction/src/V0.cxx b/DataFormats/Reconstruction/src/V0.cxx
index ad2c84f499bf8..986d52fed35f6 100644
--- a/DataFormats/Reconstruction/src/V0.cxx
+++ b/DataFormats/Reconstruction/src/V0.cxx
@@ -14,20 +14,18 @@
 using namespace o2::dataformats;
 
 V0::V0(const std::array& xyz, const std::array& pxyz, const std::array& covxyz,
-       const o2::track::TrackParCov& trPos, const o2::track::TrackParCov& trNeg,
-       GIndex trPosID, GIndex trNegID, o2::track::PID pid)
-  : mProngIDs{trPosID, trNegID}, mProngs{trPos, trNeg}
+       const o2::track::TrackParCov& trPos, const o2::track::TrackParCov& trNeg, o2::track::PID pid) : mProngs{trPos, trNeg}
 {
-  std::array covV{}, covN{};
-  trPos.getCovXYZPxPyPzGlo(covV);
+  std::array covV{0.}, covP, covN;
+  trPos.getCovXYZPxPyPzGlo(covP);
   trNeg.getCovXYZPxPyPzGlo(covN);
-  for (int i = 0; i < 21; i++) {
-    covV[i] += covN[i];
-  }
+  constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component
   for (int i = 0; i < 6; i++) {
     covV[i] = covxyz[i];
+    covV[MomInd[i]] = covP[MomInd[i]] + covN[MomInd[i]];
   }
   this->set(xyz, pxyz, covV, trPos.getCharge() + trNeg.getCharge(), true, pid);
+  this->checkCorrelations();
 }
 
 float V0::calcMass2(float massPos2, float massNeg2) const
@@ -37,3 +35,36 @@ float V0::calcMass2(float massPos2, float massNeg2) const
   auto energy = std::sqrt(massPos2 + p2pos) + std::sqrt(massNeg2 + p2neg);
   return energy * energy - p2;
 }
+
+float V0::calcAPAlpha() const
+{
+  // calculate Armenteros-Podolanski alpha
+  std::array pP, pN, pV0;
+  float alp = 0.f, pV0tot2 = 0.f;
+  getProng(0).getPxPyPzGlo(pP);
+  getProng(1).getPxPyPzGlo(pN);
+  for (int i = 0; i < 3; i++) {
+    pV0[i] = pP[i] + pN[i];
+    alp += pV0[i] * (pP[i] - pN[i]);
+    pV0tot2 += pV0[i] * pV0[i];
+  }
+  alp /= pV0tot2;
+  return alp;
+}
+
+float V0::calcAPQt() const
+{
+  // calculate Armenteros-Podolanski qt
+  std::array pP, pN, pV0;
+  float pPtot2 = 0.f, pV0tot2 = 0.f, cross = 0.f;
+  getProng(0).getPxPyPzGlo(pP);
+  getProng(1).getPxPyPzGlo(pN);
+  for (int i = 0; i < 3; i++) {
+    pV0[i] = pP[i] + pN[i];
+    pPtot2 += pP[i] * pP[i];
+    pV0tot2 += pV0[i] * pV0[i];
+    cross += pV0[i] * pP[i]; // -> pP * pV0 * cos
+  }
+  float qt = pPtot2 - (cross * cross) / pV0tot2;
+  return qt > 0 ? std::sqrt(qt) : 0;
+}
diff --git a/DataFormats/Reconstruction/src/Vertex.cxx b/DataFormats/Reconstruction/src/Vertex.cxx
index dfbc5eac281e8..9294aeb655803 100644
--- a/DataFormats/Reconstruction/src/Vertex.cxx
+++ b/DataFormats/Reconstruction/src/Vertex.cxx
@@ -10,9 +10,9 @@
 // or submit itself to any jurisdiction.
 
 #include "ReconstructionDataFormats/Vertex.h"
-#include 
 #ifndef GPUCA_NO_FMT
-#include 
+#include 
+#include 
 #endif
 
 namespace o2
@@ -20,8 +20,6 @@ namespace o2
 namespace dataformats
 {
 
-#ifndef GPUCA_GPUCODE_DEVICE
-#ifndef GPUCA_NO_FMT
 std::string VertexBase::asString() const
 {
   return fmt::format("Vtx {{{:+.4e},{:+.4e},{:+.4e}}} Cov.:{{{{{:.3e}..}},{{{:.3e},{:.3e}..}},{{{:.3e},{:.3e},{:.3e}}}}}",
@@ -39,7 +37,6 @@ void VertexBase::print() const
 {
   std::cout << *this << std::endl;
 }
-#endif
 
 bool VertexBase::operator==(const VertexBase& other) const
 {
@@ -54,7 +51,8 @@ bool VertexBase::operator==(const VertexBase& other) const
   return true;
 }
 
-#endif
+template class o2::dataformats::Vertex>;
+template class o2::dataformats::Vertex>;
 
 } // namespace dataformats
 } // namespace o2
diff --git a/DataFormats/Reconstruction/src/VtxTrackRef.cxx b/DataFormats/Reconstruction/src/VtxTrackRef.cxx
index a3100b1933521..7bda5ebabd6ca 100644
--- a/DataFormats/Reconstruction/src/VtxTrackRef.cxx
+++ b/DataFormats/Reconstruction/src/VtxTrackRef.cxx
@@ -31,6 +31,9 @@ std::string VtxTrackRef::asString(bool skipEmpty) const
       str += fmt::format(", N{:s} : {:d}", VtxTrackIndex::getSourceName(i), getEntriesOfSource(i));
     }
   }
+  if (mITSGloContributors.getEntries()) {
+    str += fmt::format("| ITScontributors: {} from {}", mITSGloContributors.getEntries(), mITSGloContributors.getFirstEntry());
+  }
   return str;
 }
 
diff --git a/DataFormats/Reconstruction/test/testLTOFIntegration.cxx b/DataFormats/Reconstruction/test/testLTOFIntegration.cxx
index bb65c60d08d18..f737b1df53666 100644
--- a/DataFormats/Reconstruction/test/testLTOFIntegration.cxx
+++ b/DataFormats/Reconstruction/test/testLTOFIntegration.cxx
@@ -33,8 +33,8 @@ BOOST_AUTO_TEST_CASE(TrackLTIntegral)
   const int nStep = 100;
   const float dx2x0 = 0.01f;
   for (int i = 0; i < nStep; i++) {
-    lt.addStep(1., trc.getP2Inv());
-    lt1.addStep(1., trc1.getP2Inv());
+    lt.addStep(1., trc.getQ2P2());
+    lt1.addStep(1., trc1.getQ2P2());
     lt1.addX2X0(dx2x0);
   }
   trc.printParam();
diff --git a/DataFormats/common/CMakeLists.txt b/DataFormats/common/CMakeLists.txt
index 0e0e4076eb000..6b0081a920c8d 100644
--- a/DataFormats/common/CMakeLists.txt
+++ b/DataFormats/common/CMakeLists.txt
@@ -16,6 +16,7 @@ o2_add_library(CommonDataFormat
                        src/FlatHisto2D.cxx
                        src/AbstractRefAccessor.cxx
                        src/TFIDInfo.cxx
+                       src/TimeStamp.cxx
                PUBLIC_LINK_LIBRARIES O2::CommonConstants O2::GPUCommon
                                      ROOT::Core O2::MathUtils Microsoft.GSL::GSL)
 
diff --git a/DataFormats/common/include/CommonDataFormat/AbstractRef.h b/DataFormats/common/include/CommonDataFormat/AbstractRef.h
index cdc65f6c412d6..72c195cfb7bc8 100644
--- a/DataFormats/common/include/CommonDataFormat/AbstractRef.h
+++ b/DataFormats/common/include/CommonDataFormat/AbstractRef.h
@@ -18,11 +18,12 @@
 
 #include "GPUCommonDef.h"
 #include "GPUCommonRtypes.h"
-#include "GPUCommonTypeTraits.h"
+#ifndef GPUCA_GPUCODE_DEVICE
+#include 
+#endif
 
-namespace o2
-{
-namespace dataformats
+
+namespace o2::dataformats
 {
 
 template 
@@ -32,8 +33,19 @@ class AbstractRef
   static constexpr auto MVAR()
   {
     static_assert(NBIT <= 64, "> 64 bits not supported");
-    typename std::conditional<(NBIT > 32), uint64_t, typename std::conditional<(NBIT > 16), uint32_t, typename std::conditional<(NBIT > 8), uint16_t, uint8_t>::type>::type>::type tp = 0;
-    return tp;
+    if constexpr (NBIT > 32) {
+      uint64_t tp = 0;
+      return tp;
+    } else if constexpr (NBIT > 16) {
+      uint32_t tp = 0;
+      return tp;
+    } else if constexpr (NBIT > 8) {
+      uint16_t tp = 0;
+      return tp;
+    } else {
+      uint8_t tp = 0;
+      return tp;
+    }
   }
 
  public:
@@ -79,6 +91,7 @@ class AbstractRef
 
   GPUdi() bool operator==(const AbstractRef& o) const { return getRawWOFlags() == o.getRawWOFlags(); }
   GPUdi() bool operator!=(const AbstractRef& o) const { return !operator==(o); }
+  GPUdi() void clear() { setRaw((Base_t(SrcMask & ((0x1 << NBSrc) - 1)) << NBIdx) | Base_t(IdxMask & ((0x1 << NBIdx) - 1))); }
 
  protected:
   Base_t mRef = IdxMask | (SrcMask << NBIdx); // packed reference, dummy by default
@@ -86,7 +99,6 @@ class AbstractRef
   ClassDefNV(AbstractRef, 1);
 };
 
-} // namespace dataformats
-} // namespace o2
+} // namespace o2::dataformats
 
 #endif
diff --git a/DataFormats/common/include/CommonDataFormat/BunchFilling.h b/DataFormats/common/include/CommonDataFormat/BunchFilling.h
index 182a665532668..f11ce2498d04b 100644
--- a/DataFormats/common/include/CommonDataFormat/BunchFilling.h
+++ b/DataFormats/common/include/CommonDataFormat/BunchFilling.h
@@ -107,7 +107,6 @@ class BunchFilling
 
   ClassDefNV(BunchFilling, 2);
 };
-} // namespace o2
 
 namespace framework
 {
@@ -118,5 +117,6 @@ struct is_messageable : std::true_type {
 };
 
 } // namespace framework
+} // namespace o2
 
 #endif
diff --git a/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h b/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h
index ac91eafc4d147..f8d49042dd8c5 100644
--- a/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h
+++ b/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h
@@ -34,7 +34,7 @@ namespace dataformats
   Fast 1D histo class which can be messages as
   FlatHisto1D histo(nbins, xmin, xmax);
   histo.fill(...);
-  pc.outputs().snapshot(Output{"Origin", "Desc", 0, Lifetime::Timeframe}, histo.getBase());
+  pc.outputs().snapshot(Output{"Origin", "Desc", 0}, histo.getBase());
 
   and received (read only!) as
   const auto hdata = pc.inputs().get>("histodata");
diff --git a/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h b/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h
index 9102d53edeaaa..47793618c0649 100644
--- a/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h
+++ b/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h
@@ -35,7 +35,7 @@ namespace dataformats
   Fast 2D histo class which can be messages as
   FlatHisto2D histo(nbinsX, xmin, xmax, nbinsY, ymin, ymax);
   histo.fill(...);
-  pc.outputs().snapshot(Output{"Origin", "Desc", 0, Lifetime::Timeframe}, histo.getBase());
+  pc.outputs().snapshot(Output{"Origin", "Desc", 0}, histo.getBase());
 
   and received (read only!) as
   const auto hdata = pc.inputs().get>("histodata");
diff --git a/DataFormats/common/include/CommonDataFormat/IRFrame.h b/DataFormats/common/include/CommonDataFormat/IRFrame.h
index 26b1e33f17e50..d404a9538306a 100644
--- a/DataFormats/common/include/CommonDataFormat/IRFrame.h
+++ b/DataFormats/common/include/CommonDataFormat/IRFrame.h
@@ -28,9 +28,22 @@ namespace dataformats
 // We could just alias it to the bracket specialization, but this would create
 // problems with fwd.declaration
 struct IRFrame : public o2::math_utils::detail::Bracket {
+  static constexpr uint64_t LastIRFrame = uint64_t(0x1) << 63;
+
   using o2::math_utils::detail::Bracket::Bracket;
 
-  uint64_t info = 0;
+  uint64_t info = uint64_t(0);
+
+  void setLast(bool v = true)
+  {
+    if (v) {
+      info |= LastIRFrame;
+    } else {
+      v &= ~LastIRFrame;
+    }
+  }
+
+  bool isLast() const { return (info & LastIRFrame) != 0UL; }
 
   ClassDefNV(IRFrame, 2);
 };
diff --git a/DataFormats/common/include/CommonDataFormat/InteractionRecord.h b/DataFormats/common/include/CommonDataFormat/InteractionRecord.h
index c96b7745079ac..c22b24ad03aac 100644
--- a/DataFormats/common/include/CommonDataFormat/InteractionRecord.h
+++ b/DataFormats/common/include/CommonDataFormat/InteractionRecord.h
@@ -15,7 +15,7 @@
 #define ALICEO2_INTERACTIONRECORD_H
 
 #include "GPUCommonRtypes.h"
-#ifndef GPUCA_ALIGPUCODE
+#ifndef GPUCA_GPUCODE_DEVICE
 #include 
 #include 
 #endif
@@ -71,9 +71,9 @@ struct InteractionRecord {
 
   static int ns2bc(double ns, unsigned int& orb)
   {
-    orb = ns > 0 ? ns / o2::constants::lhc::LHCOrbitNS : 0;
-    ns -= orb * o2::constants::lhc::LHCOrbitNS;
-    return std::round(ns / o2::constants::lhc::LHCBunchSpacingNS);
+    long nb = std::round(ns / o2::constants::lhc::LHCBunchSpacingNS);
+    orb = nb / o2::constants::lhc::LHCMaxBunches;
+    return nb % o2::constants::lhc::LHCMaxBunches;
   }
 
   double bc2ns() const
@@ -109,7 +109,7 @@ struct InteractionRecord {
 
   float differenceInBCMUS(const InteractionRecord& other) const
   {
-    // return difference in bunch-crossings in ms
+    // return difference in bunch-crossings in us
     return differenceInBC(other) * o2::constants::lhc::LHCBunchSpacingMUS;
   }
 
@@ -281,7 +281,7 @@ struct InteractionRecord {
     return tmp;
   }
 
-#ifndef GPUCA_ALIGPUCODE
+#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE)
   void print() const;
   std::string asString() const;
   friend std::ostream& operator<<(std::ostream& stream, InteractionRecord const& ir);
@@ -359,7 +359,7 @@ struct InteractionTimeRecord : public InteractionRecord {
     return !((*this) > other);
   }
 
-#ifndef GPUCA_ALIGPUCODE
+#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE)
   void print() const;
   std::string asString() const;
   friend std::ostream& operator<<(std::ostream& stream, InteractionTimeRecord const& ir);
diff --git a/DataFormats/common/include/CommonDataFormat/RangeReference.h b/DataFormats/common/include/CommonDataFormat/RangeReference.h
index 0308d3b8af937..3d0c58298de03 100644
--- a/DataFormats/common/include/CommonDataFormat/RangeReference.h
+++ b/DataFormats/common/include/CommonDataFormat/RangeReference.h
@@ -29,23 +29,23 @@ template 
 class RangeReference
 {
  public:
-  GPUd() RangeReference(FirstEntry ent, NElem n) { set(ent, n); }
-  GPUdDefault() RangeReference(const RangeReference& src) = default;
-  GPUdDefault() RangeReference() = default;
-  GPUdDefault() ~RangeReference() = default;
-  GPUd() void set(FirstEntry ent, NElem n)
+  GPUhd() RangeReference(FirstEntry ent, NElem n) { set(ent, n); }
+  GPUhdDefault() RangeReference(const RangeReference& src) = default;
+  GPUhdDefault() RangeReference() = default;
+  GPUhdDefault() ~RangeReference() = default;
+  GPUhd() void set(FirstEntry ent, NElem n)
   {
     mFirstEntry = ent;
     mEntries = n;
   }
-  GPUd() void clear() { set(0, 0); }
-  GPUd() FirstEntry getFirstEntry() const { return mFirstEntry; }
-  GPUd() FirstEntry getEntriesBound() const { return mFirstEntry + mEntries; }
-  GPUd() NElem getEntries() const { return mEntries; }
-  GPUd() void setFirstEntry(FirstEntry ent) { mFirstEntry = ent; }
-  GPUd() void setEntries(NElem n) { mEntries = n; }
-  GPUd() void changeEntriesBy(NElem inc) { mEntries += inc; }
-  GPUd() bool operator==(const RangeReference& other) const
+  GPUhd() void clear() { set(0, 0); }
+  GPUhd() FirstEntry getFirstEntry() const { return mFirstEntry; }
+  GPUhd() FirstEntry getEntriesBound() const { return mFirstEntry + mEntries; }
+  GPUhd() NElem getEntries() const { return mEntries; }
+  GPUhd() void setFirstEntry(FirstEntry ent) { mFirstEntry = ent; }
+  GPUhd() void setEntries(NElem n) { mEntries = n; }
+  GPUhd() void changeEntriesBy(NElem inc) { mEntries += inc; }
+  GPUhd() bool operator==(const RangeReference& other) const
   {
     return mFirstEntry == other.mFirstEntry && mEntries == other.mEntries;
   }
@@ -68,21 +68,21 @@ class RangeRefComp
   static constexpr Base MaskN = ((0x1 << NBitsN) - 1);
   static constexpr Base MaskR = (~Base(0)) & (~MaskN);
   Base mData = 0; ///< packed 1st entry reference + N entries
-  GPUd() void sanityCheck()
+  GPUhd() void sanityCheck()
   {
     static_assert(NBitsN < NBitsTotal, "NBitsN too large");
   }
 
  public:
-  GPUd() RangeRefComp(int ent, int n) { set(ent, n); }
-  GPUdDefault() RangeRefComp() = default;
-  GPUdDefault() RangeRefComp(const RangeRefComp& src) = default;
+  GPUhd() RangeRefComp(int ent, int n) { set(ent, n); }
+  GPUhdDefault() RangeRefComp() = default;
+  GPUhdDefault() RangeRefComp(const RangeRefComp& src) = default;
   GPUhd() void set(int ent, int n)
   {
     mData = (Base(ent) << NBitsN) + (Base(n) & MaskN);
   }
-  GPUd() static constexpr Base getMaxFirstEntry() { return MaskR >> NBitsN; }
-  GPUd() static constexpr Base getMaxEntries() { return MaskN; }
+  GPUhd() static constexpr Base getMaxFirstEntry() { return MaskR >> NBitsN; }
+  GPUhd() static constexpr Base getMaxEntries() { return MaskN; }
   GPUhd() int getFirstEntry() const { return mData >> NBitsN; }
   GPUhd() int getEntries() const { return mData & ((0x1 << NBitsN) - 1); }
   GPUhd() int getEntriesBound() const { return getFirstEntry() + getEntries(); }
diff --git a/DataFormats/common/include/CommonDataFormat/TFIDInfo.h b/DataFormats/common/include/CommonDataFormat/TFIDInfo.h
index 2a41a4e725781..9628b38b95fa3 100644
--- a/DataFormats/common/include/CommonDataFormat/TFIDInfo.h
+++ b/DataFormats/common/include/CommonDataFormat/TFIDInfo.h
@@ -29,6 +29,7 @@ struct TFIDInfo { // helper info to patch DataHeader
   uint32_t runNumber = -1U;
   uint32_t startTime = -1U; // same as timeslot
   uint64_t creation = -1UL;
+  bool discard = false;
 
   bool isDummy() { return tfCounter == -1U; }
   void fill(uint32_t firstTForbit_, uint32_t tfCounter_, uint32_t runNumber_, uint32_t startTime_, uint64_t creation_)
@@ -38,9 +39,10 @@ struct TFIDInfo { // helper info to patch DataHeader
     runNumber = runNumber_;
     startTime = startTime_;
     creation = creation_;
+    discard = (firstTForbit < tfCounter) || firstTForbit == -1U || creation == -1;
   }
 
-  ClassDefNV(TFIDInfo, 2);
+  ClassDefNV(TFIDInfo, 3);
 };
 } // namespace dataformats
 } // namespace o2
diff --git a/DataFormats/common/include/CommonDataFormat/TimeStamp.h b/DataFormats/common/include/CommonDataFormat/TimeStamp.h
index c4732523d6e93..709af221c28f8 100644
--- a/DataFormats/common/include/CommonDataFormat/TimeStamp.h
+++ b/DataFormats/common/include/CommonDataFormat/TimeStamp.h
@@ -25,12 +25,12 @@ template 
 class TimeStamp
 {
  public:
-  GPUhdDefault() TimeStamp() CON_DEFAULT;
-  GPUhdDefault() ~TimeStamp() CON_DEFAULT;
-  GPUdi() TimeStamp(T time) { mTimeStamp = time; }
-  GPUdi() T getTimeStamp() const { return mTimeStamp; }
-  GPUdi() void setTimeStamp(T t) { mTimeStamp = t; }
-  GPUdi() bool operator==(const TimeStamp& t) const { return mTimeStamp == t.mTimeStamp; }
+  GPUhdDefault() TimeStamp() = default;
+  GPUhdDefault() ~TimeStamp() = default;
+  GPUhdi() TimeStamp(T time) { mTimeStamp = time; }
+  GPUhdi() T getTimeStamp() const { return mTimeStamp; }
+  GPUhdi() void setTimeStamp(T t) { mTimeStamp = t; }
+  GPUhdi() bool operator==(const TimeStamp& t) const { return mTimeStamp == t.mTimeStamp; }
 
  private:
   T mTimeStamp = 0;
@@ -41,10 +41,11 @@ template 
 class TimeStampWithError : public TimeStamp
 {
  public:
-  GPUdDefault() TimeStampWithError() = default;
-  GPUd() TimeStampWithError(T t, E te) : TimeStamp(t), mTimeStampError(te) {}
-  GPUdi() E getTimeStampError() const { return mTimeStampError; }
-  GPUdi() void setTimeStampError(E te) { mTimeStampError = te; }
+  GPUhdDefault() TimeStampWithError() = default;
+  GPUhd() TimeStampWithError(T t, E te) : TimeStamp(t), mTimeStampError(te) {}
+  GPUhdi() E getTimeStampError() const { return mTimeStampError; }
+  GPUhdi() E getTimeStampError2() const { return mTimeStampError * mTimeStampError; }
+  GPUhdi() void setTimeStampError(E te) { mTimeStampError = te; }
 
  private:
   E mTimeStampError = 0;
diff --git a/DataFormats/common/src/CommonDataFormatLinkDef.h b/DataFormats/common/src/CommonDataFormatLinkDef.h
index 631305cd28f13..d66e89af637cc 100644
--- a/DataFormats/common/src/CommonDataFormatLinkDef.h
+++ b/DataFormats/common/src/CommonDataFormatLinkDef.h
@@ -26,10 +26,12 @@
 #pragma link C++ class o2::dataformats::TimeStamp < float> + ;
 #pragma link C++ class o2::dataformats::TimeStamp < double> + ;
 #pragma link C++ class o2::dataformats::TimeStamp < int> + ;
-#pragma link C++ class o2::dataformats::TimeStamp < Float16_t > + ;
+#pragma link C++ class o2::dataformats::TimeStamp < uint32_t> + ;
+#pragma link C++ class o2::dataformats::TimeStamp < Float16_t> + ;
 #pragma link C++ class o2::dataformats::TimeStampWithError < float, float> + ;
 #pragma link C++ class o2::dataformats::TimeStampWithError < double, double> + ;
 #pragma link C++ class o2::dataformats::TimeStampWithError < int, int> + ;
+#pragma link C++ class o2::dataformats::TimeStampWithError < uint32_t, uint16_t> + ;
 
 #pragma link C++ class o2::dataformats::EvIndex < int, int> + ;
 #pragma link C++ class o2::dataformats::RangeReference < int, int> + ;
diff --git a/DataFormats/common/src/FlatHisto1D.cxx b/DataFormats/common/src/FlatHisto1D.cxx
index 1313da72da8b7..8e04c2c58614e 100644
--- a/DataFormats/common/src/FlatHisto1D.cxx
+++ b/DataFormats/common/src/FlatHisto1D.cxx
@@ -40,9 +40,10 @@ FlatHisto1D& FlatHisto1D::operator=(const FlatHisto1D& rhs)
   if (this == &rhs) {
     return *this;
   }
-  if (!rhs.canFill()) {
+  if (!rhs.canFill() && rhs.getNBins()) { // was initialized
     throw std::runtime_error("trying to copy read-only histogram");
-  } else {
+  }
+  if (rhs.getNBins()) {
     mContainer = rhs.mContainer;
     init(gsl::span(mContainer.data(), mContainer.size()));
   }
diff --git a/DataFormats/common/src/FlatHisto2D.cxx b/DataFormats/common/src/FlatHisto2D.cxx
index b06241ff8d193..aa064eee10bca 100644
--- a/DataFormats/common/src/FlatHisto2D.cxx
+++ b/DataFormats/common/src/FlatHisto2D.cxx
@@ -43,9 +43,10 @@ FlatHisto2D& FlatHisto2D::operator=(const FlatHisto2D& rhs)
   if (this == &rhs) {
     return *this;
   }
-  if (!rhs.canFill()) {
+  if (!rhs.canFill() && rhs.getNBins()) { // was initialized
     throw std::runtime_error("trying to copy read-only histogram");
-  } else {
+  }
+  if (rhs.getNBins()) {
     mContainer = rhs.mContainer;
     init(gsl::span(mContainer.data(), mContainer.size()));
   }
diff --git a/DataFormats/common/src/InteractionRecord.cxx b/DataFormats/common/src/InteractionRecord.cxx
index f15c8c8e85328..213ed600e1c57 100644
--- a/DataFormats/common/src/InteractionRecord.cxx
+++ b/DataFormats/common/src/InteractionRecord.cxx
@@ -15,8 +15,6 @@
 namespace o2
 {
 
-#ifndef GPUCA_ALIGPUCODE
-
 std::string InteractionRecord::asString() const
 {
   return isDummy() ? std::string{"NotSet"} : fmt::format("BCid: {:4d} Orbit: {:6d}", bc, orbit);
@@ -49,6 +47,4 @@ void InteractionTimeRecord::print() const
   std::cout << (*this) << std::endl;
 }
 
-#endif
-
 } // namespace o2
diff --git a/DataFormats/common/src/TimeStamp.cxx b/DataFormats/common/src/TimeStamp.cxx
new file mode 100644
index 0000000000000..2681c9721ba42
--- /dev/null
+++ b/DataFormats/common/src/TimeStamp.cxx
@@ -0,0 +1,19 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#include "CommonDataFormat/TimeStamp.h"
+
+template class o2::dataformats::TimeStamp;
+template class o2::dataformats::TimeStamp;
+template class o2::dataformats::TimeStamp;
+template class o2::dataformats::TimeStampWithError;
+template class o2::dataformats::TimeStampWithError;
+template class o2::dataformats::TimeStampWithError;
diff --git a/DataFormats/simulation/CMakeLists.txt b/DataFormats/simulation/CMakeLists.txt
index d7d048879a7a9..33c91337c77e9 100644
--- a/DataFormats/simulation/CMakeLists.txt
+++ b/DataFormats/simulation/CMakeLists.txt
@@ -26,6 +26,7 @@ o2_add_library(SimulationDataFormat
                                      O2::DetectorsCommonDataFormats
                                      O2::DataFormatsParameters
                                      O2::DataFormatsCalibration
+                                     O2::DataFormatsCTP
                                      O2::GPUCommon
                                      ROOT::TreePlayer)
 
@@ -54,6 +55,11 @@ o2_target_root_dictionary(
 # * src/SimulationDataLinkDef.h
 # * and not src/SimulationDataFormatLinkDef.h
 
+o2_add_test(InteractionSampler
+            SOURCES test/testInteractionSampler.cxx
+            COMPONENT_NAME SimulationDataFormat
+            PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat)
+
 o2_add_test(BasicHits
             SOURCES test/testBasicHits.cxx
             COMPONENT_NAME SimulationDataFormat
@@ -83,3 +89,8 @@ o2_add_test(MCGenStatus
             SOURCES test/testMCGenStatus.cxx
             COMPONENT_NAME SimulationDataFormat
             PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat)
+
+o2_add_test(MCGenId
+            SOURCES test/testMCGenId.cxx
+            COMPONENT_NAME SimulationDataFormat
+            PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat)
diff --git a/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h b/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h
index b9ed356ec8b5a..d1e1ee357c1cf 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h
@@ -49,13 +49,13 @@ class BasicXYZVHit : public BaseHit
   math_utils::Point3D mPos; // cartesian position of Hit
   E mTime;                     // time of flight
   V mHitValue;                 // hit value
-  short mDetectorID;           // the detector/sensor id
+  unsigned short mDetectorID;  // the detector/sensor id
 
  public:
   BasicXYZVHit() = default; // for ROOT IO
 
   // constructor
-  BasicXYZVHit(T x, T y, T z, E time, V val, int trackid, short did)
+  BasicXYZVHit(T x, T y, T z, E time, V val, int trackid, unsigned short did)
     : mPos(x, y, z), mTime(time), mHitValue(val), BaseHit(trackid), mDetectorID(did)
   {
   }
@@ -70,12 +70,12 @@ class BasicXYZVHit : public BaseHit
   // getting the time
   E GetTime() const { return mTime; }
   // get detector + track information
-  short GetDetectorID() const { return mDetectorID; }
+  unsigned short GetDetectorID() const { return mDetectorID; }
 
   // modifiers
   void SetTime(E time) { mTime = time; }
   void SetHitValue(V val) { mHitValue = val; }
-  void SetDetectorID(short detID) { mDetectorID = detID; }
+  void SetDetectorID(unsigned short detID) { mDetectorID = detID; }
   void SetX(T x) { mPos.SetX(x); }
   void SetY(T y) { mPos.SetY(y); }
   void SetZ(T z) { mPos.SetZ(z); }
@@ -87,7 +87,7 @@ class BasicXYZVHit : public BaseHit
   }
   void SetPos(math_utils::Point3D const& p) { mPos = p; }
 
-  ClassDefNV(BasicXYZVHit, 1);
+  ClassDefNV(BasicXYZVHit, 2);
 };
 
 // Class for a hit containing energy loss as hit value
diff --git a/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h b/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h
index 0e76cef50f5f6..72c66c863f698 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h
@@ -139,7 +139,7 @@ class ConstMCTruthContainerView
   {
     (void)0;
   }
-  ConstMCTruthContainerView(const ConstMCTruthContainerView&) = default;
+  // ConstMCTruthContainerView(const ConstMCTruthContainerView&) = default;
 
   // const data access
   // get individual const "view" container for a given data index
diff --git a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h
index 783884615df05..0dc3806e52cf2 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h
@@ -23,6 +23,7 @@
 #include 
 #include 
 #include 
+#include 
 
 namespace o2
 {
@@ -81,6 +82,9 @@ class DigitizationContext
   void setMuPerBC(float m) { mMuBC = m; }
   float getMuPerBC() const { return mMuBC; }
 
+  /// returns the main (hadronic interaction rate) associated to this digitization context
+  float getCalculatedInteractionRate() const { return getMuPerBC() * getBunchFilling().getNBunches() * o2::constants::lhc::LHCRevFreq; }
+
   void printCollisionSummary(bool withQED = false, int truncateOutputTo = -1) const;
 
   // we need a method to fill the file names
@@ -109,6 +113,11 @@ class DigitizationContext
   /// Check collision parts for vertex consistency.
   bool checkVertexCompatibility(bool verbose = false) const;
 
+  /// retrieves collision context for a single timeframe-id (which may be needed by simulation)
+  /// (Only copies collision context without QED information. This can be added to the result with the fillQED method
+  ///  in a second step. Takes as input a timeframe indices collection)
+  DigitizationContext extractSingleTimeframe(int timeframeid, std::vector> const& timeframeindices, std::vector const& sources_to_offset);
+
   /// function reading the hits from a chain (previously initialized with initSimChains
   /// The hits pointer will be initialized (what to we do about ownership??)
   template 
@@ -121,16 +130,23 @@ class DigitizationContext
   /// returns the GRP object associated to this context
   o2::parameters::GRPObject const& getGRP() const;
 
-  // apply collision number cuts and potential relabeling of eventID
-  void applyMaxCollisionFilter(long startOrbit, long orbitsPerTF, int maxColl);
+  // apply collision number cuts and potential relabeling of eventID, (keeps collisions which fall into the orbitsEarly range for the next timeframe)
+  // needs a timeframe index structure (determined by calcTimeframeIndices), which is adjusted during the process to reflect the filtering
+  void applyMaxCollisionFilter(std::vector>& timeframeindices, long startOrbit, long orbitsPerTF, int maxColl, double orbitsEarly = 0.);
 
-  // finalize timeframe structure (fixes the indices in mTimeFrameStartIndex)
-  void finalizeTimeframeStructure(long startOrbit, long orbitsPerTF);
+  /// get timeframe structure --> index markers where timeframe starts/ends/is_influenced_by
+  std::vector> calcTimeframeIndices(long startOrbit, long orbitsPerTF, double orbitsEarly = 0.) const;
 
   // Sample and fix interaction vertices (according to some distribution). Makes sure that same event ids
   // have to have same vertex, as well as event ids associated to same collision.
   void sampleInteractionVertices(o2::dataformats::MeanVertexObject const& v);
 
+  // Function allowing to inject interaction vertixes from the outside.
+  // Useful when this is given from data for instance. The vertex vector needs to be of same
+  // size as the interaction record.
+  // Returns 0 if success. 1 if there is a problem.
+  int setInteractionVertices(std::vector> const& vertices);
+
   // helper functions to save and load a context
   void saveToFile(std::string_view filename) const;
 
@@ -141,6 +157,20 @@ class DigitizationContext
 
   static DigitizationContext* loadFromFile(std::string_view filename = "");
 
+  void setCTPDigits(std::vector const* ctpdigits) const
+  {
+    mCTPTrigger = ctpdigits;
+    if (mCTPTrigger) {
+      mHasTrigger = true;
+    }
+  }
+
+  void setDigitizerInteractionRate(float intRate) { mDigitizerInteractionRate = intRate; }
+  float getDigitizerInteractionRate() const { return mDigitizerInteractionRate; }
+
+  std::vector const* getCTPDigits() const { return mCTPTrigger; }
+  bool hasTriggerInput() const { return mHasTrigger; }
+
  private:
   int mNofEntries = 0;
   int mMaxPartNumber = 0; // max number of parts in any given collision
@@ -152,24 +182,29 @@ class DigitizationContext
   // for each collision we record the constituents (which shall not exceed mMaxPartNumber)
   std::vector> mEventParts;
 
-  // for each collision we may record/fix the interaction vertex (to be used in event generation)
+  // for each collisionstd::vector> &timeframeindice we may record/fix the interaction vertex (to be used in event generation)
   std::vector> mInteractionVertices;
 
-  // the collision records _with_ QED interleaved;
+  // the collision records **with** QED interleaved;
   std::vector mEventRecordsWithQED;
   std::vector> mEventPartsWithQED;
 
-  // timeframe structure
-  std::vector> mTimeFrameStartIndex;    // for each timeframe, the pair of start-index and end-index into mEventParts, mEventRecords
-  std::vector> mTimeFrameStartIndexQED; // for each timeframe, the pair of start-index and end-index into mEventParts, mEventRecords (QED version)
-
   o2::BunchFilling mBCFilling; // pattern of active BCs
 
   std::vector mSimPrefixes;             // identifiers to the hit sim products; the key corresponds to the source ID of event record
   std::string mQEDSimPrefix;                         // prefix for QED production/contribution
   mutable o2::parameters::GRPObject* mGRP = nullptr; //!
 
-  ClassDefNV(DigitizationContext, 4);
+  mutable std::vector const* mCTPTrigger = nullptr; // CTP trigger info associated to this digitization context
+  mutable bool mHasTrigger = false;                                    //
+
+  // The global ALICE interaction hadronic interaction rate as applied in digitization.
+  // It should be consistent with mMuPerBC but it might be easier to handle.
+  // The value will be filled/inserted by the digitization workflow so that digiters can access it.
+  // There is no guarantee that the value is available elsewhere.
+  float mDigitizerInteractionRate{-1};
+
+  ClassDefNV(DigitizationContext, 6);
 };
 
 /// function reading the hits from a chain (previously initialized with initSimChains
@@ -180,12 +215,19 @@ inline void DigitizationContext::retrieveHits(std::vector const& chains
                                               int entryID,
                                               std::vector* hits) const
 {
+  if (chains.size() <= sourceID) {
+    return;
+  }
   auto br = chains[sourceID]->GetBranch(brname);
   if (!br) {
     LOG(error) << "No branch found with name " << brname;
     return;
   }
   br->SetAddress(&hits);
+  auto maxEntries = br->GetEntries();
+  if (maxEntries) {
+    entryID %= maxEntries;
+  }
   br->GetEntry(entryID);
 }
 
diff --git a/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h b/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h
index 13fe099aa344a..47dd4f5e4652d 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h
@@ -22,6 +22,7 @@
 #include "CommonDataFormat/BunchFilling.h"
 #include "CommonConstants/LHCConstants.h"
 #include "MathUtils/RandomRing.h"
+#include 
 
 namespace o2
 {
@@ -67,7 +68,7 @@ class InteractionSampler
   void print() const;
 
  protected:
-  int simulateInteractingBC();
+  virtual int simulateInteractingBC();
   void nextCollidingBC(int n);
 
   o2::math_utils::RandomRing<10000> mBCJumpGenerator;  // generator of random jumps in BC
@@ -89,7 +90,7 @@ class InteractionSampler
 
   static constexpr float DefIntRate = 50e3; ///< default interaction rate
 
-  ClassDefNV(InteractionSampler, 1);
+  ClassDef(InteractionSampler, 1);
 };
 
 //_________________________________________________
@@ -113,6 +114,47 @@ inline void InteractionSampler::nextCollidingBC(int n)
   mIR.bc = mInteractingBCs[mCurrBCIdx];
 }
 
+// Special case of InteractionSampler without actual sampling.
+// Engineers interaction sequence by putting one in each N-th BC with multiplicity mult.
+class FixedSkipBC_InteractionSampler : public InteractionSampler
+{
+
+ public:
+  FixedSkipBC_InteractionSampler(int every_n, int mult) : mEveryN{every_n}, mMultiplicity{mult}, InteractionSampler() {}
+
+ protected:
+  int simulateInteractingBC() override;
+
+ private:
+  int mEveryN;       // the skip number ---> fills every N-th BC in the bunch filling scheme
+  int mMultiplicity; // how many events to put if bc is filled
+  ClassDef(FixedSkipBC_InteractionSampler, 1);
+};
+
+// A version of the interaction sampler which can sample according to non-uniform mu(bc) as
+// observed during data taking.
+class NonUniformMuInteractionSampler : public InteractionSampler
+{
+ public:
+  NonUniformMuInteractionSampler() : InteractionSampler() { mBCIntensityScales.resize(o2::constants::lhc::LHCMaxBunches, 1); }
+  bool setBCIntensityScales(const std::vector& scales_from_vector);
+  bool setBCIntensityScales(const TH1F& scales_from_histo); // initialize scales
+
+  // helper function to determine the scales from a histogram (count from event selection analysis)
+  std::vector determineBCIntensityScalesFromHistogram(const TH1F& scales_from_histo);
+
+  const std::vector& getBCIntensityScales() const { return mBCIntensityScales; }
+
+ protected:
+  int simulateInteractingBC() override;
+  int getBCJump() const;
+
+ private:
+  // non-uniformity
+  std::vector mBCIntensityScales;
+  ClassDef(NonUniformMuInteractionSampler, 1);
+};
+
 } // namespace steer
 } // namespace o2
 
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h b/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h
index bdac15ef8e10e..74c47c87f22d5 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h
@@ -12,7 +12,9 @@
 #ifndef ALICEO2_MCCOMPLABEL_H
 #define ALICEO2_MCCOMPLABEL_H
 
+#include 
 #include "GPUCommonRtypes.h"
+#include 
 
 namespace o2
 {
@@ -127,7 +129,7 @@ class MCCompLabel
   int getEventID() const { return (mLabel >> nbitsTrackID) & maskEvID; }
   int getSourceID() const { return (mLabel >> (nbitsTrackID + nbitsEvID)) & maskSrcID; }
   uint64_t getTrackEventSourceID() const { return static_cast(mLabel & maskFull); }
-  void get(int& trackID, int& evID, int& srcID, bool& fake)
+  void get(int& trackID, int& evID, int& srcID, bool& fake) const
   {
     /// parse label
     trackID = getTrackID();
@@ -137,6 +139,7 @@ class MCCompLabel
   }
 
   void print() const;
+  std::string asString() const;
 
   static constexpr int maxSourceID() { return maskSrcID; }
   static constexpr int maxEventID() { return maskEvID; }
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h b/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h
index cf323e197392d..efaf46806a89a 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h
@@ -27,6 +27,73 @@ namespace dataformats
 
 class GeneratorHeader;
 
+/** Common keys for information in MC event header */
+struct MCInfoKeys {
+  /** @{
+@name HepMC3 heavy-ion fields */
+  static constexpr const char* impactParameter = "Bimpact";
+  static constexpr const char* nPart = "Npart";
+  static constexpr const char* nPartProjectile = "Npart_proj";
+  static constexpr const char* nPartTarget = "Npart_targ";
+  static constexpr const char* nColl = "Ncoll";
+  static constexpr const char* nCollHard = "Ncoll_hard";
+  static constexpr const char* nCollNNWounded = "NColl_NNw";
+  static constexpr const char* nCollNWoundedN = "NColl_NwN";
+  static constexpr const char* nCollNWoundedNwounded = "NColl_NwNW";
+  static constexpr const char* planeAngle = "eventPsi";
+  static constexpr const char* sigmaInelNN = "sigmaInelNN";
+  static constexpr const char* centrality = "centrality";
+  static constexpr const char* nSpecProjectileProton = "Nspec_proj_p";
+  static constexpr const char* nSpecProjectileNeutron = "Nspec_proj_n";
+  static constexpr const char* nSpecTargetProton = "Nspec_targ_p";
+  static constexpr const char* nSpecTargetNeutron = "Nspec_targ_n";
+  /** @} */
+  /** @{
+@name HepMC3 PDF information
+
+In principle a header can have many of these.  In that case,
+each set should be prefixed with "_" where "" is a
+serial number.
+  */
+  static constexpr const char* pdfParton1Id = "pdf_parton_1_id";
+  static constexpr const char* pdfParton2Id = "pdf_parton_2_id";
+  static constexpr const char* pdfX1 = "pdf_x1";
+  static constexpr const char* pdfX2 = "pdf_x2";
+  static constexpr const char* pdfScale = "pdf_scale";
+  static constexpr const char* pdfXF1 = "pdf_par_x1";
+  static constexpr const char* pdfXF2 = "pdf_par_x2";
+  static constexpr const char* pdfCode1 = "pdf_lhc_1_id";
+  static constexpr const char* pdfCode2 = "pdf_lhc_2_id";
+  /** @} */
+  /** @{
+@name HepMC3 cross-section information
+
+In principle we can have one cross section per weight. In that
+case, each should be post-fixed by "_" where "" is a
+serial number.  These should then matcht possible names of
+weights.
+  */
+  static constexpr const char* acceptedEvents = "accepted_events";
+  static constexpr const char* attemptedEvents = "attempted_events";
+  static constexpr const char* xSection = "cross_section";
+  static constexpr const char* xSectionError = "cross_section_error";
+  /** @} */
+  /** @{
+@name Common fields */
+  static constexpr const char* generator = "generator";
+  static constexpr const char* generatorVersion = "version";
+  static constexpr const char* processName = "processName";
+  static constexpr const char* processCode = "processCode";
+  static constexpr const char* weight = "weight";
+  /** @} */
+  /** @{
+@name Pythia8-specific fields */
+  static constexpr const char* processID = "signal_process_id";
+  static constexpr const char* eventScale = "event_scale";
+  static constexpr const char* mpi = "mpi";
+  /** @} */
+};
+
 /*****************************************************************/
 /*****************************************************************/
 
@@ -75,17 +142,28 @@ class MCEventHeader : public FairMCEventHeader
     return ref;
   };
 
+  void print() const;
+
   /// prints a summary of info keys/types attached to this header
   void printInfo() const
   {
     mEventInfo.print();
   }
 
+  /// inits info fields from another Event header
+  void copyInfoFrom(MCEventHeader const& other)
+  {
+    mEventInfo.copyFrom(other.mEventInfo);
+  }
+
   /** methods **/
   virtual void Reset();
 
   MCEventStats& getMCEventStats() { return mEventStats; }
 
+  void setDetId2HitBitLUT(std::vector const& v) { mDetId2HitBitIndex = v; }
+  std::vector const& getDetId2HitBitLUT() const { return mDetId2HitBitIndex; }
+
   /// create a standalone ROOT file/tree with only the MCHeader branch
   static void extractFileFromKinematics(std::string_view kinefilename, std::string_view targetfilename);
 
@@ -96,9 +174,10 @@ class MCEventHeader : public FairMCEventHeader
   // store a few global properties that this event
   // had in the current simulation (which can be used quick filtering/searching)
   MCEventStats mEventStats{};
+  std::vector mDetId2HitBitIndex;
   o2::utils::RootSerializableKeyValueStore mEventInfo;
 
-  ClassDefOverride(MCEventHeader, 3);
+  ClassDefOverride(MCEventHeader, 4);
 
 }; /** class MCEventHeader **/
 
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCGenProperties.h b/DataFormats/simulation/include/SimulationDataFormat/MCGenProperties.h
new file mode 100644
index 0000000000000..1a4f86cc98122
--- /dev/null
+++ b/DataFormats/simulation/include/SimulationDataFormat/MCGenProperties.h
@@ -0,0 +1,132 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#ifndef ALICEO2_SIMDATA_MCGENPROPERTIES_H_
+#define ALICEO2_SIMDATA_MCGENPROPERTIES_H_
+
+namespace o2
+{
+namespace mcgenstatus
+{
+
+// Value to check MCGenStatusEncoding::isEncoded against to decide whether or not the stored value is encoded or basically only the HepMC status code
+// as it used to be
+constexpr unsigned int isEncodedValue{5};
+
+// internal structure to allow convenient manipulation of properties as bits on an int to (dis)entangle HepMC and specific generator status codes
+union MCGenStatusEncoding {
+  MCGenStatusEncoding() : fullEncoding(0) {}
+  MCGenStatusEncoding(int enc) : fullEncoding(enc) {}
+  // To be backward-compatible, only set transport to 1 if hepmc status is 1
+  MCGenStatusEncoding(int hepmcIn, int genIn) : isEncoded(isEncodedValue), hepmc(hepmcIn), gen(genIn), reserved(0) {}
+  int fullEncoding;
+  struct {
+    int hepmc : 9;              // HepMC status code
+    int gen : 10;               // specific generator status code
+    int reserved : 10;          // reserved bits for future usage
+    unsigned int isEncoded : 3; // special bits to check whether or not the fullEncoding is a combination of HepMC and gen status codes
+  };
+};
+
+inline bool isEncoded(MCGenStatusEncoding statusCode)
+{
+  return (statusCode.isEncoded == isEncodedValue);
+}
+
+inline bool isEncoded(int statusCode)
+{
+  return isEncoded(MCGenStatusEncoding(statusCode));
+}
+
+inline int getHepMCStatusCode(MCGenStatusEncoding enc)
+{
+  if (!isEncoded(enc)) {
+    // in this case simply set hepmc code to what was given
+    return enc.fullEncoding;
+  }
+  return enc.hepmc;
+}
+
+inline int getGenStatusCode(MCGenStatusEncoding enc)
+{
+  if (!isEncoded(enc)) {
+    // in this case simply set hepmc code to what was given
+    return enc.fullEncoding;
+  }
+  return enc.gen;
+}
+
+inline int getHepMCStatusCode(int encoded)
+{
+  return getHepMCStatusCode(MCGenStatusEncoding(encoded));
+}
+
+inline int getGenStatusCode(int encoded)
+{
+  return getGenStatusCode(MCGenStatusEncoding(encoded));
+}
+
+} // namespace mcgenstatus
+
+namespace mcgenid
+{
+
+// Define some common properties that can be set for Generators
+class GeneratorProperty
+{
+ public:
+  typedef const char* Property;
+  static constexpr Property GENERATORID{"generator_id"};
+  static constexpr Property GENERATORDESCRIPTION{"generator_description"};
+  static constexpr Property SUBGENERATORID{"subgenerator_id"};
+  static constexpr Property SUBGENERATORDESCRIPTIONMAP{"subgenerator_description_map"};
+};
+
+// internal structure to allow encoding of generator IDs and map different numbers to a single short
+union MCGenIdEncoding {
+  MCGenIdEncoding() : fullEncoding(0) {}
+  MCGenIdEncoding(int enc) : fullEncoding(enc) {}
+  MCGenIdEncoding(int generatorId, int sourceId, int subGeneratorId) : generatorId(generatorId), sourceId(sourceId), subGeneratorId(subGeneratorId) {}
+  short fullEncoding;
+  struct {
+    unsigned short generatorId : 7;    // an additional identifier for a generator which can be set by the user
+    unsigned short sourceId : 4;       // ID used in embedding scenarios
+    unsigned short subGeneratorId : 5; // sub generator ID in case a generator implements some additional logic
+  };
+};
+
+inline short getEncodedGenId(int generatorId, int sourceId, int subGeneratorId = -1)
+{
+
+  return MCGenIdEncoding(generatorId, sourceId, subGeneratorId + 1).fullEncoding;
+}
+
+inline int getGeneratorId(short encoded)
+{
+
+  return static_cast(MCGenIdEncoding(encoded).generatorId);
+}
+
+inline int getSourceId(short encoded)
+{
+  return static_cast(MCGenIdEncoding(encoded).sourceId);
+}
+
+inline int getSubGeneratorId(short encoded)
+{
+  return static_cast(MCGenIdEncoding(encoded).subGeneratorId) - 1;
+}
+
+} // namespace mcgenid
+
+} // namespace o2
+
+#endif
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCGenStatus.h b/DataFormats/simulation/include/SimulationDataFormat/MCGenStatus.h
deleted file mode 100644
index 09b2b51c8e483..0000000000000
--- a/DataFormats/simulation/include/SimulationDataFormat/MCGenStatus.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
-// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
-// All rights not expressly granted are reserved.
-//
-// This software is distributed under the terms of the GNU General Public
-// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
-//
-// In applying this license CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-#ifndef ALICEO2_SIMDATA_MCGENSTATUS_H_
-#define ALICEO2_SIMDATA_MCGENSTATUS_H_
-
-namespace o2
-{
-namespace mcgenstatus
-{
-
-// Value to check MCGenStatusEncoding::isEncoded against to decide whether or not the stored value is encoded or basically only the HepMC status code
-// as it used to be
-constexpr unsigned int isEncodedValue{5};
-
-// internal structure to allow convenient manipulation of properties as bits on an int to (dis)entangle HepMC and specific generator status codes
-union MCGenStatusEncoding {
-  MCGenStatusEncoding() : fullEncoding(0) {}
-  MCGenStatusEncoding(int enc) : fullEncoding(enc) {}
-  // To be backward-compatible, only set transport to 1 if hepmc status is 1
-  MCGenStatusEncoding(int hepmcIn, int genIn) : isEncoded(isEncodedValue), hepmc(hepmcIn), gen(genIn), reserved(0) {}
-  int fullEncoding;
-  struct {
-    int hepmc : 9;              // HepMC status code
-    int gen : 10;               // specific generator status code
-    int reserved : 10;          // reserved bits for future usage
-    unsigned int isEncoded : 3; // special bits to check whether or not the fullEncoding is a combination of HepMC and gen status codes
-  };
-};
-
-inline bool isEncoded(MCGenStatusEncoding statusCode)
-{
-  return (statusCode.isEncoded == isEncodedValue);
-}
-
-inline bool isEncoded(int statusCode)
-{
-  return isEncoded(MCGenStatusEncoding(statusCode));
-}
-
-inline int getHepMCStatusCode(MCGenStatusEncoding enc)
-{
-  if (!isEncoded(enc)) {
-    // in this case simply set hepmc code to what was given
-    return enc.fullEncoding;
-  }
-  return enc.hepmc;
-}
-
-inline int getGenStatusCode(MCGenStatusEncoding enc)
-{
-  if (!isEncoded(enc)) {
-    // in this case simply set hepmc code to what was given
-    return enc.fullEncoding;
-  }
-  return enc.gen;
-}
-
-inline int getHepMCStatusCode(int encoded)
-{
-  return getHepMCStatusCode(MCGenStatusEncoding(encoded));
-}
-
-inline int getGenStatusCode(int encoded)
-{
-  return getGenStatusCode(MCGenStatusEncoding(encoded));
-}
-
-} // namespace mcgenstatus
-
-} // namespace o2
-
-#endif
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h b/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h
index 0fc9b932ce7a6..b4cf26b11f82e 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h
@@ -17,7 +17,7 @@
 #define ALICEO2_DATA_MCTRACK_H_
 
 #include "SimulationDataFormat/ParticleStatus.h"
-#include "SimulationDataFormat/MCGenStatus.h"
+#include "SimulationDataFormat/MCGenProperties.h"
 #include "DetectorsCommonDataFormats/DetID.h"
 #include "Rtypes.h"
 #include "SimulationDataFormat/O2DatabasePDG.h"
@@ -27,6 +27,7 @@
 #include "TParticle.h"
 #include "TParticlePDG.h"
 #include "TVector3.h"
+#include 
 
 namespace o2
 {
@@ -45,6 +46,8 @@ template 
 class MCTrackT
 {
  public:
+  static constexpr int NHITBITS = 22; // do not modify this
+
   ///  Default constructor
   MCTrackT();
 
@@ -69,7 +72,7 @@ class MCTrackT
   Int_t GetPdgCode() const { return mPdgCode; }
   Int_t getMotherTrackId() const { return mMotherTrackId; }
   Int_t getSecondMotherTrackId() const { return mSecondMotherTrackId; }
-  bool isPrimary() const { return getProcess() == TMCProcess::kPPrimary; }
+  bool isPrimary() const { return (getProcess() == TMCProcess::kPPrimary) || (getMotherTrackId() < 0 && getSecondMotherTrackId() < 0); }
   bool isSecondary() const { return !isPrimary(); }
   Int_t getFirstDaughterTrackId() const { return mFirstDaughterTrackId; }
   Int_t getLastDaughterTrackId() const { return mLastDaughterTrackId; }
@@ -81,6 +84,11 @@ class MCTrackT
   Double_t GetStartVertexCoordinatesZ() const { return mStartVertexCoordinatesZ; }
   Double_t GetStartVertexCoordinatesT() const { return mStartVertexCoordinatesT; }
 
+  /// production radius squared
+  Double_t R2() const { return Vx() * Vx() + Vy() * Vy(); }
+  /// production radius
+  Double_t R() const { return std::sqrt(R2()); }
+
   /// return mass from PDG Database if known (print message in case cannot look up)
   Double_t GetMass() const;
 
@@ -117,7 +125,7 @@ class MCTrackT
   {
     double mx(mStartVertexMomentumX);
     double my(mStartVertexMomentumY);
-    return (TMath::Pi() + TMath::ATan2(-mx, -my));
+    return (TMath::Pi() + TMath::ATan2(-my, -mx));
   }
 
   Double_t GetEta() const
@@ -131,6 +139,12 @@ class MCTrackT
     }
   }
 
+  Double_t GetTgl() const
+  {
+    auto pT = GetPt();
+    return pT > 1e-6 ? mStartVertexMomentumZ / pT : (GetStartVertexMomentumZ() > 0 ? 999. : -999.);
+  }
+
   Double_t GetTheta() const
   {
     double mz(mStartVertexMomentumZ);
@@ -139,11 +153,11 @@ class MCTrackT
 
   Double_t GetRapidity() const;
 
-  void GetMomentum(TVector3& momentum);
+  void GetMomentum(TVector3& momentum) const;
 
-  void Get4Momentum(TLorentzVector& momentum);
+  void Get4Momentum(TLorentzVector& momentum) const;
 
-  void GetStartVertex(TVector3& vertex);
+  void GetStartVertex(TVector3& vertex) const;
 
   /// Accessors to the hit mask
   Int_t getHitMask() const { return ((PropEncoding)mProp).hitmask; }
@@ -153,24 +167,40 @@ class MCTrackT
   void SetSecondMotherTrackId(Int_t id) { mSecondMotherTrackId = id; }
   void SetFirstDaughterTrackId(Int_t id) { mFirstDaughterTrackId = id; }
   void SetLastDaughterTrackId(Int_t id) { mLastDaughterTrackId = id; }
+
   // set bit indicating that this track
-  // left a hit in detector with id iDet
-  void setHit(Int_t iDet)
+  // left a hit in detector that corresponds to bit iDetBit. This bit is found in
+  // a lookup table.
+  void setHit(Int_t iDetBit)
   {
-    assert(0 <= iDet && iDet < o2::detectors::DetID::nDetectors);
+    assert(0 <= iDetBit && iDetBit < o2::detectors::DetID::nDetectors);
     auto prop = ((PropEncoding)mProp);
-    prop.hitmask |= 1 << iDet;
+    prop.hitmask |= 1 << iDetBit;
     mProp = prop.i;
   }
 
-  // did detector iDet see this track?
-  bool leftTrace(Int_t iDet) const { return (((PropEncoding)mProp).hitmask & (1 << iDet)) > 0; }
+  bool leftTraceGivenBitField(int bit) const
+  {
+    return (((PropEncoding)mProp).hitmask & (1 << bit)) > 0;
+  }
+
+  /// Returns of detector with DetID iDet has seen this track.
+  /// Needs lookup table detIDToBit mapping detectorIDs to bitfields (which is persistified in MCEventHeaders).
+  bool leftTrace(Int_t iDet, std::vector const& detIDtoBit) const
+  {
+    auto bit = detIDtoBit[iDet];
+    if (bit != -1) {
+      return leftTraceGivenBitField(bit);
+    }
+    return false;
+  }
+
   // determine how many detectors "saw" this track
   int getNumDet() const
   {
     int count = 0;
-    for (auto i = o2::detectors::DetID::First; i < o2::detectors::DetID::nDetectors; ++i) {
-      if (leftTrace(i)) {
+    for (auto i = 0; i < NHITBITS; ++i) {
+      if (leftTraceGivenBitField(i)) {
         count++;
       }
     }
@@ -255,9 +285,8 @@ class MCTrackT
     struct {
       int storage : 1;  // encoding whether to store this track to the output
       unsigned int process : 6; // encoding process that created this track (enough to store TMCProcess from ROOT)
-      int hitmask : 21; // encoding hits per detector
-      int reserved1 : 1; // bit reserved for possible future purposes
-      int reserved2 : 1; // bit reserved for possible future purposes
+      int hitmask : NHITBITS;   // encoding hits per detector
+      int reserved1 : 1;        // bit reserved for possible future purposes
       int inhibited : 1; // whether tracking of this was inhibited
       int toBeDone : 1; // whether this (still) needs tracking --> we might more complete information to cover full ParticleStatus space
     };
@@ -275,24 +304,26 @@ template 
 inline Double_t MCTrackT::GetEnergy() const
 {
   const auto mass = GetMass();
-  return std::sqrt(mass * mass + mStartVertexMomentumX * mStartVertexMomentumX +
-                   mStartVertexMomentumY * mStartVertexMomentumY + mStartVertexMomentumZ * mStartVertexMomentumZ);
+  Double_t px = mStartVertexMomentumX;
+  Double_t py = mStartVertexMomentumY;
+  Double_t pz = mStartVertexMomentumZ;
+  return std::sqrt(mass * mass + px * px + py * py + pz * pz);
 }
 
 template 
-inline void MCTrackT::GetMomentum(TVector3& momentum)
+inline void MCTrackT::GetMomentum(TVector3& momentum) const
 {
   momentum.SetXYZ(mStartVertexMomentumX, mStartVertexMomentumY, mStartVertexMomentumZ);
 }
 
 template 
-inline void MCTrackT::Get4Momentum(TLorentzVector& momentum)
+inline void MCTrackT::Get4Momentum(TLorentzVector& momentum) const
 {
   momentum.SetXYZT(mStartVertexMomentumX, mStartVertexMomentumY, mStartVertexMomentumZ, GetEnergy());
 }
 
 template 
-inline void MCTrackT::GetStartVertex(TVector3& vertex)
+inline void MCTrackT::GetStartVertex(TVector3& vertex) const
 {
   vertex.SetXYZ(mStartVertexCoordinatesX, mStartVertexCoordinatesY, mStartVertexCoordinatesZ);
 }
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCUtils.h b/DataFormats/simulation/include/SimulationDataFormat/MCUtils.h
index d6bdbbe79498e..c54b424eb0238 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/MCUtils.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/MCUtils.h
@@ -16,8 +16,8 @@
 #ifndef O2_MCUTILS_H
 #define O2_MCUTILS_H
 
+#include 
 #include 
-#include 
 #include 
 #include "TPDGCode.h"
 #include "TParticle.h"
@@ -26,6 +26,7 @@ namespace o2
 {
 namespace mcutils
 {
+
 /// A couple of functions to query on MC tracks ( that needs navigation within the global container
 /// of available tracks. It is a class so as to make it available for interactive ROOT more easily.
 class MCTrackNavigator
@@ -79,7 +80,6 @@ class MCGenHelper
   // Has to be in a class as a static methid. Just in a namespace it doesn't work to use this function in ROOT macros.
   static void encodeParticleStatusAndTracking(TParticle& particle, bool wanttracking = true);
   static void encodeParticleStatusAndTracking(TParticle& particle, int hepmcStatus, int genStatus, bool wanttracking = true);
-
   ClassDefNV(MCGenHelper, 1)
 };
 
diff --git a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h
index 16d85d337fa3e..7a19798674bf6 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h
@@ -15,6 +15,7 @@
 
 #ifndef O2_O2DATABASEPDG_H
 #define O2_O2DATABASEPDG_H
+
 #include 
 #include "TDatabasePDG.h"
 #include "TParticlePDG.h"
@@ -37,10 +38,6 @@ class O2DatabasePDG
     auto db = TDatabasePDG::Instance();
     if (!initialized) {
       addALICEParticles(db);
-      if (const char* o2Root = std::getenv("O2_ROOT")) {
-        auto inputExtraPDGs = std::string(o2Root) + "/share/Detectors/gconfig/data/extra_ions_pdg_table.dat";
-        db->ReadPDGTable(inputExtraPDGs.c_str());
-      }
       initialized = true;
     }
     return db;
@@ -48,6 +45,7 @@ class O2DatabasePDG
 
   // adds ALICE particles to a given TDatabasePDG instance
   static void addALICEParticles(TDatabasePDG* db = TDatabasePDG::Instance());
+  static void addParticlesFromExternalFile(TDatabasePDG* db);
 
   // get particle's (if any) mass
   static Double_t MassImpl(TParticlePDG* particle, bool& success)
@@ -61,10 +59,8 @@ class O2DatabasePDG
   }
 
   // determine particle to get mass for based on PDG
-  static Double_t Mass(int pdg, bool& success)
+  static Double_t Mass(int pdg, bool& success, TDatabasePDG* db = O2DatabasePDG::Instance())
   {
-    auto db = Instance();
-
     if (pdg < IONBASELOW || pdg > IONBASEHIGH) {
       // not an ion, return immediately
       return MassImpl(db->GetParticle(pdg), success);
@@ -78,9 +74,10 @@ class O2DatabasePDG
     return MassImpl(db->GetParticle(pdg), success);
   }
 
- private:
-  // private constructor
+  // remove default constructor
   O2DatabasePDG() = delete;
+
+ private:
   static constexpr int IONBASELOW{1000000000};
   static constexpr int IONBASEHIGH{1099999999};
 };
@@ -108,18 +105,23 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
   const Double_t khShGev = khSlash * kErg2Gev;
   const Double_t kYear2Sec = 3600 * 24 * 365.25;
 
+  // Heavy-flavour particles
+
+  // χc1(3872) aka X(3872), taken from PDG 2022 (https://pdg.lbl.gov/2022/listings/rpp2022-list-chi-c1-3872.pdf)
+  db->AddParticle("Chi_c1(3872)", "Chi_c1(3872)", 3.87165, kFALSE, 0, 0, "CCBarMeson", 9920443);
+
   //
   // Bottom mesons
   // mass and life-time from PDG
   //
   db->AddParticle("Upsilon(3S)", "Upsilon(3S)", 10.3552, kTRUE,
-                  0, 1, "Bottonium", 200553);
+                  0, 0, "Bottonium", 200553);
 
   // QCD diffractive states
   db->AddParticle("rho_diff0", "rho_diff0", 0, kTRUE,
                   0, 0, "QCD diffr. state", 9900110);
   db->AddParticle("pi_diffr+", "pi_diffr+", 0, kTRUE,
-                  0, 1, "QCD diffr. state", 9900210);
+                  0, 3, "QCD diffr. state", 9900210);
   db->AddParticle("omega_di", "omega_di", 0, kTRUE,
                   0, 0, "QCD diffr. state", 9900220);
   db->AddParticle("phi_diff", "phi_diff", 0, kTRUE,
@@ -129,7 +131,7 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
   db->AddParticle("n_diffr0", "n_diffr0", 0, kTRUE,
                   0, 0, "QCD diffr. state", 9902110);
   db->AddParticle("p_diffr+", "p_diffr+", 0, kTRUE,
-                  0, 1, "QCD diffr. state", 9902210);
+                  0, 3, "QCD diffr. state", 9902210);
 
   // From Herwig
   db->AddParticle("PSID    ", " ", 3.7699, kFALSE, 0.0, 0, "meson", 30443);
@@ -229,81 +231,67 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
                   0, 0, "Special", kspe + 50);
   db->AddParticle("FeedbackPhoton", "FeedbackPhoton", 0, kFALSE,
                   0, 0, "Special", kspe + 51);
-  db->AddParticle("Lambda1520", "Lambda1520", 1.5195, kFALSE,
-                  0.0156, 0, "Resonance", 3124);
-  db->AddAntiParticle("Lambda1520bar", -3124);
 
   //Hyper nuclei and exotica
   ionCode = 1010010030;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("HyperTriton", "HyperTriton", 2.99131, kFALSE,
+    db->AddParticle("HyperTriton", "HyperTriton", 2.991134, kFALSE,
                     2.5e-15, 3, "Ion", ionCode);
   }
 
   ionCode = -1010010030;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperTriton", "AntiHyperTriton", 2.99131, kFALSE,
+    db->AddParticle("AntiHyperTriton", "AntiHyperTriton", 2.991134, kFALSE,
                     2.5e-15, 3, "Ion", ionCode);
   }
 
   //hyper hydrogen 4 ground state
   ionCode = 1010010040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("Hyperhydrog4", "Hyperhydrog4", 3.9226, kFALSE,
+    db->AddParticle("Hyperhydrog4", "Hyperhydrog4", 3.922434, kFALSE,
                     2.5e-15, 3, "Ion", ionCode);
   }
   //anti hyper hydrogen 4 ground state
   ionCode = -1010010040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperhydrog4", "AntiHyperhydrog4", 3.9226, kFALSE,
-                    2.5e-15, 3, "Ion", ionCode);
-  }
-  //hyper hydrogen 4 excited state
-  ionCode = 1010010041;
-  if (!db->GetParticle(ionCode)) {
-    db->AddParticle("Hyperhydrog4*", "Hyperhydrog4*", 3.9237, kFALSE,
-                    2.5e-15, 3, "Ion", ionCode);
-  }
-  //anti hyper hydrogen 4 excited state
-  ionCode = -1010010041;
-  if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperhydrog4*", "AntiHyperhydrog4*", 3.9237, kFALSE,
+    db->AddParticle("AntiHyperhydrog4", "AntiHyperhydrog4", 3.922434, kFALSE,
                     2.5e-15, 3, "Ion", ionCode);
   }
   //hyper helium 4 ground state
   ionCode = 1010020040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("Hyperhelium4", "Hyperhelium4", 3.9217, kFALSE,
+    db->AddParticle("Hyperhelium4", "Hyperhelium4", 3.921728, kFALSE,
                     2.5e-15, 6, "Ion", ionCode);
   }
   //anti hyper helium 4 ground state
   ionCode = -1010020040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperhelium4", "AntiHyperhelium4", 3.9217, kFALSE,
+    db->AddParticle("AntiHyperhelium4", "AntiHyperhelium4", 3.921728, kFALSE,
                     2.5e-15, 6, "Ion", ionCode);
   }
-  //hyper helium 4 excited state
-  ionCode = 1010020041;
+
+  // Lithium 4 ground state
+  ionCode = 1000030040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("Hyperhelium4*", "Hyperhelium4*", 3.9231, kFALSE,
-                    2.5e-15, 6, "Ion", ionCode);
+    db->AddParticle("Lithium4", "Lithium4", 3.7513, kFALSE,
+                    0.003, 9, "Ion", ionCode);
   }
-  //anti hyper helium 4 excited state
-  ionCode = -1010020041;
+  // anti Lithium 4 ground state
+  ionCode = -1000030040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperhelium4*", "AntiHyperhelium4*", 3.9231, kFALSE,
-                    2.5e-15, 6, "Ion", ionCode);
+    db->AddParticle("AntiLithium4", "AntiLithium4", 3.7513, kFALSE,
+                    0.003, 9, "Ion", ionCode);
   }
 
   ionCode = 1010020050;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("Hyperhelium5", "Hyperhelium5", 4.841, kFALSE,
+    db->AddParticle("Hyperhelium5", "Hyperhelium5", 4.839961, kFALSE,
                     2.5e-15, 6, "Ion", ionCode);
   }
 
   ionCode = -1010020050;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperhelium5", "AntiHyperhelium5", 4.841, kFALSE,
+    db->AddParticle("AntiHyperhelium5", "AntiHyperhelium5", 4.839961, kFALSE,
                     2.5e-15, 6, "Ion", ionCode);
   }
 
@@ -319,6 +307,33 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
                     2.5e-15, 6, "Ion", ionCode);
   }
 
+  // 4-Xi-He
+  ionCode = 1120020040;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("4XiHe", "4XiHe", 4.128, kFALSE, 4.04e-15, 3, "Ion", ionCode);
+    db->AddAntiParticle("Anti4XiHe", -ionCode);
+  }
+
+  // 4-Xi-H
+  ionCode = 1120010040;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("4XiH", "4XiH", 4.128, kFALSE, 4.04e-15, 3, "Ion", ionCode);
+    db->AddAntiParticle("Anti4XiH", -ionCode);
+  }
+
+  // hyper helium 4 sigma
+  ionCode = 1110020040;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Hyperhelium4sigma", "Hyperhelium4sigma", 3.995, kFALSE,
+                    2.5e-15, 6, "Ion", ionCode);
+  }
+  // anti-hyper helium 4 sigma
+  ionCode = -1110020040;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("AntiHyperhelium4sigma", "AntiHyperhelium4sigma", 3.995, kFALSE,
+                    2.5e-15, 6, "Ion", ionCode);
+  }
+
   ionCode = 1010000020;
   if (!db->GetParticle(ionCode)) {
     db->AddParticle("LambdaNeutron", "LambdaNeutron", 2.054, kFALSE,
@@ -393,14 +408,14 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
 
   ionCode = 1060020020;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("OmegaOmega", "OmegaOmega", 3.229, kFALSE,
-                    2.5e-15, 6, "Special", ionCode);
+    db->AddParticle("OmegaOmega", "OmegaOmega", 3.343, kFALSE,
+                    8.01e-15, 6, "Special", ionCode);
   }
 
   ionCode = -1060020020;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiOmegaOmega", "AntiOmegaOmega", 3.229, kFALSE,
-                    2.5e-15, 6, "Special", ionCode);
+    db->AddParticle("AntiOmegaOmega", "AntiOmegaOmega", 3.343, kFALSE,
+                    8.01e-15, 6, "Special", ionCode);
   }
 
   ionCode = 1010010021;
@@ -455,6 +470,102 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
                     0.185, 0, "Resonance", ionCode);
   }
 
+  // Lambda(1405)0
+  ionCode = 102132;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Lambda_1405_0", "Lambda_1405_0", 1.405, kFALSE, 0.05, 0, "Resonance", ionCode);
+  }
+  if (!db->GetParticle(-ionCode)) {
+    db->AddParticle("AntiLambda_1405_0", "AntiLambda_1405_0", 1.405, kFALSE, 0.05, 0, "Resonance", -ionCode);
+  }
+
+  // Lambda(1520)0
+  ionCode = 102134;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Lambda_1520_0", "Lambda_1520_0", 1.5195, kFALSE, 0.0156, 0, "Resonance", ionCode);
+  }
+  if (!db->GetParticle(-ionCode)) {
+    db->AddParticle("AntiLambda_1520_0", "AntiLambda_1520_0", 1.5195, kFALSE, 0.0156, 0, "Resonance", -ionCode);
+  }
+
+  // f1 study
+  ionCode = 20223;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f1_1285", "f1_1285", 1.28210, kFALSE, 0.02420, 0, "Resonance", ionCode);
+  }
+  ionCode = 20333;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f1_1420", "f1_1420", 1.42640, kFALSE, 0.05490, 0, "Resonance", ionCode);
+  }
+
+  // glueball hunting
+  ionCode = 225;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f2_1270", "f2_1270", 1.2754, kFALSE, 0.1858, 0, "Resonance", ionCode);
+  }
+  ionCode = 115;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("a2_1320", "a2_1320", 1.3182, kFALSE, 0.1078, 0, "Resonance", ionCode);
+  }
+  ionCode = 10221;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f0_1370", "f0_1370", 1.37, kFALSE, 0.200, 0, "Resonance", ionCode);
+  }
+  ionCode = 9030221;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f0_1500", "f0_1500", 1.500, kFALSE, 0.112, 0, "Resonance", ionCode);
+  }
+  ionCode = 10331;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f0_1710", "f0_1710", 1.710, kFALSE, 0.139, 0, "Resonance", ionCode);
+  }
+  ionCode = 335;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f2_1525", "f2_1525", 1.525, kFALSE, 0.073, 0, "Resonance", ionCode);
+  }
+
+  // when using hadronic rescattering in Pythia8
+  ionCode = 9000221;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f0_500", "f0_500", 0.500, kFALSE, 0.350, 0.0, "Resonance", ionCode);
+  }
+  ionCode = -100313;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("K*(1410)bar0", "K*(1410)bar0", 1.414, kFALSE, 0.232, 0, "Resonance", ionCode);
+  }
+  ionCode = 100313;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("K*(1410)0", "K*(1410)0", 1.414, kFALSE, 0.232, 0, "Resonance", ionCode);
+  }
+  ionCode = 100323;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("K*(1410)+", "K*(1410)+", 1.414, kFALSE, 0.232, +1, "Resonance", ionCode);
+  }
+  ionCode = -100323;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("K*(1410)-", "K*(1410)-", 1.414, kFALSE, 0.232, -1, "Resonance", ionCode);
+  }
+  ionCode = 100211;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("pi(1300)+", "pi(1300)+", 1.165, kFALSE, 0.400, +1, "Resonance", ionCode);
+  }
+  ionCode = -100211;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("pi(1300)-", "pi(1300)-", 1.165, kFALSE, 0.400, -1, "Resonance", ionCode);
+  }
+  ionCode = 202112;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("n(1440)0", "n(1440)0", 1.358, kFALSE, 0.350, 0, "Ion", ionCode);
+  }
+  ionCode = -202212;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("p(1440)bar-", "p(1440)bar-", 1.793, kFALSE, 0.350, -1, "Ion", ionCode);
+  }
+  ionCode = 202212;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("p(1440)+", "p(1440)+", 1.793, kFALSE, 0.350, 1, "Ion", ionCode);
+  }
+
   // Xi-/+ (1820)
   ionCode = 123314;
   if (!db->GetParticle(ionCode)) {
@@ -580,10 +691,62 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
     db->AddParticle("Anti-Theta_c_3100", "Anti-Theta_c_3100", 3.099, kFALSE,
                     83.e-6, 0, "Resonance", ionCode); // same width as D*+ (83 keV)
   }
-  if (!db->GetParticle(-ionCode)) {
-    db->AddParticle("Theta_c_3100", "Theta_c_3100", 3.099, kFALSE,
-                    83.e-6, 0, "Resonance", -ionCode); // same width as D*+ (83 keV)
+  db->AddAntiParticle("Theta_c_3100", -ionCode);
+
+  // Charm resonances not present in PYTHIA (consistent with https://github.com/AliceO2Group/O2DPG/blob/master/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_DResoTrigger.cfg)
+  // Mesons
+  ionCode = 30433;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("D*_s1_Plus_2700", "D*_s1_Plus_2700", 2.714, false, 0.122, 3, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("D*_s1_Minus_2700", -ionCode);
+  ionCode = 40433;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("D*_s1_Plus_2860", "D*_s1_Plus_2860", 2.859, false, 0.160, 3, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("D*_s1_Minus_2860", -ionCode);
+  ionCode = 437;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("D*_s3_Plus_2860", "D*_s3_Plus_2860", 2.860, false, 0.053, 3, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("D*_s3_Minus_2860", -ionCode);
+
+  // Baryons
+  ionCode = 4325;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Xi_c_Plus_3055", "Xi_c_Plus_3055", 3.0559, false, 0.0078, 3, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("Xi_c_Minus_3055", -ionCode);
+  ionCode = 4326;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Xi_c_Plus_3080", "Xi_c_Plus_3080", 3.0772, false, 0.0036, 3, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("Xi_c_Minus_3080", -ionCode);
+  ionCode = 4315;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Xi_c_0_3055", "Xi_c_0_3055", 3.0590, false, 0.0064, 0, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("Anti-Xi_c_0_3055", -ionCode);
+  ionCode = 4316;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Xi_c_0_3080", "Xi_c_0_3080", 3.0799, false, 0.0056, 0, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("Anti-Xi_c_0_3080", -ionCode);
+  ionCode = 24124;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Lambda_c_Plus_2860", "Lambda_c_Plus_2860", 2.8561, false, 0.0680, 0, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("Anti-Lambda_c_Minus_2860", -ionCode);
+  ionCode = 24126;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Lambda_c_Plus_2880", "Lambda_c_Plus_2880", 2.8816, false, 0.0056, 0, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("Anti-Lambda_c_Minus_2880", -ionCode);
+  ionCode = 4125;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Lambda_c_Plus_2940", "Lambda_c_Plus_2940", 2.9396, false, 0.0200, 0, "Resonance", ionCode);
   }
+  db->AddAntiParticle("Anti-Lambda_c_Minus_2940", -ionCode);
 
   // d*(2380) - dibaryon resonance
 
@@ -603,6 +766,26 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
   if (!db->GetParticle(-ionCode)) {
     db->AddParticle("AntiSexaquark", "AntiSexaquark", 2.0, kTRUE, 0.0, 0, "Special", -ionCode);
   }
+
+  // lastly, add particle from the the extra text file
+  addParticlesFromExternalFile(db);
+}
+
+inline void O2DatabasePDG::addParticlesFromExternalFile(TDatabasePDG* db)
+{
+  static bool initialized = false;
+  if (!initialized) {
+    // allow user to specify custom file
+    if (const char* custom = std::getenv("O2_SIM_CUSTOM_PDG")) {
+      // TODO: make sure this is a file
+      db->ReadPDGTable(custom);
+    } else if (const char* o2Root = std::getenv("O2_ROOT")) {
+      // take the maintained file from O2
+      auto inputExtraPDGs = std::string(o2Root) + "/share/Detectors/gconfig/data/extra_ions_pdg_table.dat";
+      db->ReadPDGTable(inputExtraPDGs.c_str());
+    }
+    initialized = true;
+  }
 }
 
 } // namespace o2
diff --git a/DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h b/DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h
deleted file mode 100644
index 150a8272c7714..0000000000000
--- a/DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
-// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
-// All rights not expressly granted are reserved.
-//
-// This software is distributed under the terms of the GNU General Public
-// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
-//
-// In applying this license CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-/// \file ProcessingEventInfo.h
-/// \brief Encapsulated meta information about current event being processed by FairRoot (analysis) tasks
-/// \author Sandro Wenzel
-
-#ifndef ALICEO2_DATA_EVENTINFO_H_
-#define ALICEO2_DATA_EVENTINFO_H_
-
-namespace o2
-{
-
-// A class encapsulating meta information about events being process
-// and the data being sent by run classes such as FairRunAna.
-// Can be send to processing tasks for usage so that they do no longer
-// need to access the FairRootManager directly.
-struct ProcessingEventInfo {
-  double eventTime;   //! time of the current event
-  int eventNumber;    //! the current entry
-  int sourceNumber;   //! the current source number
-  bool numberSources; //! number of sources
-  // can be extended further
-};
-
-} // namespace o2
-
-#endif
diff --git a/DataFormats/simulation/include/SimulationDataFormat/TrackReference.h b/DataFormats/simulation/include/SimulationDataFormat/TrackReference.h
index f3d41a17208f0..34d1c57aa9f0b 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/TrackReference.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/TrackReference.h
@@ -171,7 +171,7 @@ class TrackReference
   float mTrackLength = 0; ///< track length from its origin in cm
   float mTof = 0;         ///< time of flight in cm
   Int_t mUserId = 0;      ///< optional Id defined by user
-  Int_t mDetectorId = 0;  ///< Detector Id
+  Int_t mDetectorId = -1; ///< sensitive Detector Id (-1 if unknown or in passive material)
   SimTrackStatus mStatus; ///< encoding the track status
 
   friend std::ostream& operator<<(std::ostream&, const TrackReference&);
diff --git a/DataFormats/simulation/src/DigitizationContext.cxx b/DataFormats/simulation/src/DigitizationContext.cxx
index f3f40d77042a5..79e36aa9fa48b 100644
--- a/DataFormats/simulation/src/DigitizationContext.cxx
+++ b/DataFormats/simulation/src/DigitizationContext.cxx
@@ -19,6 +19,7 @@
 #include  // for iota
 #include 
 #include 
+#include 
 
 using namespace o2::steer;
 
@@ -48,6 +49,27 @@ void DigitizationContext::printCollisionSummary(bool withQED, int truncateOutput
     }
   } else {
     std::cout << "Number of Collisions " << mEventRecords.size() << "\n";
+    if (mEventPartsWithQED.size() > 0) {
+      auto num_qed_events = mEventPartsWithQED.size() - mEventRecords.size();
+      if (num_qed_events > 0) {
+        std::cout << "Number of QED events (but not shown) " << num_qed_events << "\n";
+        // find first and last QED collision so that we can give the range in orbits where these
+        // things are included
+        auto firstQEDcoll_iter = std::find_if(mEventPartsWithQED.begin(), mEventPartsWithQED.end(),
+                                              [](const std::vector& vec) {
+                                                return std::find_if(vec.begin(), vec.end(), [](EventPart const& p) { return p.sourceID == 99; }) != vec.end();
+                                              });
+
+        auto lastColl_iter = std::find_if(mEventPartsWithQED.rbegin(), mEventPartsWithQED.rend(),
+                                          [](const std::vector& vec) {
+                                            return std::find_if(vec.begin(), vec.end(), [](EventPart const& p) { return p.sourceID == 99; }) != vec.end();
+                                          });
+
+        auto firstindex = std::distance(mEventPartsWithQED.begin(), firstQEDcoll_iter);
+        auto lastindex = std::distance(mEventPartsWithQED.begin(), lastColl_iter.base()) - 1;
+        std::cout << "QED from: " << mEventRecordsWithQED[firstindex] << " ---> " << mEventRecordsWithQED[lastindex] << "\n";
+      }
+    }
     for (int i = 0; i < mEventRecords.size(); ++i) {
       if (truncateOutputTo >= 0 && i > truncateOutputTo) {
         std::cout << "--- Output truncated to " << truncateOutputTo << " ---\n";
@@ -77,6 +99,14 @@ bool DigitizationContext::initSimChains(o2::detectors::DetID detid, std::vector<
     return false;
   }
 
+  // check that all files are present, otherwise quit
+  for (int source = 0; source < mSimPrefixes.size(); ++source) {
+    if (!std::filesystem::exists(o2::base::DetectorNameConf::getHitsFileName(detid, mSimPrefixes[source].data()))) {
+      LOG(info) << "Not hit file present for " << detid.getName() << " (exiting SimChain setup)";
+      return false;
+    }
+  }
+
   simchains.emplace_back(new TChain("o2sim"));
   // add the main (background) file
   simchains.back()->AddFile(o2::base::DetectorNameConf::getHitsFileName(detid, mSimPrefixes[0].data()).c_str());
@@ -121,6 +151,19 @@ bool DigitizationContext::initSimKinematicsChains(std::vector& simkinem
     // add signal files
     simkinematicschains.back()->AddFile(o2::base::NameConf::getMCKinematicsFileName(mSimPrefixes[source].data()).c_str());
   }
+
+  // we add QED, if used in the digitization context
+  if (mEventRecordsWithQED.size() > 0) {
+    if (mSimPrefixes.size() >= QEDSOURCEID) {
+      LOG(fatal) << "Too many signal chains; crashes with QED source ID";
+    }
+
+    // it might be better to use an unordered_map for the simchains but this requires interface changes
+    simkinematicschains.resize(QEDSOURCEID + 1, nullptr);
+    simkinematicschains[QEDSOURCEID] = new TChain("o2sim");
+    simkinematicschains[QEDSOURCEID]->AddFile(o2::base::DetectorNameConf::getMCKinematicsFileName(mQEDSimPrefix).c_str());
+  }
+
   return true;
 }
 
@@ -196,10 +239,52 @@ o2::parameters::GRPObject const& DigitizationContext::getGRP() const
 
 void DigitizationContext::saveToFile(std::string_view filename) const
 {
+  // checks if the path content of filename exists ... otherwise it is created before creating the ROOT file
+  auto ensure_path_exists = [](std::string_view filename) {
+    try {
+      // Extract the directory path from the filename
+      std::filesystem::path file_path(filename);
+      std::filesystem::path dir_path = file_path.parent_path();
+
+      // Check if the directory path is empty (which means filename was just a name without path)
+      if (dir_path.empty()) {
+        // nothing to do
+        return true;
+      }
+
+      // Create directories if they do not exist
+      if (!std::filesystem::exists(dir_path)) {
+        if (std::filesystem::create_directories(dir_path)) {
+          // std::cout << "Directories created successfully: " << dir_path.string() << std::endl;
+          return true;
+        } else {
+          std::cerr << "Failed to create directories: " << dir_path.string() << std::endl;
+          return false;
+        }
+      }
+      return true;
+    } catch (const std::filesystem::filesystem_error& ex) {
+      std::cerr << "Filesystem error: " << ex.what() << std::endl;
+      return false;
+    } catch (const std::exception& ex) {
+      std::cerr << "General error: " << ex.what() << std::endl;
+      return false;
+    }
+  };
+
+  if (!ensure_path_exists(filename)) {
+    LOG(error) << "Filename contains path component which could not be created";
+    return;
+  }
+
   TFile file(filename.data(), "RECREATE");
-  auto cl = TClass::GetClass(typeid(*this));
-  file.WriteObjectAny(this, cl, "DigitizationContext");
-  file.Close();
+  if (file.IsOpen()) {
+    auto cl = TClass::GetClass(typeid(*this));
+    file.WriteObjectAny(this, cl, "DigitizationContext");
+    file.Close();
+  } else {
+    LOG(error) << "Could not write to file " << filename.data();
+  }
 }
 
 DigitizationContext* DigitizationContext::loadFromFile(std::string_view filename)
@@ -217,6 +302,11 @@ DigitizationContext* DigitizationContext::loadFromFile(std::string_view filename
 
 void DigitizationContext::fillQED(std::string_view QEDprefix, int max_events, double qedrate)
 {
+  if (mEventRecords.size() <= 1) {
+    // nothing to do
+    return;
+  }
+
   o2::steer::InteractionSampler qedInteractionSampler;
   qedInteractionSampler.setBunchFilling(mBCFilling);
 
@@ -337,9 +427,67 @@ std::vector> getTimeFrameBoundaries(std::vector(left, right - 1));
   return result;
 }
+
+// a common helper for timeframe structure - includes indices for orbits-early (orbits from last timeframe still affecting current one)
+std::vector> getTimeFrameBoundaries(std::vector const& irecords,
+                                                              long startOrbit,
+                                                              long orbitsPerTF,
+                                                              float orbitsEarly)
+{
+  // we could actually use the other method first ... then do another pass to fix the early-index ... or impact index
+  auto true_indices = getTimeFrameBoundaries(irecords, startOrbit, orbitsPerTF);
+
+  std::vector> indices_with_early{};
+  for (int ti = 0; ti < true_indices.size(); ++ti) {
+    // for each timeframe we copy the true indices
+    auto& tf_range = true_indices[ti];
+
+    // init new index without fixing the early index yet
+    indices_with_early.push_back(std::make_tuple(tf_range.first, tf_range.second, -1));
+
+    // from the second timeframe on we can determine the index in the previous timeframe
+    // which matches our criterion
+    if (orbitsEarly > 0. && ti > 0) {
+      auto& prev_tf_range = true_indices[ti - 1];
+      // in this range search the smallest index which precedes
+      // timeframe ti by not more than "orbitsEarly" orbits
+      // (could probably use binary search, in case optimization becomes necessary)
+      int earlyOrbitIndex = -1; // init to start of this timeframe ... there may not be early orbits
+
+      // this is the orbit of the ti-th timeframe start
+      auto orbit_timeframe_start = startOrbit + ti * orbitsPerTF;
+
+      auto orbit_timeframe_early_fractional = 1. * orbit_timeframe_start - orbitsEarly;
+      auto orbit_timeframe_early_integral = static_cast(std::floor(orbit_timeframe_early_fractional));
+
+      auto bc_early = (uint32_t)((orbit_timeframe_early_fractional - orbit_timeframe_early_integral) * o2::constants::lhc::LHCMaxBunches);
+
+      // this is the interaction record of the ti-th timeframe start
+      o2::InteractionRecord timeframe_start_record(0, orbit_timeframe_start);
+      // this is the interaction record in some previous timeframe after which interactions could still
+      // influence the ti-th timeframe according to orbitsEarly
+      o2::InteractionRecord timeframe_early_record(bc_early, orbit_timeframe_early_integral);
+
+      auto differenceInBCNS_max = timeframe_start_record.differenceInBCNS(timeframe_early_record);
+
+      for (int j = prev_tf_range.second; j >= prev_tf_range.first; --j) {
+        // determine difference in timing in NS; compare that with the limit given by orbitsEarly
+        auto timediff_NS = timeframe_start_record.differenceInBCNS(irecords[j]);
+        if (timediff_NS < differenceInBCNS_max) {
+          earlyOrbitIndex = j;
+        } else {
+          break;
+        }
+      }
+      std::get<2>(indices_with_early.back()) = earlyOrbitIndex;
+    }
+  }
+  return indices_with_early;
+}
+
 } // namespace
 
-void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPerTF, int maxColl)
+void DigitizationContext::applyMaxCollisionFilter(std::vector>& timeframeindices, long startOrbit, long orbitsPerTF, int maxColl, double orbitsEarly)
 {
   // the idea is to go through each timeframe and throw away collisions beyond a certain count
   // then the indices should be condensed
@@ -347,9 +495,6 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe
   std::vector> newparts;
   std::vector newrecords;
 
-  // get a timeframe boundary indexing
-  auto timeframeindices = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF);
-
   std::unordered_map currMaxId;                           // the max id encountered for a source
   std::unordered_map> reIndexMap; // for each source, a map of old to new index for the event parts
 
@@ -357,12 +502,51 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe
     maxColl = mEventRecords.size();
   }
 
+  // the actual first actual timeframe
+  int first_timeframe = orbitsEarly > 0. ? 1 : 0;
+
+  // mapping of old to new indices
+  std::unordered_map indices_old_to_new;
+
   // now we can go through the structure timeframe by timeframe
-  for (auto timeframe : timeframeindices) {
-    auto firstindex = timeframe.first;
-    auto lastindex = timeframe.second;
+  for (int tf_id = first_timeframe; tf_id < timeframeindices.size(); ++tf_id) {
+    auto& tf_indices = timeframeindices[tf_id];
+
+    auto firstindex = std::get<0>(tf_indices); // .first;
+    auto lastindex = std::get<1>(tf_indices);  // .second;
+    auto previndex = std::get<2>(tf_indices);
+
+    LOG(info) << "timeframe indices " << previndex << " : " << firstindex << " : " << lastindex;
+
+    int collCount = 0; // counting collisions within timeframe
     // copy to new structure
-    for (int index = firstindex; index <= std::min(lastindex, firstindex + maxColl - 1); ++index) {
+    for (int index = previndex >= 0 ? previndex : firstindex; index <= lastindex; ++index) {
+      if (collCount >= maxColl) {
+        continue;
+      }
+
+      // look if this index was already done?
+      // avoid duplicate entries in transformed records
+      if (indices_old_to_new.find(index) != indices_old_to_new.end()) {
+        continue;
+      }
+
+      // we put these events under a certain condition
+      bool keep = index < firstindex || collCount < maxColl;
+
+      if (!keep) {
+        continue;
+      }
+
+      if (index >= firstindex) {
+        collCount++;
+      }
+
+      // we must also make sure that we don't duplicate the records
+      // moreover some records are merely put as precoll of tf2 ---> so they shouldn't be part of tf1 in the final
+      // extraction, ouch !
+      // maybe we should combine the filter and individual tf extraction in one step !!
+      indices_old_to_new[index] = newrecords.size();
       newrecords.push_back(mEventRecords[index]);
       newparts.push_back(mEventParts[index]);
 
@@ -384,6 +568,19 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe
           currMaxId[source] += 1;
         }
       }
+    } // ends one timeframe
+
+    // correct the timeframe indices
+    if (indices_old_to_new.find(firstindex) != indices_old_to_new.end()) {
+      std::get<0>(tf_indices) = indices_old_to_new[firstindex]; // start
+    }
+    if (indices_old_to_new.find(lastindex) != indices_old_to_new.end()) {
+      std::get<1>(tf_indices) = indices_old_to_new[lastindex]; // end;
+    } else {
+      std::get<1>(tf_indices) = newrecords.size() - 1; // end; -1 since index inclusif
+    }
+    if (indices_old_to_new.find(previndex) != indices_old_to_new.end()) {
+      std::get<2>(tf_indices) = indices_old_to_new[previndex]; // previous or "early" index
     }
   }
   // reassignment
@@ -391,13 +588,10 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe
   mEventParts = newparts;
 }
 
-void DigitizationContext::finalizeTimeframeStructure(long startOrbit, long orbitsPerTF)
+std::vector> DigitizationContext::calcTimeframeIndices(long startOrbit, long orbitsPerTF, double orbitsEarly) const
 {
-  mTimeFrameStartIndex = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF);
-  LOG(info) << "Fixed " << mTimeFrameStartIndex.size() << " timeframes ";
-  for (auto p : mTimeFrameStartIndex) {
-    LOG(info) << p.first << " " << p.second;
-  }
+  auto timeframeindices = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF, orbitsEarly);
+  return timeframeindices;
 }
 
 std::unordered_map DigitizationContext::getCollisionIndicesForSource(int source) const
@@ -436,6 +630,17 @@ struct pair_hash {
 };
 } // namespace
 
+int DigitizationContext::setInteractionVertices(std::vector> const& external_vertices)
+{
+  if (external_vertices.size() != mEventRecords.size()) {
+    LOG(error) << "Size mismatch with event record";
+    return 1;
+  }
+  mInteractionVertices.clear();
+  std::copy(external_vertices.begin(), external_vertices.end(), std::back_inserter(mInteractionVertices));
+  return 0;
+}
+
 void DigitizationContext::sampleInteractionVertices(o2::dataformats::MeanVertexObject const& meanv)
 {
   // mapping of source x event --> index into mInteractionVertices
@@ -483,3 +688,63 @@ void DigitizationContext::sampleInteractionVertices(o2::dataformats::MeanVertexO
     }
   }
 }
+
+DigitizationContext DigitizationContext::extractSingleTimeframe(int timeframeid, std::vector> const& timeframeindices, std::vector const& sources_to_offset)
+{
+  DigitizationContext r; // make a return object
+  if (timeframeindices.size() == 0) {
+    LOG(error) << "Timeframe index structure empty; Returning empty object.";
+    return r;
+  }
+  r.mSimPrefixes = mSimPrefixes;
+  r.mMuBC = mMuBC;
+  r.mBCFilling = mBCFilling;
+  r.mDigitizerInteractionRate = mDigitizerInteractionRate;
+  try {
+    auto tf_ranges = timeframeindices.at(timeframeid);
+
+    auto startindex = std::get<0>(tf_ranges);
+    auto endindex = std::get<1>(tf_ranges) + 1;
+    auto earlyindex = std::get<2>(tf_ranges);
+
+    if (earlyindex >= 0) {
+      startindex = earlyindex;
+    }
+    std::copy(mEventRecords.begin() + startindex, mEventRecords.begin() + endindex, std::back_inserter(r.mEventRecords));
+    std::copy(mEventParts.begin() + startindex, mEventParts.begin() + endindex, std::back_inserter(r.mEventParts));
+    if (mInteractionVertices.size() >= endindex) {
+      std::copy(mInteractionVertices.begin() + startindex, mInteractionVertices.begin() + endindex, std::back_inserter(r.mInteractionVertices));
+    }
+
+    // let's assume we want to fix the ids for source = source_id
+    // Then we find the first index that has this source_id and take the corresponding number
+    // as offset. Thereafter we subtract this offset from all known event parts.
+    auto perform_offsetting = [&r](int source_id) {
+      auto indices_for_source = r.getCollisionIndicesForSource(source_id);
+      int minvalue = std::numeric_limits::max();
+      for (auto& p : indices_for_source) {
+        if (p.first < minvalue) {
+          minvalue = p.first;
+        }
+      }
+      // now fix them
+      for (auto& p : indices_for_source) {
+        auto index_into_mEventParts = p.second;
+        for (auto& part : r.mEventParts[index_into_mEventParts]) {
+          if (part.sourceID == source_id) {
+            part.entryID -= minvalue;
+          }
+        }
+      }
+    };
+    for (auto source_id : sources_to_offset) {
+      perform_offsetting(source_id);
+    }
+
+  } catch (std::exception) {
+    LOG(warn) << "No such timeframe id in collision context. Returing empty object";
+  }
+  // fix number of collisions
+  r.setNCollisions(r.mEventRecords.size());
+  return r;
+}
diff --git a/DataFormats/simulation/src/InteractionSampler.cxx b/DataFormats/simulation/src/InteractionSampler.cxx
index 1936bf7dc06a9..f3ece5c51f90b 100644
--- a/DataFormats/simulation/src/InteractionSampler.cxx
+++ b/DataFormats/simulation/src/InteractionSampler.cxx
@@ -115,8 +115,8 @@ const o2::InteractionTimeRecord& InteractionSampler::generateCollisionTime()
 int InteractionSampler::simulateInteractingBC()
 {
   // Returns number of collisions assigned to selected BC
-
   nextCollidingBC(mBCJumpGenerator.getNextValue());
+
   // once BC is decided, enforce at least one interaction
   int ncoll = mNCollBCGenerator.getNextValue();
 
@@ -130,6 +130,24 @@ int InteractionSampler::simulateInteractingBC()
   return ncoll;
 }
 
+//_________________________________________________
+int FixedSkipBC_InteractionSampler::simulateInteractingBC()
+{
+  // Returns number of collisions assigned to selected BC
+
+  nextCollidingBC(mEveryN);  // we jump regular intervals
+  int ncoll = mMultiplicity; // well defined pileup
+
+  // assign random time withing a bunch
+  for (int i = ncoll; i--;) {
+    mTimeInBC.push_back(mCollTimeGenerator.getNextValue());
+  }
+  if (ncoll > 1) { // sort in DECREASING time order (we are reading vector from the end)
+    std::sort(mTimeInBC.begin(), mTimeInBC.end(), [](const float a, const float b) { return a > b; });
+  }
+  return ncoll;
+}
+
 //_________________________________________________
 void InteractionSampler::setBunchFilling(const std::string& bcFillingFile)
 {
@@ -144,3 +162,101 @@ void InteractionSampler::setBunchFilling(const std::string& bcFillingFile)
   mBCFilling = *bc;
   delete bc;
 }
+
+// ________________________________________________
+bool NonUniformMuInteractionSampler::setBCIntensityScales(const std::vector& scales_from_vector)
+{
+  // Sets the intensity scales per bunch crossing index
+  // The length of this vector needs to be compatible with the bunch filling chosen
+  mBCIntensityScales = scales_from_vector;
+
+  if (scales_from_vector.size() != mInteractingBCs.size()) {
+    LOG(error) << "Scaling factors and bunch filling scheme are not compatible. Not doing anything";
+    return false;
+  }
+
+  float sum = 0.;
+  for (auto v : mBCIntensityScales) {
+    sum += std::abs(v);
+  }
+  if (sum == 0) {
+    LOGP(warn, "total intensity is 0, assuming uniform");
+    for (auto& v : mBCIntensityScales) {
+      v = 1.f;
+    }
+  } else { // normalize
+    float norm = mBCIntensityScales.size() / sum;
+    for (auto& v : mBCIntensityScales) {
+      v = std::abs(v) * norm;
+    }
+  }
+  return false;
+}
+
+// ________________________________________________
+
+bool NonUniformMuInteractionSampler::setBCIntensityScales(const TH1F& hist)
+{
+  return setBCIntensityScales(determineBCIntensityScalesFromHistogram(hist));
+}
+
+std::vector NonUniformMuInteractionSampler::determineBCIntensityScalesFromHistogram(const TH1F& hist)
+{
+  if (mInteractingBCs.size() == 0) {
+    LOG(error) << " Initialize bunch crossing scheme before assigning scales";
+  }
+  std::vector scales;
+  // we go through the BCs and query the count from histogram
+  for (auto bc : mInteractingBCs) {
+    scales.push_back(hist.GetBinContent(bc + 1));
+  }
+  return scales;
+}
+
+int NonUniformMuInteractionSampler::getBCJump() const
+{
+  auto muFunc = [this](int bc_position) {
+    return mBCIntensityScales[bc_position % mInteractingBCs.size()] * mMuBC;
+  };
+
+  double U = gRandom->Rndm();    // uniform (0,1)
+  double T = -std::log(1.0 - U); // threshold
+  double sumMu = 0.0;
+  int offset = 0;
+  auto bcStart = mCurrBCIdx; // the current bc
+
+  while (sumMu < T) {
+    auto mu_here = muFunc(bcStart + offset); // mu at next BC
+    sumMu += mu_here;
+    if (sumMu >= T) {
+      break; // found BC with at least one collision
+    }
+    ++offset;
+  }
+  return offset;
+}
+
+int NonUniformMuInteractionSampler::simulateInteractingBC()
+{
+  nextCollidingBC(getBCJump());
+
+  auto muFunc = [this](int bc_position) {
+    return mBCIntensityScales[bc_position % mInteractingBCs.size()] * mMuBC;
+  };
+
+  // now sample number of collisions in chosenBC, conditioned >=1:
+  double mu_chosen = muFunc(mCurrBCIdx); // or does it need to be mCurrBCIdx
+  int ncoll = 0;
+  do {
+    ncoll = gRandom->Poisson(mu_chosen);
+  } while (ncoll == 0);
+
+  // assign random time withing a bunch
+  for (int i = ncoll; i--;) {
+    mTimeInBC.push_back(mCollTimeGenerator.getNextValue());
+  }
+  if (ncoll > 1) { // sort in DECREASING time order (we are reading vector from the end)
+    std::sort(mTimeInBC.begin(), mTimeInBC.end(), [](const float a, const float b) { return a > b; });
+  }
+  return ncoll;
+}
\ No newline at end of file
diff --git a/DataFormats/simulation/src/MCCompLabel.cxx b/DataFormats/simulation/src/MCCompLabel.cxx
index 8e4f2ad73f876..656fcd9770579 100644
--- a/DataFormats/simulation/src/MCCompLabel.cxx
+++ b/DataFormats/simulation/src/MCCompLabel.cxx
@@ -14,6 +14,7 @@
 #include 
 #include 
 #include 
+#include 
 
 namespace o2
 {
@@ -31,6 +32,16 @@ std::ostream& operator<<(std::ostream& os, MCCompLabel const& c)
   return os;
 }
 
+//_____________________________________________
+std::string MCCompLabel::asString() const
+{
+  // describe itself
+  if (isValid()) {
+    return fmt::format("[{}/{}{}/{:6d}]", getSourceID(), getEventID(), isFake() ? '-' : '+', getTrackID());
+  }
+  return isNoise() ? "[noise]" : "[unset]";
+}
+
 //_____________________________________________
 void MCCompLabel::print() const
 {
diff --git a/DataFormats/simulation/src/MCEventHeader.cxx b/DataFormats/simulation/src/MCEventHeader.cxx
index 99004fec46ffa..8a46d84805507 100644
--- a/DataFormats/simulation/src/MCEventHeader.cxx
+++ b/DataFormats/simulation/src/MCEventHeader.cxx
@@ -15,6 +15,7 @@
 #include "FairRootManager.h"
 #include 
 #include 
+#include 
 
 namespace o2
 {
@@ -61,6 +62,69 @@ void MCEventHeader::extractFileFromKinematics(std::string_view kinefilename, std
   }
 }
 
+void MCEventHeader::print() const
+{
+  // print some used parts from FairMCEventHeader
+  std::cout << "RunID " << fRunId << "\n";
+  std::cout << "EventID " << fEventId << "\n";
+  std::cout << "Vertex-X " << fX << "\n";
+  std::cout << "Vertex-Y " << fY << "\n";
+  std::cout << "Vertex-Z " << fZ << "\n";
+  std::cout << "Vertex-T " << fT << "\n";
+  std::cout << "Impact-B " << fB << "\n";
+  std::cout << "NPrim " << fNPrim << "\n";
+  // print meta-fields
+  printInfo();
+}
+
+/** alternative implementations below
+
+void MCEventHeader::extractFileFromKinematics(std::string_view kinefilename, std::string_view targetfilename) {
+  TFile kinefile(kinefilename.data(), "OPEN");
+  auto kinetree = static_cast(kinefile.Get("o2sim")); // belongs to "kinefile"
+  kinetree->SetBranchStatus("*", 0);
+  // activate header branch
+  kinetree->SetBranchStatus("MCEventHeader*", 1);
+  std::unique_ptr headeronlyfile{TFile::Open(targetfilename.data(), "RECREATE")};
+  auto headeronlytree = kinetree->CloneTree(0);
+
+  // copy the branches
+  headeronlytree->CopyEntries(kinetree, kinetree->GetEntries());
+  headeronlytree->SetEntries(kinetree->GetEntries());
+  // flush to disc
+  headeronlytree->Write();
+}
+
+void MCEventHeader::extractFileFromKinematics(std::string_view kinefilename, std::string_view targetfilename)
+{
+  TFile kinefile(kinefilename.data(), "OPEN");
+  auto kinetree = static_cast(kinefile.Get("o2sim")); // owned by "kinefile"
+  auto headerbranchorig = kinetree->GetBranch("MCEventHeader.");
+  if (!headerbranchorig) {
+    return;
+  }
+
+  std::unique_ptr headeronlyfile{TFile::Open(targetfilename.data(), "RECREATE")};
+  auto headeronlytree = new TTree("o2sim", "o2sim"); // owned by headeronlyfile
+  MCEventHeader* header = nullptr;
+  auto targetBranch = headeronlytree->Branch("MCEventHeader.", &header);
+  headerbranchorig->SetAddress(&header);
+
+  for (auto entry = 0; entry < kinetree->GetEntries(); ++entry) {
+    headerbranchorig->GetEntry(entry);
+    targetBranch->Fill();
+    if (header) {
+      delete header;
+      header = nullptr;
+    }
+  }
+  headeronlytree->SetEntries(kinetree->GetEntries());
+  // flush to disc
+  headeronlytree->Write();
+}
+
+*/
+
 /*****************************************************************/
 /*****************************************************************/
 
diff --git a/DataFormats/simulation/src/MCUtils.cxx b/DataFormats/simulation/src/MCUtils.cxx
index 8c1e242a5332b..14cf7ab7048a4 100644
--- a/DataFormats/simulation/src/MCUtils.cxx
+++ b/DataFormats/simulation/src/MCUtils.cxx
@@ -14,7 +14,7 @@
 //
 
 #include 
-#include 
+#include 
 
 namespace o2::mcutils
 {
diff --git a/DataFormats/simulation/src/SimulationDataLinkDef.h b/DataFormats/simulation/src/SimulationDataLinkDef.h
index 8a1e0c536c089..8f74bd757e791 100644
--- a/DataFormats/simulation/src/SimulationDataLinkDef.h
+++ b/DataFormats/simulation/src/SimulationDataLinkDef.h
@@ -24,6 +24,8 @@
 #pragma link off all functions;
 
 #pragma link C++ class o2::steer::InteractionSampler + ;
+#pragma link C++ class o2::steer::FixedSkipBC_InteractionSampler + ;
+#pragma link C++ class o2::steer::NonUniformMuInteractionSampler + ;
 #pragma link C++ class o2::sim::StackParam + ;
 #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::sim::StackParam> + ;
 #pragma link C++ class o2::MCTrackT < double> + ;
diff --git a/DataFormats/simulation/test/MCTrack.cxx b/DataFormats/simulation/test/MCTrack.cxx
index 699adc89abb8c..e857e2b88da41 100644
--- a/DataFormats/simulation/test/MCTrack.cxx
+++ b/DataFormats/simulation/test/MCTrack.cxx
@@ -24,10 +24,17 @@ using namespace o2;
 BOOST_AUTO_TEST_CASE(MCTrack_test)
 {
   MCTrack track;
+
+  // auxiliary lookup table needed to fetch and set hit properties
+  std::vector hitLUT(o2::detectors::DetID::nDetectors, -1);
+  // in this test we have a single fictional detector 1, which we map to
+  // the first bit
+  hitLUT[1] = 0;
+
   // check properties on default constructed object
   BOOST_CHECK(track.getStore() == false);
   for (auto i = o2::detectors::DetID::First; i < o2::detectors::DetID::nDetectors; ++i) {
-    BOOST_CHECK(track.leftTrace(i) == false);
+    BOOST_CHECK(track.leftTrace(i, hitLUT) == false);
   }
   BOOST_CHECK(track.getNumDet() == 0);
   BOOST_CHECK(track.hasHits() == false);
@@ -41,10 +48,10 @@ BOOST_AUTO_TEST_CASE(MCTrack_test)
   BOOST_CHECK(track.getStore() == true);
 
   // set hit for first detector
-  BOOST_CHECK(track.leftTrace(1) == false);
-  track.setHit(1);
+  BOOST_CHECK(track.leftTrace(1, hitLUT) == false);
+  track.setHit(hitLUT[1]);
   BOOST_CHECK(track.hasHits() == true);
-  BOOST_CHECK(track.leftTrace(1) == true);
+  BOOST_CHECK(track.leftTrace(1, hitLUT) == true);
   BOOST_CHECK(track.getNumDet() == 1);
 
   // check process encoding
diff --git a/DataFormats/simulation/test/testBasicHits.cxx b/DataFormats/simulation/test/testBasicHits.cxx
index e81c173fedae8..ccd16ae7a3671 100644
--- a/DataFormats/simulation/test/testBasicHits.cxx
+++ b/DataFormats/simulation/test/testBasicHits.cxx
@@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(BasicXYZHit_ROOTIO)
   }
 
   // same for double valued hits
-  using HitTypeD = BasicXYZEHit;
+  using HitTypeD = BasicXYZEHit;
   HitTypeD hitD(1., 2., 3., 0.01, -1.1, -1, 1);
 
   // try writing hit to a TBuffer
diff --git a/DataFormats/simulation/test/testInteractionSampler.cxx b/DataFormats/simulation/test/testInteractionSampler.cxx
new file mode 100644
index 0000000000000..b1b3691884ccf
--- /dev/null
+++ b/DataFormats/simulation/test/testInteractionSampler.cxx
@@ -0,0 +1,76 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#define BOOST_TEST_MODULE Test InteractionSampler class
+#define BOOST_TEST_MAIN
+#define BOOST_TEST_DYN_LINK
+
+#include 
+#include "SimulationDataFormat/InteractionSampler.h"
+#include "CCDB/BasicCCDBManager.h"
+#include "DataFormatsParameters/AggregatedRunInfo.h"
+#include "DataFormatsParameters/GRPLHCIFData.h"
+#include "TFile.h"
+#include "TGrid.h"
+#include 
+
+namespace o2
+{
+
+BOOST_AUTO_TEST_CASE(NonUniformSampler)
+{
+  auto run_number = 559827;
+  TGrid::Connect("alien");
+  if (gGrid) {
+    auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run_number);
+
+    o2::steer::NonUniformMuInteractionSampler sampler;
+    sampler.setBunchFilling(runInfo.grpLHC->getBunchFilling());
+
+    // the test distribution provided by Igor Altsybeev
+    auto distr_file = TFile::Open("alien:///alice/cern.ch/user/s/swenzel/AliceO2_TestData/NBcVTX_559827/hBcTVX_data_PbPb_24ar_559827.root");
+
+    //
+    if (distr_file && !distr_file->IsZombie()) {
+      auto hist = distr_file->Get("hBcTVX");
+      if (hist) {
+        sampler.init();
+        sampler.setBCIntensityScales(*hist);
+
+        // sample into a vector of a certain size
+        std::vector samples;
+
+        int N = 100000;
+        samples.resize(N);
+
+        sampler.generateCollisionTimes(samples);
+
+        // fill an output histogram
+        auto output_hist = (TH1F*)hist->Clone("h2"); // make a full copy
+        output_hist->Reset();
+
+        for (const auto& sample : samples) {
+          output_hist->Fill(sample.bc);
+        }
+
+        // Write out
+        auto fout = TFile::Open("NBCVTX_out.root", "RECREATE");
+        fout->WriteObject(output_hist, "NBcVTX");
+        fout->Close();
+
+        // compare mean values of original and newly sampled hist
+        BOOST_CHECK_CLOSE(hist->GetMean(), output_hist->GetMean(), 0.5);
+      }
+    }
+  }
+}
+
+} // namespace o2
diff --git a/DataFormats/simulation/test/testMCGenId.cxx b/DataFormats/simulation/test/testMCGenId.cxx
new file mode 100644
index 0000000000000..246268ac78d52
--- /dev/null
+++ b/DataFormats/simulation/test/testMCGenId.cxx
@@ -0,0 +1,50 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#define BOOST_TEST_MODULE Test MCGenId class
+#define BOOST_TEST_MAIN
+#define BOOST_TEST_DYN_LINK
+#include 
+#include "SimulationDataFormat/MCGenProperties.h"
+
+using namespace o2::mcgenid;
+
+BOOST_AUTO_TEST_CASE(MCGenId_test)
+{
+  // possible generator IDs range from 0 to 127 (included)
+  constexpr int highGenerator{128};
+  // possible sub-generator IDs range from -1 to 30 (included)
+  constexpr int highSubGenerator{31};
+  // possible soufce IDs range from 0 to 15 (included)
+  constexpr int highSource{16};
+
+  // test all combinations
+  for (int sourceId = 0; sourceId < highSource; sourceId++) {
+    for (int generatorId = 0; generatorId < highGenerator; generatorId++) {
+      for (int subGeneratorId = -1; subGeneratorId < highSubGenerator; subGeneratorId++) {
+        auto encoded = getEncodedGenId(generatorId, sourceId, subGeneratorId);
+        // decode them
+        auto sourceIdAfter = getSourceId(encoded);
+        auto generatorIdAfter = getGeneratorId(encoded);
+        auto subGeneratorIdAfter = getSubGeneratorId(encoded);
+
+        std::cout << "SourceID: " << sourceId << " ==> " << sourceIdAfter << "\n"
+                  << "generatorId: " << generatorId << " ==> " << generatorIdAfter << "\n"
+                  << "subGeneratorId: " << subGeneratorId << " ==> " << subGeneratorIdAfter << "\n";
+
+        // check if original and decoded numbers are the same
+        BOOST_CHECK(sourceIdAfter == sourceId);
+        BOOST_CHECK(generatorIdAfter == generatorId);
+        BOOST_CHECK(subGeneratorId == subGeneratorIdAfter);
+      }
+    }
+  }
+}
diff --git a/DataFormats/simulation/test/testMCGenStatus.cxx b/DataFormats/simulation/test/testMCGenStatus.cxx
index 6156f20b0fa47..a9b8b7277b3e7 100644
--- a/DataFormats/simulation/test/testMCGenStatus.cxx
+++ b/DataFormats/simulation/test/testMCGenStatus.cxx
@@ -15,7 +15,7 @@
 #include 
 #include "SimulationDataFormat/MCTrack.h"
 #include "SimulationDataFormat/ParticleStatus.h"
-#include "SimulationDataFormat/MCGenStatus.h"
+#include "SimulationDataFormat/MCGenProperties.h"
 #include "SimulationDataFormat/MCUtils.h"
 #include "TFile.h"
 #include "TParticle.h"
diff --git a/Detectors/AOD/CMakeLists.txt b/Detectors/AOD/CMakeLists.txt
index 5ec99773a5dc9..827b23b3e4cdd 100644
--- a/Detectors/AOD/CMakeLists.txt
+++ b/Detectors/AOD/CMakeLists.txt
@@ -19,6 +19,7 @@ target_link_libraries(
           O2::FDDWorkflow
           O2::FV0Workflow
           O2::Framework
+          O2::FrameworkAnalysisSupport
           O2::GlobalTracking
           O2::GlobalTrackingWorkflow
           O2::ITSMFTWorkflow
@@ -41,15 +42,15 @@ o2_add_executable(
   workflow
   COMPONENT_NAME aod-producer
   TARGETVARNAME targetName
-  SOURCES src/aod-producer-workflow.cxx src/AODProducerWorkflowSpec.cxx
-  PUBLIC_LINK_LIBRARIES internal::AODProducerWorkflow O2::Version
+  SOURCES src/aod-producer-workflow.cxx src/AODProducerWorkflowSpec.cxx src/AODMcProducerHelpers.cxx
+  PUBLIC_LINK_LIBRARIES internal::AODProducerWorkflow O2::Version nlohmann_json::nlohmann_json
 )
 
 o2_add_executable(
   workflow
   COMPONENT_NAME aod-mc-producer
-  SOURCES src/aod-mc-producer-workflow.cxx src/AODMcProducerWorkflowSpec.cxx
-  PUBLIC_LINK_LIBRARIES internal::AODProducerWorkflow O2::Version
+  SOURCES src/aod-mc-producer-workflow.cxx src/AODMcProducerWorkflowSpec.cxx src/AODMcProducerHelpers.cxx
+  PUBLIC_LINK_LIBRARIES internal::AODProducerWorkflow O2::Version nlohmann_json::nlohmann_json
 )
 
 o2_add_executable(
@@ -75,7 +76,9 @@ o2_add_executable(
         O2::DataFormatsFT0
         O2::Steer
         O2::ZDCBase
-)
+        O2::FrameworkAnalysisSupport
+        nlohmann_json::nlohmann_json
+        )
 
 if (OpenMP_CXX_FOUND)
   target_compile_definitions(${targetName} PRIVATE WITH_OPENMP)
diff --git a/Detectors/AOD/README.md b/Detectors/AOD/README.md
new file mode 100644
index 0000000000000..42a9840fabc04
--- /dev/null
+++ b/Detectors/AOD/README.md
@@ -0,0 +1,50 @@
+
+
+# AOD producers
+
+The code in this directory is meant to produce `AO2D.root` output
+files.   There are two such producers:
+
+- [`o2-aod-producer-workflow`](src/AODProducerWorkflowSpec.cxx) that
+  produces `AO2D.root` from reconstructed data (whether from
+  simulations or the experiment).  The output will contain
+  reconstructed tracks "calo" hits, and so on.
+- [`o2-aod-mc-producer-workflow`](src/AODMcProducerWorkflowSpec.cxx)
+  that produces `AO2D.root` from simulation data only. The output will
+  only contain the simulation particles and collision headers.
+
+## Do a full simulation to AO2D chain
+
+You need [`O2DPG`](https://github.com/AliceO2Group/O2DPG/) installed.
+
+Then, a script a la
+
+    #!/bin/bash
+    #
+    # A example workflow MC->RECO->AOD for a simple pp production
+    # excluding ZDC
+    NWORKERS=${NWORKERS:-6}
+    SIMENGINE=${SIMENGINE:-TGeant4}
+
+    ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py \
+                 -eCM 14000 \
+                 -col pp \
+                 -gen pythia8 \
+                 -proc "cdiff" \
+                 -tf 2  \
+                 -ns 50 \
+                 -e ${SIMENGINE} \
+                 -j ${NWORKERS} \
+                 -run 303000 \
+                 -seed 624 \
+                 -interactionRate 50000
+
+    ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod
+
+will set-up a chain and run it.  The above will make 2 time-frames,
+each with 50 pp collisions at sqrt(s)=14TeV generated by Pythia8.
+
+See also [this page](https://aliceo2group.github.io/simulation/) for more on simulations with O2.
+
diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerHelpers.h b/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerHelpers.h
new file mode 100644
index 0000000000000..5e9cd445b576b
--- /dev/null
+++ b/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerHelpers.h
@@ -0,0 +1,325 @@
+// Copyright 2023-2099 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+/// @file   AODMcProducerHelpers.h
+/// @author Christian Holm Christensen 
+/// common helpers for AOD MC producers
+
+#ifndef O2_AODMCPRODUCER_HELPERS
+#define O2_AODMCPRODUCER_HELPERS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/**
+ * Utilities to transform simulated data into AO2D tables.
+ *
+ * The function templates below are templated on the cursor type over
+ * the relevant AOD tables.  Such a table can be obtained from the
+ * ProcessingContext @c pc
+ *
+ * @code
+ * auto builder = pc.make(OutputForTable::ref());
+ * auto cursor  = builder->cursor
(); + * @endcode + * + * If the task uses the @c Produces
template, + * + * @code + * Produces
mTable; + * @endcode + * + * then a cursor is obtained via, + * + * @code + * auto cursor = mTable.cursor; + * @endcode + * + * Note that these functions cannot be moved into a compilation unit, + * because that would require deducing the table cursor type, by + * f.ex. + * + * @code + * template + * struct TableCursor { + * using cursor_t = decltype(std::declval() + * .cursor
()); + * }; + * using CollisionCursor = TableCursor:cursor_t; + * @endcode + * + * but since cursors are really Lambdas and Lambda types are specific + * to the compilation unit, then the implementation file (compilation + * unit) of these functions definitions and their use (another + * compilation unit) would have different types of the the cursers, + * and thus not be able to link. More information is given at + * https://stackoverflow.com/questions/50033797. + */ +namespace o2::aodmchelpers +{ +//================================================================== +/** + * Deduce cursor type and wrap in std::function + */ +template +struct TableCursor { + using type = decltype(framework::FFL(std::declval() + .cursor
())); +}; +//================================================================== +/** Cursor over aod::McCollisions */ +using CollisionCursor = TableCursor::type; +/** Cursor over aod::McParticles */ +using ParticleCursor = TableCursor::type; +/** Cursor over aod::HepMCXSections */ +using XSectionCursor = TableCursor::type; +/** Cursor over aod::HepMCPdfInfos */ +using PdfInfoCursor = TableCursor::type; +/** Cursor over aod::HepMCHeavyIons */ +using HeavyIonCursor = TableCursor::type; +//================================================================== +/** Types of updates on HepMC tables. */ +enum HepMCUpdate { + never, + always, + anyKey, + allKeys +}; + +//================================================================== +/** + * Check if header has keys. If the argument @a anyNotAll is true, + * then this member function returns true if @e any of the keys + * were found. If @a anyNotAll is false, then return true only if + * @a all keys were found. + * + * @param header MC event header + * @param keys Keys to look for + * @param anyNotAll If true, return true if @e any key was found. + * If false, return true only if @a all keys were found + * + * @return true if any or all keys were found + */ +bool hasKeys(o2::dataformats::MCEventHeader const& header, + const std::vector& keys, + bool anyNotall = true); +//-------------------------------------------------------------------- +/** + * Get a property from the header, or if not set or not valid, a + * default value. + * + * @param header The MC event header + * @param key Key to look for + * @param def Value to return if key is not found + * + * @return Value of key or def if key is not found + */ +template +const T getEventInfo(o2::dataformats::MCEventHeader const& header, + std::string const& key, + T const& def) +{ + if (not header.hasInfo(key)) + return def; + + bool isValid = false; + const T& val = header.getInfo(key, isValid); + if (not isValid) + return def; + + return val; +} +//==================================================================== +/** + * Fill in collision information. This is read from the passed MC + * header and stored in the MCCollision table. The member function + * returns the encoded generator ID. + * + * @param cursor Cursor over o2::aod::McCollisions table + * @param bcId Bunch-crossing Identifier + * @param time Time of collisions + * @param header Event header from generator + * @param generatorId Default generator + * @param sourceId Identifier of source + * + * @return encoded generator ID + */ +short updateMCCollisions(const CollisionCursor& cursor, + int bcId, + float time, + o2::dataformats::MCEventHeader const& header, + short generatorId = 0, + int sourceId = 0, + unsigned int mask = 0xFFFFFFF0); +//-------------------------------------------------------------------- +/** + * Fill in HepMC cross-section table from event generator header. + * + * @param cursor Cursor over o2::aod::HepMCXSections table + * @param collisionID Identifier of collision (as given updateMCCollision) + * @param generatorID Encoded generator ID + * @param header Event header from generator + * @param anyNotAll If true, then any key present trigger and update. + * If false, then all keys must be present to update + * the table. + * + * @return true if table was updated + */ +bool updateHepMCXSection(const XSectionCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when = HepMCUpdate::anyKey); +//-------------------------------------------------------------------- +/** + * Fill in HepMC parton distribution function table from event + * generator header + * + * @param cursor Cursor over o2::aod::HepMCXSections table + * @param collisionID Identifier of collision (as given updateMCCollision) + * @param generatorID Encoded generator ID + * @param header Event header from generator + * @param anyNotAll If true, then any key present trigger and update. + * If false, then all keys must be present to update + * the table. + * + * @return true if table was updated + */ +bool updateHepMCPdfInfo(const PdfInfoCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when = HepMCUpdate::anyKey); +//-------------------------------------------------------------------- +/** + * Fill in HepMC heavy-ion table from generator event header. + * + * @param cursor Cursor over o2::aod::HepMCXSections table + * @param collisionID Identifier of collision (as given updateMCCollision) + * @param generatorID Encoded generator ID + * @param header Event header from generator + * @param anyNotAll If true, then any key present trigger and update. + * If false, then all keys must be present to update + * the table. + * + * @return true if table was updated + */ +bool updateHepMCHeavyIon(const HeavyIonCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when = HepMCUpdate::anyKey); +//-------------------------------------------------------------------- +/** + * Type of mapping from track number to row index + */ +using TrackToIndex = std::unordered_map; +//-------------------------------------------------------------------- +/** + * Update aod::McParticles table with information from an MC track. + * + * @param cursor Cursor over aod::McParticles table + * @param mapping Maps track number to index in table + * @param collisionID Collision identifier + * @param track Track to update table with + * @param tracks List of all tracks of current collision + * @param flags Base flags of this track + * @param weightMask Mask on weight floating point value + * @param momentumMask Mask on momentum floating point values + * @param positionMask Mask on position floating point values + */ +void updateParticle(const ParticleCursor& cursor, + const TrackToIndex& toStore, + int collisionID, + o2::MCTrack const& track, + std::vector const& tracks, + uint8_t flags = 0, + uint32_t weightMask = 0xFFFFFFF0, + uint32_t momentumMask = 0xFFFFFFF0, + uint32_t positionMask = 0xFFFFFFF0); +//-------------------------------------------------------------------- +/** + * Update aod::McParticles table with tracks from MC. + * + * To add particles from many events, one will do + * + * @code + * TrackToIndex preselect = findMcTracksToStore(...); + * + * size_t offset = 0; + * for (auto event : events) + * offset = updateParticles(cursor, + * event.getCollisionID(), + * event.getTracks(), + * offset, + * filter, + * event.isBackground(), + * preselect); + * @endcode + * + * Here @a preselect must be a map from track number to a positive + * value. Tracks that are mapped as such in @a preselect are stored + * in addition to other tracks selected by the function. Note that @a + * preselect may be empty. + * + * If @a filter is false, then @a all tracks will be stored. + * + * If @a filter is true, then tracks that are + * + * - generated by the generator, + * - physical primaries + * (MCTrackNavigator::isPhysicalPrimary), + * - to be kept for physics + * (MCTrackNavigator::isKeepPhysics), or + * - is listed with a positive value in @a preselect, or + * - either a mother or daughter of one such track, then + * + * that track is kept + * + * On return, the @a preselect will map from track number (index in + * the @a tracks container) to the table row index (including offset + * from previous events in the same time-frame). + * + * @param cursor Cursor over aod::McParticles + * @param int Collision identifier + * @param tracks List of all tracks of current collision + * @param offset Index just beyond last table entry + * @param filter Filter tracks + * @param background True of from background event + * @param preselect Mapping of preselected tracks + * @param weightMask Mask on weight floating point value + * @param momentumMask Mask on momentum floating point values + * @param positionMask Mask on position floating point values + * + * @return Index beyond the last particle added to table + */ +uint32_t updateParticles(const ParticleCursor& cursor, + int collisionID, + std::vector const& tracks, + TrackToIndex& preselect, + uint32_t offset = 0, + bool filter = false, + bool background = false, + uint32_t weightMask = 0xFFFFFFF0, + uint32_t momentumMask = 0xFFFFFFF0, + uint32_t positionMask = 0xFFFFFFF0, + bool signalFilter = false); +} // namespace o2::aodmchelpers + +#endif /* O2_AODMCPRODUCER_HELPERS */ +// Local Variables: +// mode: C++ +// End: diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerWorkflowSpec.h index 392dad61df4b3..674fbf7bfcd05 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerWorkflowSpec.h @@ -15,14 +15,14 @@ #define O2_AODMCPRODUCER_WORKFLOW_SPEC #include "AODProducerHelpers.h" +#include "AODMcProducerHelpers.h" #include "CommonDataFormat/InteractionRecord.h" #include "Framework/AnalysisDataModel.h" #include "Framework/AnalysisHelpers.h" -#include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" #include "Steer/MCKinematicsReader.h" -#include "TMap.h" -#include "TStopwatch.h" +#include #include #include @@ -41,30 +41,95 @@ class AODMcProducerWorkflowDPL : public Task void run(ProcessingContext& pc) final; void endOfStream(EndOfStreamContext& ec) final; + /** Some types we will use */ + using MCEventHeader = dataformats::MCEventHeader; + using McCollisions = aod::McCollisions; + using McParticles = aod::StoredMcParticles; + using Origins = aod::Origins; + using XSections = aod::HepMCXSections; + using PdfInfos = aod::HepMCPdfInfos; + using HeavyIons = aod::HepMCHeavyIons; + using MCKinematicsReader = steer::MCKinematicsReader; + /** */ private: + /** some other types we will use */ + using CollisionCursor = aodmchelpers::CollisionCursor; + using XSectionCursor = aodmchelpers::XSectionCursor; + using PdfInfoCursor = aodmchelpers::PdfInfoCursor; + using HeavyIonCursor = aodmchelpers::HeavyIonCursor; + using HepMCUpdate = aodmchelpers::HepMCUpdate; + /** + * Update the header (collision and HepMC aux) information. + * + * When updating the HepMC aux tables, we take the relevant policies + * into account (mXSectionUpdate, mPdfInfoUpdate, mHeavyIonUpdate). + * + * - If a policy is "never", then the corresponding table is never + * updated. + * + * - If the policy is "always", then the table is always + * update. + * + * - If the policy is either "anyKey" or "allKeys", _and_ + * this is the first event, then we check if any or all keys, + * respectively are present in the header. + * + * - If that check fails, then we do not update and set the + * corresponding policy to be "never". + * + * - If the check succeeds, then we do update the table, and set + * the corresponding policty to "always". + * + * In this way, we will let the first event decide what to do for + * subsequent events and thus avoid too many string comparisions. + * + * @param collisionCursor Cursor over aod::McCollisions + * @param xSectionCursor Cursor over aod::HepMCXSections + * @param pdfInfoCursor Cursor over aod::HepMCPdfInfos + * @param heavyIonCursor Cursor over aod::HepMCHeavyIons + * @param header Header to read information from + * @param collisionID Index of collision in table + * @param bcID Current event identifier (bcID) + * @param time Time of event + * @param generatorID Generator identifier, if any + * @param sourceID Source identifier + * + */ + void updateHeader(CollisionCursor& collisionCursor, + XSectionCursor& xSectionCursor, + PdfInfoCursor& pdfInfoCursor, + HeavyIonCursor& heavyIonCursor, + const MCEventHeader& header, + int collisionID, // Index + int bcID, + float time, + short generatorID, + int sourceID); + /** Current timeframe number */ int64_t mTFNumber{1}; - int mTruncate{1}; + /** Whether to filter tracks so that only primary particles, + particles from EG, or their parents are written out */ int mFilterMC{0}; + /** Whether to enable embedding */ bool mEnableEmbed{false}; - std::string mMCHeaderFNames; + /** LPM production tag, for anchoring */ TString mLPMProdTag{""}; + /** Pass identifier for anchoring */ TString mAnchorPass{""}; + /** Production identifier for anchoring */ TString mAnchorProd{""}; + /** Reconstruction pass for anchoring */ TString mRecoPass{""}; + /** Timer */ TStopwatch mTimer; + /** Rules for when to update HepMC tables */ + HepMCUpdate mXSectionUpdate = HepMCUpdate::anyKey; + HepMCUpdate mPdfInfoUpdate = HepMCUpdate::anyKey; + HepMCUpdate mHeavyIonUpdate = HepMCUpdate::anyKey; std::string mSimPrefix; - o2::aodhelpers::TripletsMap_t mToStore; - - // keep track event/source id for each mc-collision - // using map and not unordered_map to ensure - // correct ordering when iterating over container elements - std::vector> mMCColToEvSrc; - // MC production metadata holder - std::vector mMetaDataKeys; - std::vector mMetaDataVals; bool mIsMDSent{false}; // truncation is enabled by default @@ -72,10 +137,6 @@ class AODMcProducerWorkflowDPL : public Task uint32_t mMcParticleW = 0xFFFFFFF0; // 19 bits uint32_t mMcParticlePos = 0xFFFFFFF0; // 19 bits uint32_t mMcParticleMom = 0xFFFFFFF0; // 19 bits - - template - void fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader, - const MCParticlesCursorType& mcParticlesCursor); }; /// create a processor spec diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h index f53b2a3082260..5351504443269 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h @@ -18,8 +18,7 @@ #include #include #include -#include -#include +#include namespace o2::aodhelpers { @@ -48,6 +47,15 @@ struct TripletEqualTo { typedef boost::unordered_map TripletsMap_t; +template +auto createTableCursor(framework::ProcessingContext& pc) +{ + framework::Produces c; + c.resetCursor(pc.outputs() + .make(framework::OutputForTable::ref())); + c.setLabel(aod::label()); + return c; +} } // namespace o2::aodhelpers #endif /* O2_AODPRODUCER_HELPERS */ diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index d5575f8caf6e1..21728e78a7a20 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -14,36 +14,33 @@ #ifndef O2_AODPRODUCER_WORKFLOW_SPEC #define O2_AODPRODUCER_WORKFLOW_SPEC -#include "CCDB/BasicCCDBManager.h" -#include "DataFormatsFT0/RecPoints.h" -#include "DataFormatsFDD/RecPoint.h" -#include "DataFormatsFV0/RecPoints.h" +#include "AODMcProducerHelpers.h" +#include "DataFormatsEMCAL/Cell.h" #include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsITS/TrackITS.h" -#include "DataFormatsMFT/TrackMFT.h" -#include "DataFormatsMCH/TrackMCH.h" -#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsPHOS/Cell.h" #include "DataFormatsTRD/TrackTRD.h" -#include "DataFormatsZDC/BCRecData.h" -#include "DataFormatsEMCAL/EventHandler.h" -#include "DataFormatsPHOS/EventHandler.h" +#include "TRDBase/PadCalibrationsAliases.h" +#include "DataFormatsTRD/NoiseCalibration.h" +#include "DataFormatsTRD/CalGain.h" +#include "DataFormatsTRD/Constants.h" #include "DetectorsBase/GRPGeomHelper.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/AnalysisHelpers.h" +#include "DetectorsBase/Propagator.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ReconstructionDataFormats/GlobalTrackID.h" -#include "ReconstructionDataFormats/PrimaryVertex.h" -#include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/VtxTrackIndex.h" -#include "SimulationDataFormat/MCCompLabel.h" #include "Steer/MCKinematicsReader.h" -#include "TMap.h" #include "TStopwatch.h" - -#include +#include "ZDCBase/Constants.h" +#include "GlobalTracking/MatchGlobalFwd.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "CommonUtils/EnumFlags.h" + +#include +#include +#include #include - +#include using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; using GIndex = o2::dataformats::VtxTrackIndex; @@ -51,6 +48,13 @@ using DataRequest = o2::globaltracking::DataRequest; namespace o2::aodproducer { +/// helper struct to keep mapping of colIndex to MC labels and bunch crossing +struct MCColInfo { + int colIndex; + int sourceID; + int eventID; + int64_t bc; // global bunch crossing +}; /// A structure or container to organize bunch crossing data of a timeframe /// and to facilitate fast lookup and search within bunch crossings. @@ -74,15 +78,23 @@ class BunchCrossings /// return the sorted vector of increaing BC times std::vector const& getBCTimeVector() const { return mBCTimeVector; } - /// Performs a "lower bound" search for timestamp within the bunch crossing data. - /// Returns the smallest bunch crossing (index and value) equal or greater than timestamp. - /// The functions is expected to perform much better than - /// a binary search in the bunch crossing data directly. Expect O(1) instead of O(log(N)) at the cost - /// of the additional memory used by this class. + /// Performs a "lower bound" search for timestamp within the bunch + /// crossing data. + /// + /// Returns the smallest bunch crossing (index and value) equal or + /// greater than timestamp. + /// + /// The functions is expected to perform much better than a binary + /// search in the bunch crossing data directly. Expect O(1) instead + /// of O(log(N)) at the cost of the additional memory used by this + /// class. + /// + /// This is _not_ O(1). The loop below makes it at least O(N). The + /// call to std::lower_bound is O(log(N)). std::pair lower_bound(uint64_t timestamp) const { // a) determine the timewindow - const auto NofWindows = mTimeWindows.size(); + const auto NofWindows = static_cast(mTimeWindows.size()); const auto smallestBC = mBCTimeVector[0]; const auto largestBC = mBCTimeVector.back(); auto timeindex = std::max((int)0, (int)((timestamp - smallestBC) / mWindowSize)); @@ -158,19 +170,19 @@ class BunchCrossings mWindowSize = bcrange / (1. * window_number); // now we go through the list of times and bucket them into the correct windows mTimeWindows.resize(window_number); - for (int bcindex = 0; bcindex < mBCTimeVector.size(); ++bcindex) { + for (auto bcindex = 0U; bcindex < mBCTimeVector.size(); ++bcindex) { auto windowindex = (int)((mBCTimeVector[bcindex] - mBCTimeVector[0]) / mWindowSize); // we add "bcindex" to the TimeWindow windowindex auto& tw = mTimeWindows[windowindex]; if (tw.from == -1) { tw.from = bcindex; } else { - tw.from = std::min(tw.from, bcindex); + tw.from = std::min(tw.from, static_cast(bcindex)); } if (tw.to == -1) { tw.to = bcindex; } else { - tw.to = std::max(tw.to, bcindex); + tw.to = std::max(tw.to, static_cast(bcindex)); } } @@ -205,12 +217,17 @@ class BunchCrossings std::vector mTimeWindows; // the time window structure covering the complete duration of mBCTimeVector double mWindowSize; // the size of a single time window -}; // end internal class +}; // end internal class + +// Steering bits for additional output during AOD production +enum struct AODProducerStreamerFlags : uint8_t { + TrackQA, +}; class AODProducerWorkflowDPL : public Task { public: - AODProducerWorkflowDPL(GID::mask_t src, std::shared_ptr dataRequest, std::shared_ptr gr, bool enableSV, bool useMC = true) : mInputSources(src), mDataRequest(dataRequest), mGGCCDBRequest(gr), mEnableSV(enableSV), mUseMC(useMC) {} + AODProducerWorkflowDPL(GID::mask_t src, std::shared_ptr dataRequest, std::shared_ptr gr, bool enableSV, bool useMC = true, bool enableFITextra = false, bool enableTRDextra = false) : mUseMC(useMC), mEnableSV(enableSV), mEnableFITextra(enableFITextra), mEnableTRDextra(enableTRDextra), mInputSources(src), mDataRequest(dataRequest), mGGCCDBRequest(gr) {} ~AODProducerWorkflowDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -229,10 +246,43 @@ class AODProducerWorkflowDPL : public Task return std::uint64_t(mStartIR.toLong()) + relativeTime_to_LocalBC(relativeTimeStampInNS); } + bool collectConfigFiles(std::vector& keys, std::vector& values, int indent = -1); + + bool mThinTracks{false}; + bool mPropTracks{false}; + bool mPropMuons{false}; + bool mStoreAllMFTCov{false}; + float mTrackQCKeepGlobalTracks{false}; + float mTrackQCRetainOnlydEdx{false}; + float mTrackQCFraction{0.00}; + int64_t mTrackQCNTrCut{4}; + float mTrackQCDCAxy{3.}; + float mTrackQCPt{0.2}; + int mTrackQCNCls{80}; + float mSqrtS{13860.}; + std::mt19937 mGenerator{}; ///< random generator for trackQA sampling + o2::base::Propagator::MatCorrType mMatCorr{o2::base::Propagator::MatCorrType::USEMatCorrLUT}; + o2::dataformats::MeanVertexObject mVtx; + float mMaxPropXiu{5.0f}; // max X_IU for which track is to be propagated if mPropTracks is true. (other option: o2::constants::geom::XTPCInnerRef + 0.1f) + + const o2::trd::LocalGainFactor* mTRDLocalGain; // TRD local gain factors from krypton calibration + const o2::trd::CalGain* mTRDGainCalib; // TRD time-dependent gain calib at chamber level + const o2::trd::NoiseStatusMCM* mTRDNoiseMap; // TRD noise map + + std::unordered_set mGIDUsedBySVtx; + std::unordered_set mGIDUsedByStr; + + o2::utils::EnumFlags mStreamerFlags; + std::shared_ptr mStreamer; + int mNThreads = 1; bool mUseMC = true; - bool mEnableSV = true; // enable secondary vertices + bool mUseSigFiltMC = false; // enable signal filtering for MC with embedding + bool mEnableSV = true; // enable secondary vertices + bool mEnableFITextra = false; + bool mEnableTRDextra = false; bool mFieldON = false; + bool mCollectConfigFiles = false; const float cSpeed = 0.029979246f; // speed of light in TOF units GID::mask_t mInputSources; @@ -245,7 +295,12 @@ class AODProducerWorkflowDPL : public Task TString mAnchorPass{""}; TString mAnchorProd{""}; TString mRecoPass{""}; + std::string mAODParent{""}; // link to possible parent AOD file (MC embedding,...) + TString mUser{"aliprod"}; // who created this AOD (aliprod, alidaq, individual users) TStopwatch mTimer; + bool mEMCselectLeading{false}; + uint64_t mEMCALTrgClassMask = 0; + size_t mCurrentTRDTrigID = 0; // current index of the TRD trigger record, to speed up search // unordered map connects global indices and table indices of barrel tracks std::unordered_map mGIDToTableID; @@ -263,6 +318,11 @@ class AODProducerWorkflowDPL : public Task std::unordered_map mV0ToTableID; int mTableV0ID{0}; + // Strangeness tracking indices lookup tables + std::vector mVertexStrLUT; /// LUT for accessing strangeness tracks for each vertex + std::vector> mCollisionStrTrk; /// collision index and original index of the strangeness track + std::vector mStrTrkIndices; /// indices of strangeness tracks in the track table + // std::unordered_map mIndexTableFwd; std::vector mIndexTableFwd; int mIndexFwdID{0}; @@ -274,8 +334,8 @@ class AODProducerWorkflowDPL : public Task // zdc helper maps to avoid a number of "if" statements // when filling ZDC table - map mZDCEnergyMap; // mapping detector name to a corresponding energy - map mZDCTDCMap; // mapping TDC channel to a corresponding TDC value + std::array mZDCEnergyMap; // mapping detector id to a corresponding energy + std::array mZDCTDCMap; // mapping TDC channel id to a corresponding TDC value std::vector mITSTPCTRDTriggers; // mapping from TRD tracks ID to corresponding trigger (for tracks time extraction) std::vector mTPCTRDTriggers; // mapping from TRD tracks ID to corresponding trigger (for tracks time extraction) @@ -284,6 +344,8 @@ class AODProducerWorkflowDPL : public Task std::vector mMCHROFs; // mapping from MCH tracks ID to corresponding ROF (for SA MCH tracks time extraction) double mITSROFrameHalfLengthNS = -1; // ITS ROF half length double mMFTROFrameHalfLengthNS = -1; // ITS ROF half length + double mITSROFBiasNS = 0; // ITS ROF start bias + double mMFTROFBiasNS = 0; // ITS ROF start bias double mNSigmaTimeTrack = -1; // number track errors sigmas (for gaussian errors only) used in track-vertex matching double mTimeMarginTrackTime = -1; // safety margin in NS used for track-vertex matching (additive to track uncertainty) double mTPCBinNS = -1; // inverse TPC time-bin in ns @@ -292,16 +354,18 @@ class AODProducerWorkflowDPL : public Task // Mapping of eventID, sourceID, trackID to some integer. // The first two indices are not sparse whereas the trackID index is sparse which explains // the combination of vector and map - std::vector*>> mToStore; + std::vector>> mToStore; + o2::steer::MCKinematicsReader* mMCKineReader = nullptr; //! // production metadata std::vector mMetaDataKeys; std::vector mMetaDataVals; - bool mIsMDSent{false}; std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; + o2::globaltracking::MatchGlobalFwd mMatching; + static constexpr int TOFTimePrecPS = 16; // required max error in ps for TOF tracks // truncation is enabled by default uint32_t mCollisionPosition = 0xFFFFFFF0; // 19 bits mantissa @@ -316,6 +380,7 @@ class AODProducerWorkflowDPL : public Task uint32_t mTrackCovOffDiag = 0xFFFF0000; // 7 bits uint32_t mTrackSignal = 0xFFFFFF00; // 15 bits uint32_t mTrackTime = 0xFFFFFFFF; // use full float precision for time + uint32_t mTPCTime0 = 0xFFFFFFE0; // 18 bits, providing 14256./(1<<19) = 0.027 TB precision e.g., ~0.13 mm in z uint32_t mTrackTimeError = 0xFFFFFF00; // 15 bits uint32_t mTrackPosEMCAL = 0xFFFFFF00; // 15 bits uint32_t mTracklets = 0xFFFFFF00; // 15 bits @@ -336,8 +401,11 @@ class AODProducerWorkflowDPL : public Task uint32_t mMuonCl = 0xFFFFFF00; // 15 bits uint32_t mMuonClErr = 0xFFFF0000; // 7 bits uint32_t mV0Time = 0xFFFFF000; // 11 bits + uint32_t mV0ChannelTime = 0xFFFFFF00; // 15 bits uint32_t mFDDTime = 0xFFFFF000; // 11 bits + uint32_t mFDDChannelTime = 0xFFFFFF00; // 15 bits uint32_t mT0Time = 0xFFFFFF00; // 15 bits + uint32_t mT0ChannelTime = 0xFFFFFFF0; // 19 bits uint32_t mV0Amplitude = 0xFFFFF000; // 11 bits uint32_t mFDDAmplitude = 0xFFFFF000; // 11 bits uint32_t mT0Amplitude = 0xFFFFF000; // 11 bits @@ -347,9 +415,11 @@ class AODProducerWorkflowDPL : public Task struct TrackExtraInfo { float tpcInnerParam = 0.f; uint32_t flags = 0; + uint32_t itsClusterSizes = 0u; uint8_t itsClusterMap = 0; uint8_t tpcNClsFindable = 0; int8_t tpcNClsFindableMinusFound = 0; + int8_t tpcNClsFindableMinusPID = 0; int8_t tpcNClsFindableMinusCrossedRows = 0; uint8_t tpcNClsShared = 0; uint8_t trdPattern = 0; @@ -365,7 +435,38 @@ class AODProducerWorkflowDPL : public Task float trackPhiEMCAL = -999.f; float trackTime = -999.f; float trackTimeRes = -999.f; + int diffBCRef = 0; // offset of time reference BC from the start of the orbit int bcSlice[2] = {-1, -1}; + bool isTPConly = false; // not to be written out + }; + + struct TrackQA { + GID trackID; + float tpcTime0{}; + float tpcdEdxNorm{}; + int16_t tpcdcaR{}; + int16_t tpcdcaZ{}; + uint8_t tpcClusterByteMask{}; + uint8_t tpcdEdxMax0R{}; + uint8_t tpcdEdxMax1R{}; + uint8_t tpcdEdxMax2R{}; + uint8_t tpcdEdxMax3R{}; + uint8_t tpcdEdxTot0R{}; + uint8_t tpcdEdxTot1R{}; + uint8_t tpcdEdxTot2R{}; + uint8_t tpcdEdxTot3R{}; + int8_t dRefContY{std::numeric_limits::min()}; + int8_t dRefContZ{std::numeric_limits::min()}; + int8_t dRefContSnp{std::numeric_limits::min()}; + int8_t dRefContTgl{std::numeric_limits::min()}; + int8_t dRefContQ2Pt{std::numeric_limits::min()}; + int8_t dRefGloY{std::numeric_limits::min()}; + int8_t dRefGloZ{std::numeric_limits::min()}; + int8_t dRefGloSnp{std::numeric_limits::min()}; + int8_t dRefGloTgl{std::numeric_limits::min()}; + int8_t dRefGloQ2Pt{std::numeric_limits::min()}; + int8_t dTofdX{std::numeric_limits::min()}; + int8_t dTofdZ{std::numeric_limits::min()}; }; // helper struct for addToFwdTracksTable() @@ -416,8 +517,6 @@ class AODProducerWorkflowDPL : public Task // using -1 as dummies for AOD struct MCLabels { uint32_t labelID = -1; - uint32_t labelITS = -1; - uint32_t labelTPC = -1; uint16_t labelMask = 0; uint8_t fwdLabelMask = 0; }; @@ -439,33 +538,41 @@ class AODProducerWorkflowDPL : public Task const std::vector& mcRecords, std::map& bcsMap); - uint64_t getTFNumber(const o2::InteractionRecord& tfStartIR, int runNumber); template void addToTracksTable(TracksCursorType& tracksCursor, TracksCovCursorType& tracksCovCursor, - const o2::track::TrackParCov& track, int collisionID); + const o2::track::TrackParCov& track, int collisionID, aod::track::TrackTypeEnum type = aod::track::TrackIU); template void addToTracksExtraTable(TracksExtraCursorType& tracksExtraCursor, TrackExtraInfo& extraInfoHolder); - template - void addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, + template + void addToTracksQATable(TracksQACursorType& tracksQACursor, TrackQA& trackQAInfoHolder); + + template + void addToTRDsExtra(const o2::globaltracking::RecoContainer& recoData, TRDsExtraCursorType& trdExtraCursor, const GIndex& trkIdx, int trkTableIdx); + + template + void addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap); - template - void addToFwdTracksTable(fwdTracksCursorType& fwdTracksCursor, fwdTracksCovCursorType& fwdTracksCovCursor, AmbigFwdTracksCursorType& ambigFwdTracksCursor, + template + void addToFwdTracksTable(fwdTracksCursorType& fwdTracksCursor, fwdTracksCovCursorType& fwdTracksCovCursor, AmbigFwdTracksCursorType& ambigFwdTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap); TrackExtraInfo processBarrelTrack(int collisionID, std::uint64_t collisionBC, GIndex trackIndex, const o2::globaltracking::RecoContainer& data, const std::map& bcsMap); + TrackQA processBarrelTrackQA(int collisionID, std::uint64_t collisionBC, GIndex trackIndex, const o2::globaltracking::RecoContainer& data, const std::map& bcsMap); + + bool propagateTrackToPV(o2::track::TrackParametrizationWithError& trackPar, const o2::globaltracking::RecoContainer& data, int colID); void extrapolateToCalorimeters(TrackExtraInfo& extraInfoHolder, const o2::track::TrackPar& track); void cacheTriggers(const o2::globaltracking::RecoContainer& recoData); // helper for track tables // * fills tables collision by collision // * interaction time is for TOF information - template + template void fillTrackTablesPerCollision(int collisionID, std::uint64_t collisionBC, const o2::dataformats::VtxTrackRef& trackRef, @@ -474,61 +581,139 @@ class AODProducerWorkflowDPL : public Task TracksCursorType& tracksCursor, TracksCovCursorType& tracksCovCursor, TracksExtraCursorType& tracksExtraCursor, + TracksQACursorType& tracksQACursor, + TRDsExtraCursorType& trdsExtraCursor, AmbigTracksCursorType& ambigTracksCursor, MFTTracksCursorType& mftTracksCursor, + MFTTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, AmbigFwdTracksCursorType& ambigFwdTracksCursor, + FwdTrkClsCursorType& fwdTrkClsCursor, const std::map& bcsMap); + template + void addClustersToFwdTrkClsTable(const o2::globaltracking::RecoContainer& recoData, FwdTrkClsCursorType& fwdTrkClsCursor, GIndex trackID, int fwdTrackId); + void fillIndexTablesPerCollision(const o2::dataformats::VtxTrackRef& trackRef, const gsl::span& GIndices, const o2::globaltracking::RecoContainer& data); template void fillSecondaryVertices(const o2::globaltracking::RecoContainer& data, V0CursorType& v0Cursor, CascadeCursorType& cascadeCursor, Decay3bodyCursorType& decay3bodyCursor); - template + template + void fillHMPID(const o2::globaltracking::RecoContainer& recoData, HMPCursorType& hmpCursor); + + void prepareStrangenessTracking(const o2::globaltracking::RecoContainer& recoData); + template + void fillStrangenessTrackingTables(const o2::globaltracking::RecoContainer& data, V0C& v0Cursor, CC& cascadeCursor, D3BC& decay3bodyCursor); + + /** some other types we will use */ + using MCCollisionCursor = aodmchelpers::CollisionCursor; + using XSectionCursor = aodmchelpers::XSectionCursor; + using PdfInfoCursor = aodmchelpers::PdfInfoCursor; + using HeavyIonCursor = aodmchelpers::HeavyIonCursor; + using MCParticlesCursor = aodmchelpers::ParticleCursor; + using HepMCUpdate = aodmchelpers::HepMCUpdate; + using MCEventHeader = dataformats::MCEventHeader; + /** Rules for when to update HepMC tables */ + HepMCUpdate mXSectionUpdate = HepMCUpdate::anyKey; + HepMCUpdate mPdfInfoUpdate = HepMCUpdate::anyKey; + HepMCUpdate mHeavyIonUpdate = HepMCUpdate::anyKey; + /** + * Update the header (collision and HepMC aux) information. + * + * When updating the HepMC aux tables, we take the relevant policies + * into account (mXSectionUpdate, mPdfInfoUpdate, mHeavyIonUpdate). + * + * - If a policy is "never", then the corresponding table is never + * updated. + * + * - If the policy is "always", then the table is always + * update. + * + * - If the policy is either "anyKey" or "allKeys", _and_ + * this is the first event, then we check if any or all keys, + * respectively are present in the header. + * + * - If that check fails, then we do not update and set the + * corresponding policy to be "never". + * + * - If the check succeeds, then we do update the table, and set + * the corresponding policty to "always". + * + * In this way, we will let the first event decide what to do for + * subsequent events and thus avoid too many string comparisions. + * + * @param collisionCursor Cursor over aod::McCollisions + * @param xSectionCursor Cursor over aod::HepMCXSections + * @param pdfInfoCursor Cursor over aod::HepMCPdfInfos + * @param heavyIonCursor Cursor over aod::HepMCHeavyIons + * @param header Header to read information from + * @param collisionID Index of collision in the table + * @param bcID Current event identifier (bcID) + * @param time Time of event + * @param generatorID Generator identifier, if any + * @param sourceID Source identifier + * + */ + void updateMCHeader(MCCollisionCursor& collisionCursor, + XSectionCursor& xSectionCursor, + PdfInfoCursor& pdfInfoCursor, + HeavyIonCursor& heavyIonCursor, + const MCEventHeader& header, + int collisionID, + int bcID, + float time, + short generatorID, + int sourceID); + void fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader, - const MCParticlesCursorType& mcParticlesCursor, + MCParticlesCursor& mcParticlesCursor, const gsl::span& primVer2TRefs, const gsl::span& GIndices, const o2::globaltracking::RecoContainer& data, - const std::vector>& mcColToEvSrc); + const std::vector& mcColToEvSrc); template - void fillMCTrackLabelsTable(const MCTrackLabelCursorType& mcTrackLabelCursor, - const MCMFTTrackLabelCursorType& mcMFTTrackLabelCursor, - const MCFwdTrackLabelCursorType& mcFwdTrackLabelCursor, + void fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTrackLabelCursor, + MCMFTTrackLabelCursorType& mcMFTTrackLabelCursor, + MCFwdTrackLabelCursorType& mcFwdTrackLabelCursor, const o2::dataformats::VtxTrackRef& trackRef, const gsl::span& primVerGIs, - const o2::globaltracking::RecoContainer& data); + const o2::globaltracking::RecoContainer& data, + int vertexId = -1); std::uint64_t fillBCSlice(int (&slice)[2], double tmin, double tmax, const std::map& bcsMap) const; + std::vector fillBCFlags(const o2::globaltracking::RecoContainer& data, std::map& bcsMap) const; + // helper for tpc clusters void countTPCClusters(const o2::globaltracking::RecoContainer& data); // helper for trd pattern uint8_t getTRDPattern(const o2::trd::TrackTRD& track); - template - void addToCaloTable(const TCaloHandler& caloHandler, const TCaloCursor& caloCellCursor, const TCaloTRGCursor& caloTRGCursor, - int eventID, int bcID, int8_t caloType); + template + void addToCaloTable(TCaloHandler& caloHandler, TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor, + TMCCaloLabelCursor& mcCaloCellLabelCursor, int eventID, int bcID, int8_t caloType); - template - void fillCaloTable(const TCaloCursor& caloCellCursor, const TCaloTRGCursor& caloTRGCursor, - const std::map& bcsMap, + template + void fillCaloTable(TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor, + TMCCaloLabelCursor& mcCaloCellLabelCursor, const std::map& bcsMap, const o2::globaltracking::RecoContainer& data); + + std::set filterEMCALIncomplete(const gsl::span triggers); }; /// create a processor spec -framework::DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool useMC, bool CTPConfigPerRun); +framework::DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableST, bool useMC, bool CTPConfigPerRun, bool enableFITextra, bool enableTRDextra); // helper interface for calo cells to "befriend" emcal and phos cells class CellHelper { public: - static int8_t getTriggerBits(const o2::emcal::Cell& cell) + static int8_t getTriggerBits(const o2::emcal::Cell& /*cell*/) { return 0; // dummy value } @@ -545,6 +730,9 @@ class CellHelper static int16_t getCellNumber(const o2::phos::Cell& cell) { + if (cell.getTRU()) { + return cell.getTRUId(); + } return cell.getAbsId(); } // If this cell - trigger one? @@ -558,7 +746,7 @@ class CellHelper return cell.getTRU(); } - static int16_t getFastOrAbsID(const o2::emcal::Cell& cell) + static int16_t getFastOrAbsID(const o2::emcal::Cell& /*cell*/) { return 0; // dummy value } @@ -578,7 +766,7 @@ class CellHelper return cell.getEnergy(); } - static int16_t getLnAmplitude(const o2::emcal::Cell& cell) + static int16_t getLnAmplitude(const o2::emcal::Cell& /*cell*/) { return 0; // dummy value } diff --git a/Detectors/AOD/src/AODMcProducerHelpers.cxx b/Detectors/AOD/src/AODMcProducerHelpers.cxx new file mode 100644 index 0000000000000..a7093e0048c25 --- /dev/null +++ b/Detectors/AOD/src/AODMcProducerHelpers.cxx @@ -0,0 +1,436 @@ +// Copyright 2023-2099 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file AODMcProducerHelpers.h +/// @author Christian Holm Christensen +/// common helpers for AOD MC producers +#include "AODProducerWorkflow/AODMcProducerHelpers.h" +#include +#include +#include +#include +#define verbosity debug + +namespace o2::aodmchelpers +{ +//================================================================== +bool hasKeys(o2::dataformats::MCEventHeader const& header, + const std::vector& keys, + bool anyNotAll) +{ + auto check = [&header](const std::string& key) { // Do not format + return header.hasInfo(key); + }; + return (anyNotAll ? // Do not format + std::any_of(keys.cbegin(), keys.cend(), check) + : // Do not format + std::all_of(keys.cbegin(), keys.cend(), check)); +} +//==================================================================== +short updateMCCollisions(const CollisionCursor& cursor, + int bcId, + float time, + o2::dataformats::MCEventHeader const& header, + short generatorId, + int sourceId, + unsigned int mask) +{ + using Key = o2::dataformats::MCInfoKeys; + using GenProp = o2::mcgenid::GeneratorProperty; + using namespace o2::math_utils; + + int subGenId = getEventInfo(header, GenProp::SUBGENERATORID, -1); + int genId = getEventInfo(header, GenProp::GENERATORID, + int(generatorId)); + float weight = getEventInfo(header, Key::weight, 1.f); + + auto encodedGeneratorId = o2::mcgenid::getEncodedGenId(genId, + sourceId, + subGenId); + + LOG(verbosity) << "Updating MC Collisions table w/bcId=" << bcId; + cursor(0, + bcId, + encodedGeneratorId, + truncateFloatFraction(header.GetX(), mask), + truncateFloatFraction(header.GetY(), mask), + truncateFloatFraction(header.GetZ(), mask), + truncateFloatFraction(time, mask), + truncateFloatFraction(weight, mask), + header.GetB(), + getEventInfo(header, Key::planeAngle, header.GetRotZ())); + return encodedGeneratorId; +} +//-------------------------------------------------------------------- +bool updateHepMCXSection(const XSectionCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when) +{ + using Key = o2::dataformats::MCInfoKeys; + + if (when == HepMCUpdate::never or + (when != HepMCUpdate::always and + not hasKeys(header, { // Do not + Key::acceptedEvents, // mess with + Key::attemptedEvents, // with + Key::xSection, // my + Key::xSectionError}, // formatting + when == HepMCUpdate::anyKey))) { + return false; + } + + LOG(verbosity) << "Updating HepMC cross-section table w/collisionId " + << collisionID; + cursor(0, + collisionID, + generatorID, + getEventInfo(header, Key::acceptedEvents, 0), + getEventInfo(header, Key::attemptedEvents, 0), + getEventInfo(header, Key::xSection, 0.f), + getEventInfo(header, Key::xSectionError, 0.f), + getEventInfo(header, Key::eventScale, 1.f), + getEventInfo(header, Key::mpi, -1), + getEventInfo(header, Key::processCode, -1)); + return true; +} +//-------------------------------------------------------------------- +bool updateHepMCPdfInfo(const PdfInfoCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when) +{ + using Key = o2::dataformats::MCInfoKeys; + + if (when == HepMCUpdate::never or + (when != HepMCUpdate::always and // Do + not hasKeys(header, {Key::pdfParton1Id, // not + Key::pdfParton2Id, // mess + Key::pdfCode1, // with + Key::pdfCode2, // my + Key::pdfX1, // formatting + Key::pdfX2, // . + Key::pdfScale, // It + Key::pdfXF1, // is + Key::pdfXF2}, // better + when == HepMCUpdate::anyKey))) { + return false; + } + + LOG(verbosity) << "Updating HepMC PDF table w/collisionId " << collisionID; + cursor(0, + collisionID, + generatorID, + getEventInfo(header, Key::pdfParton1Id, int(0)), + getEventInfo(header, Key::pdfParton2Id, int(0)), + getEventInfo(header, Key::pdfCode1, int(0)), + getEventInfo(header, Key::pdfCode2, int(0)), + getEventInfo(header, Key::pdfX1, 0.f), + getEventInfo(header, Key::pdfX2, 0.f), + getEventInfo(header, Key::pdfScale, 1.f), + getEventInfo(header, Key::pdfXF1, 0.f), + getEventInfo(header, Key::pdfXF2, 0.f)); + + return true; +} +//-------------------------------------------------------------------- +bool updateHepMCHeavyIon(const HeavyIonCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when) +{ + using Key = dataformats::MCInfoKeys; + + if (when == HepMCUpdate::never or + (when != HepMCUpdate::always and // clang + not hasKeys(header, {Key::nCollHard, // format + Key::nPartProjectile, // is + Key::nPartTarget, // so + Key::nColl, // annoying + Key::nCollNNWounded, // . + Key::nCollNWoundedN, // It + Key::nCollNWoundedNwounded, // messes + Key::nSpecProjectileNeutron, // up + Key::nSpecTargetNeutron, // the + Key::nSpecProjectileProton, // clarity + Key::nSpecTargetProton, // of + Key::planeAngle, // the + "eccentricity", // code + Key::sigmaInelNN, // to + Key::centrality}, // noavail + when == HepMCUpdate::anyKey))) { + return false; + } + + int specNeutrons = (getEventInfo(header, Key::nSpecProjectileNeutron, -1) + + getEventInfo(header, Key::nSpecTargetNeutron, -1)); + int specProtons = (getEventInfo(header, Key::nSpecProjectileProton, -1) + + getEventInfo(header, Key::nSpecTargetProton, -1)); + + LOG(verbosity) << "Updating HepMC heavy-ion table w/collisionID " + << collisionID; + cursor(0, + collisionID, + generatorID, + getEventInfo(header, Key::nCollHard, -1), + getEventInfo(header, Key::nPartProjectile, -1), + getEventInfo(header, Key::nPartTarget, -1), + getEventInfo(header, Key::nColl, -1), + getEventInfo(header, Key::nCollNNWounded, -1), + getEventInfo(header, Key::nCollNWoundedN, -1), + getEventInfo(header, Key::nCollNWoundedNwounded, -1), + specNeutrons, + specProtons, + header.GetB(), + getEventInfo(header, Key::planeAngle, header.GetRotZ()), + getEventInfo(header, "eccentricity", 0), + getEventInfo(header, Key::sigmaInelNN, 0.), + getEventInfo(header, Key::centrality, -1)); + + return true; +} +//-------------------------------------------------------------------- +void updateParticle(const ParticleCursor& cursor, + const TrackToIndex& toStore, + int collisionID, + o2::MCTrack const& track, + std::vector const& tracks, + uint8_t flags, + uint32_t weightMask, + uint32_t momentumMask, + uint32_t positionMask) +{ + using o2::mcutils::MCTrackNavigator; + using namespace o2::aod::mcparticle::enums; + using namespace o2::math_utils; + using namespace o2::mcgenstatus; + + auto mapping = [&toStore](int trackNo) { + auto iter = toStore.find(trackNo); + if (iter == toStore.end()) { + return -1; + } + return iter->second; + }; + + auto statusCode = track.getStatusCode().fullEncoding; + auto hepmc = getHepMCStatusCode(track.getStatusCode()); + if (not track.isPrimary()) { + flags |= ProducedByTransport; + statusCode = track.getProcess(); + } + if (MCTrackNavigator::isPhysicalPrimary(track, tracks)) { + flags |= PhysicalPrimary; + } + + int daughters[2] = {-1, -1}; + std::vector mothers; + int id; + if ((id = mapping(track.getMotherTrackId())) >= 0) { + mothers.push_back(id); + } + if ((id = mapping(track.getSecondMotherTrackId())) >= 0) { + mothers.push_back(id); + } + if ((id = mapping(track.getFirstDaughterTrackId())) >= 0) { + daughters[0] = id; + } + if ((id = mapping(track.getLastDaughterTrackId())) >= 0) { + daughters[1] = id; + } else { + daughters[1] = daughters[0]; + } + if (daughters[0] < 0 and daughters[1] >= 0) { + LOG(error) << "Problematic daughters: " << daughters[0] << " and " + << daughters[1]; + daughters[0] = daughters[1]; + } + if (daughters[0] > daughters[1]) { + std::swap(daughters[0], daughters[1]); + } + + float weight = track.getWeight(); + float pX = float(track.Px()); + float pY = float(track.Py()); + float pZ = float(track.Pz()); + float energy = float(track.GetEnergy()); + float vX = float(track.Vx()); + float vY = float(track.Vy()); + float vZ = float(track.Vz()); + float time = float(track.T()); + + LOG(verbosity) << "McParticle collisionId=" << collisionID << "," + << "status=" << statusCode << "," + << "hepmc=" << hepmc << "," + << "pdg=" << track.GetPdgCode() << "," + << "nMothers=" << mothers.size() << "," + << "daughters=" << daughters[0] << "," + << daughters[1]; + + cursor(0, + collisionID, + track.GetPdgCode(), + statusCode, + flags, + mothers, + daughters, + truncateFloatFraction(weight, weightMask), + truncateFloatFraction(pX, momentumMask), + truncateFloatFraction(pY, momentumMask), + truncateFloatFraction(pZ, momentumMask), + truncateFloatFraction(energy, momentumMask), + truncateFloatFraction(vX, positionMask), + truncateFloatFraction(vY, positionMask), + truncateFloatFraction(vZ, positionMask), + truncateFloatFraction(time, positionMask)); +} +//-------------------------------------------------------------------- +uint32_t updateParticles(const ParticleCursor& cursor, + int collisionID, + std::vector const& tracks, + TrackToIndex& preselect, + uint32_t offset, + bool filter, + bool background, + uint32_t weightMask, + uint32_t momentumMask, + uint32_t positionMask, + bool signalFilter) +{ + using o2::mcutils::MCTrackNavigator; + using namespace o2::aod::mcparticle::enums; + using namespace o2::mcgenstatus; + + // First loop over particles to find out which to store + // TrackToIndex toStore(preselect.begin(), preselect.end()); + // + // Guess we need to modifiy the passed in mapping so that MC labels + // can be set correctly + TrackToIndex& toStore = preselect; + + // Mapping lambda. This maps the track number to the index into + // the table exported. + auto mapping = [&toStore](int trackNo) { + auto iter = toStore.find(trackNo); + if (iter == toStore.end()) { + return -1; + } + return iter->second; + }; + + LOG(verbosity) << "Got a total of " << tracks.size(); + for (int trackNo = tracks.size() - 1; trackNo >= 0; trackNo--) { + auto& track = tracks[trackNo]; + auto hepmc = getHepMCStatusCode(track.getStatusCode()); + if (filter) { + if (toStore.find(trackNo) == toStore.end() and + /* The above test is in-correct. The track may be stored in + the list, but with a negative value. In that case, the + above test will still check mothers and daughters, and + possible store them in the output. This is however how + it is done in current `dev` branch, and so to enable + comparison on closure, we do this test for now. The + correct way it commented out below. */ + // mapping(trackNo) < 0 and + not track.isPrimary() and + not MCTrackNavigator::isPhysicalPrimary(track, tracks) and + not MCTrackNavigator::isKeepPhysics(track, tracks)) { + LOG(verbosity) << "Skipping track " << trackNo << " " << hepmc << " " + << mapping(trackNo) << " " + << track.isPrimary() << " " + << MCTrackNavigator::isPhysicalPrimary(track, tracks) + << " " + << MCTrackNavigator::isKeepPhysics(track, tracks); + continue; + } + } + if (background && signalFilter) { + continue; + } + + // Store this particle. We mark that putting a 1 in the + // `toStore` mapping. This will later on be updated with the + // actual index into the table + toStore[trackNo] = 1; + + // If we're filtering tracks, then also mark mothers and + // daughters(?) to be stored. + if (filter) { + int id; + if ((id = track.getMotherTrackId()) >= 0) { + toStore[id] = 1; + } + if ((id = track.getSecondMotherTrackId()) >= 0) { + toStore[id] = 1; + } + if ((id = track.getFirstDaughterTrackId()) >= 0) { + toStore[id] = 1; + } + if ((id = track.getLastDaughterTrackId()) >= 0) { + toStore[id] = 1; + } + } + } + + // Second loop to set indexes. This is needed to be done before + // we actually update the table, because a particle may point to a + // later particle. + LOG(verbosity) << "Will store " << toStore.size() << " particles"; + size_t index = 0; + + for (size_t trackNo = 0U; trackNo < tracks.size(); trackNo++) { + auto storeIt = mapping(trackNo); + if (storeIt < 0) { + continue; + } + + toStore[trackNo] = offset + index; + index++; + } + + // Make sure we have the right number + assert(index == toStore.size()); + LOG(verbosity) << "Starting index " << offset << ", last index " + << offset + index << " " + << "Selected " << toStore.size() << " tracks out of " + << tracks.size() << " " + << "Collision # " << collisionID; + // Third loop to actually store the particles in the order given + for (size_t trackNo = 0U; trackNo < tracks.size(); trackNo++) { + auto storeIt = mapping(trackNo); + if (storeIt < 0) { + continue; + } + + auto& track = tracks[trackNo]; + auto hepmc = getHepMCStatusCode(track.getStatusCode()); + uint8_t flags = (background ? FromBackgroundEvent : 0); + updateParticle(cursor, + toStore, + collisionID, + track, + tracks, + flags, + weightMask, + momentumMask, + positionMask); + } + LOG(verbosity) << "Return new offset " << offset + index; + return offset + index; +} +} // namespace o2::aodmchelpers + +// Local Variables: +// mode: C++ +// End: diff --git a/Detectors/AOD/src/AODMcProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODMcProducerWorkflowSpec.cxx index ca7c089983605..8136bd3e6d268 100644 --- a/Detectors/AOD/src/AODMcProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODMcProducerWorkflowSpec.cxx @@ -13,164 +13,18 @@ #include "AODProducerWorkflow/AODMcProducerWorkflowSpec.h" #include "AODProducerWorkflow/AODProducerHelpers.h" -#include "MathUtils/Utils.h" -#include "CCDB/BasicCCDBManager.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" -#include "Framework/AnalysisDataModel.h" #include "Framework/ControlService.h" #include "Framework/DataTypes.h" -#include "Framework/InputRecordWalker.h" -#include "Framework/Logger.h" -#include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" -#include "Framework/CCDBParamSpec.h" -#include "SimulationDataFormat/MCTrack.h" #include "SimulationDataFormat/MCUtils.h" #include "O2Version.h" -#include "TMath.h" #include "TString.h" -#include using namespace o2::framework; -using namespace o2::math_utils::detail; namespace o2::aodmcproducer { -template -void AODMcProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader, - const MCParticlesCursorType& mcParticlesCursor) -{ - using o2::aodhelpers::Triplet_t; - - int tableIndex = 1; - for (auto& colInfo : mMCColToEvSrc) { // loop over " <-> combined MC col. ID" key pairs - int event = colInfo[2]; - int source = colInfo[1]; - int mcColId = colInfo[0]; - std::vector const& mcParticles = mcReader.getTracks(source, event); - // mark tracks to be stored per event - // loop over stack of MC particles from end to beginning: daughters are stored after mothers - if (mFilterMC) { - for (int particle = mcParticles.size() - 1; particle >= 0; particle--) { - // we store all primary particles == particles put by generator - if (mcParticles[particle].isPrimary() || - o2::mcutils::MCTrackNavigator::isPhysicalPrimary(mcParticles[particle], mcParticles) || - o2::mcutils::MCTrackNavigator::isKeepPhysics(mcParticles[particle], mcParticles)) { - mToStore[Triplet_t(source, event, particle)] = 1; - } else { - continue; - } - // we store mothers and daughters of particles that are to be saved - int mother0 = mcParticles[particle].getMotherTrackId(); - if (mother0 != -1) { - mToStore[Triplet_t(source, event, mother0)] = 1; - } - int mother1 = mcParticles[particle].getSecondMotherTrackId(); - if (mother1 != -1) { - mToStore[Triplet_t(source, event, mother1)] = 1; - } - int daughter0 = mcParticles[particle].getFirstDaughterTrackId(); - if (daughter0 != -1) { - mToStore[Triplet_t(source, event, daughter0)] = 1; - } - int daughterL = mcParticles[particle].getLastDaughterTrackId(); - if (daughterL != -1) { - mToStore[Triplet_t(source, event, daughterL)] = 1; - } - } - // enumerate saved mc particles and their relatives to get mother/daughter relations - for (int particle = 0; particle < mcParticles.size(); particle++) { - auto mapItem = mToStore.find(Triplet_t(source, event, particle)); - if (mapItem != mToStore.end()) { - mapItem->second = tableIndex - 1; - tableIndex++; - } - } - } else { - // if all mc particles are stored, all mc particles will be enumerated - for (int particle = 0; particle < mcParticles.size(); particle++) { - mToStore[Triplet_t(source, event, particle)] = tableIndex - 1; - tableIndex++; - } - } - - // second part: fill survived mc tracks into the AOD table - for (int particle = 0; particle < mcParticles.size(); particle++) { - if (mToStore.find(Triplet_t(source, event, particle)) == mToStore.end()) { - continue; - } - int statusCode = 0; - uint8_t flags = 0; - if (!mcParticles[particle].isPrimary()) { - flags |= o2::aod::mcparticle::enums::ProducedByTransport; // mark as produced by transport - statusCode = mcParticles[particle].getProcess(); - } else { - statusCode = mcParticles[particle].getStatusCode().fullEncoding; - } - if (source == 0) { - flags |= o2::aod::mcparticle::enums::FromBackgroundEvent; // mark as particle from background event - } - if (o2::mcutils::MCTrackNavigator::isPhysicalPrimary(mcParticles[particle], mcParticles)) { - flags |= o2::aod::mcparticle::enums::PhysicalPrimary; // mark as physical primary - } - float weight = mcParticles[particle].getWeight(); - std::vector mothers; - int mcMother0 = mcParticles[particle].getMotherTrackId(); - auto item = mToStore.find(Triplet_t(source, event, mcMother0)); - if (item != mToStore.end()) { - mothers.push_back(item->second); - } - int mcMother1 = mcParticles[particle].getSecondMotherTrackId(); - item = mToStore.find(Triplet_t(source, event, mcMother1)); - if (item != mToStore.end()) { - mothers.push_back(item->second); - } - int daughters[2] = {-1, -1}; // slice - int mcDaughter0 = mcParticles[particle].getFirstDaughterTrackId(); - item = mToStore.find(Triplet_t(source, event, mcDaughter0)); - if (item != mToStore.end()) { - daughters[0] = item->second; - } - int mcDaughterL = mcParticles[particle].getLastDaughterTrackId(); - item = mToStore.find(Triplet_t(source, event, mcDaughterL)); - if (item != mToStore.end()) { - daughters[1] = item->second; - if (daughters[0] < 0) { - LOG(error) << "AOD problematic daughter case observed"; - daughters[0] = daughters[1]; /// Treat the case of first negative label (pruned in the kinematics) - } - } else { - daughters[1] = daughters[0]; - } - if (daughters[0] > daughters[1]) { - std::swap(daughters[0], daughters[1]); - } - auto pX = (float)mcParticles[particle].Px(); - auto pY = (float)mcParticles[particle].Py(); - auto pZ = (float)mcParticles[particle].Pz(); - auto energy = (float)mcParticles[particle].GetEnergy(); - mcParticlesCursor(0, - mcColId, - mcParticles[particle].GetPdgCode(), - statusCode, - flags, - mothers, - daughters, - truncateFloatFraction(weight, mMcParticleW), - truncateFloatFraction(pX, mMcParticleMom), - truncateFloatFraction(pY, mMcParticleMom), - truncateFloatFraction(pZ, mMcParticleMom), - truncateFloatFraction(energy, mMcParticleMom), - truncateFloatFraction((float)mcParticles[particle].Vx(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].Vy(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].Vz(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].T(), mMcParticlePos)); - } - mcReader.releaseTracksForSourceAndEvent(source, event); - } -} - +//------------------------------------------------------------------ void AODMcProducerWorkflowDPL::init(InitContext& ic) { mTimer.Stop(); @@ -180,13 +34,13 @@ void AODMcProducerWorkflowDPL::init(InitContext& ic) mRecoPass = ic.options().get("reco-pass"); mTFNumber = ic.options().get("aod-timeframe-id"); mFilterMC = ic.options().get("filter-mctracks"); - mTruncate = ic.options().get("enable-truncation"); + int truncate = ic.options().get("enable-truncation"); if (mTFNumber == -1L) { LOG(info) << "TFNumber will be obtained from CCDB"; } // set no truncation if selected by user - if (mTruncate != 1) { + if (truncate == 0) { LOG(info) << "Truncation is not used!"; mCollisionPosition = 0xFFFFFFFF; mMcParticleW = 0xFFFFFFFF; @@ -200,112 +54,223 @@ void AODMcProducerWorkflowDPL::init(InitContext& ic) // parse list of sim prefixes into vector mSimPrefix = ic.options().get("mckine-fname"); } + std::string hepmcUpdate = ic.options().get("hepmc-update"); + HepMCUpdate when = (hepmcUpdate == "never" // + ? HepMCUpdate::never // + : hepmcUpdate == "always" // + ? HepMCUpdate::always // + : hepmcUpdate == "all" // + ? HepMCUpdate::allKeys // + : HepMCUpdate::anyKey); + mXSectionUpdate = when; + mPdfInfoUpdate = when; + mHeavyIonUpdate = when; mTimer.Reset(); } +//------------------------------------------------------------------ +void AODMcProducerWorkflowDPL::updateHeader(CollisionCursor& collisionCursor, + XSectionCursor& xSectionCursor, + PdfInfoCursor& pdfInfoCursor, + HeavyIonCursor& heavyIonCursor, + const MCEventHeader& header, + int collisionID, // Index + int bcID, + float time, + short generatorID, + int sourceID) +{ + using aodmchelpers::updateHepMCHeavyIon; + using aodmchelpers::updateHepMCPdfInfo; + using aodmchelpers::updateHepMCXSection; + using aodmchelpers::updateMCCollisions; + + auto genID = updateMCCollisions(collisionCursor, + bcID, + time, + header, + generatorID, + sourceID, + mCollisionPosition); + mXSectionUpdate = (updateHepMCXSection(xSectionCursor, // + collisionID, // + genID, // + header, // + mXSectionUpdate) // + ? HepMCUpdate::always // + : HepMCUpdate::never); + mPdfInfoUpdate = (updateHepMCPdfInfo(pdfInfoCursor, // + collisionID, // + genID, // + header, // + mPdfInfoUpdate) // + ? HepMCUpdate::always // + : HepMCUpdate::never); + mHeavyIonUpdate = (updateHepMCHeavyIon(heavyIonCursor, // + collisionID, // + genID, // + header, // + mHeavyIonUpdate) // + ? HepMCUpdate::always // + : HepMCUpdate::never); +} + +//------------------------------------------------------------------ void AODMcProducerWorkflowDPL::run(ProcessingContext& pc) { mTimer.Start(false); uint64_t tfNumber = mTFNumber; - auto& mcCollisionsBuilder = pc.outputs().make(Output{"AOD", "MCCOLLISION"}); - auto& mcParticlesBuilder = pc.outputs().make(Output{"AOD", "MCPARTICLE_001"}); - auto& originTableBuilder = pc.outputs().make(Output{"AOD", "ORIGIN"}); - - auto mcCollisionsCursor = mcCollisionsBuilder.cursor(); - auto mcParticlesCursor = mcParticlesBuilder.cursor(); - auto originCursor = originTableBuilder.cursor(); - - std::unique_ptr mcReader; - - if (!mEnableEmbed) { - mcReader = std::make_unique(mSimPrefix, steer::MCKinematicsReader::Mode::kMCKine); + using namespace o2::aodmchelpers; + using namespace o2::aodhelpers; + + auto collisionsCursor = createTableCursor(pc); + auto particlesCursor = createTableCursor(pc); + auto originCursor = createTableCursor(pc); + auto xSectionCursor = createTableCursor(pc); + auto pdfInfoCursor = createTableCursor(pc); + auto heavyIonCursor = createTableCursor(pc); + + // --- Create our reader ------------------------------------------- + std::unique_ptr reader; + if (not mEnableEmbed) { + reader = + std::make_unique(mSimPrefix, + MCKinematicsReader::Mode::kMCKine); } else { - mcReader = std::make_unique("collisioncontext.root"); + reader = std::make_unique("collisioncontext.root"); } - // filling mcCollision table - - // TODO: figure out collision weight - float mcColWeight = 1.; + // --- Container of event indexes --------------------------------- + using EventInfo = std::vector>; + EventInfo eventInfo; + // --- Fill collision and HepMC aux tables ------------------------ // dummy time information - int bcID = 0; float time = 0; - auto updateMCCollisions = [this, mcColWeight, bcID, time, &mcCollisionsCursor](dataformats::MCEventHeader const& header, short generatorID) { - mcCollisionsCursor(0, - bcID, - generatorID, - truncateFloatFraction(header.GetX(), mCollisionPosition), - truncateFloatFraction(header.GetY(), mCollisionPosition), - truncateFloatFraction(header.GetZ(), mCollisionPosition), - truncateFloatFraction(time, mCollisionPosition), - truncateFloatFraction(mcColWeight, mCollisionPosition), - header.GetB()); - }; - - if (!mEnableEmbed) { // simply store all MC events into table + if (not mEnableEmbed) { + // simply store all MC events into table int icol = 0; - int nSources = mcReader->getNSources(); + int nSources = reader->getNSources(); for (int isrc = 0; isrc < nSources; isrc++) { short generatorID = isrc; - int nEvents = mcReader->getNEvents(isrc); + int nEvents = reader->getNEvents(isrc); for (int ievt = 0; ievt < nEvents; ievt++) { - auto& header = mcReader->getMCEventHeader(isrc, ievt); - updateMCCollisions(header, generatorID); - mMCColToEvSrc.emplace_back(std::vector{icol, isrc, ievt}); + auto& header = reader->getMCEventHeader(isrc, ievt); + updateHeader(collisionsCursor.cursor, + xSectionCursor.cursor, + pdfInfoCursor.cursor, + heavyIonCursor.cursor, + header, + ievt, + ievt, // BC is the same as collision index + time, + generatorID, + isrc); + + eventInfo.emplace_back(std::make_tuple(icol, isrc, ievt)); icol++; } } - } else { // treat embedded events using collisioncontext: injected events will be stored together with background events into the same collisions - int nMCCollisions = mcReader->getDigitizationContext()->getNCollisions(); - const auto& mcRecords = mcReader->getDigitizationContext()->getEventRecords(); - const auto& mcParts = mcReader->getDigitizationContext()->getEventParts(); - for (int icol = 0; icol < nMCCollisions; icol++) { - auto& colParts = mcParts[icol]; + } else { + // treat embedded events using collisioncontext: injected events + // will be stored together with background events into the same + // collisions + int nCollisions = reader->getDigitizationContext()->getNCollisions(); + const auto& records = reader->getDigitizationContext()->getEventRecords(); + const auto& parts = reader->getDigitizationContext()->getEventParts(); + for (int icol = 0; icol < nCollisions; icol++) { + auto& colParts = parts[icol]; auto nParts = colParts.size(); for (auto colPart : colParts) { auto eventID = colPart.entryID; auto sourceID = colPart.sourceID; - // enable embedding: if several colParts exist, then they are saved as one collision + // enable embedding: if several colParts exist, then they are + // saved as one collision if (nParts == 1 || sourceID == 0) { + // Make collision header from first source only short generatorID = sourceID; - auto& header = mcReader->getMCEventHeader(sourceID, eventID); - updateMCCollisions(header, generatorID); + auto& header = reader->getMCEventHeader(sourceID, eventID); + + updateHeader(collisionsCursor.cursor, + xSectionCursor.cursor, + pdfInfoCursor.cursor, + heavyIonCursor.cursor, + header, + icol, + icol, // BC is the same as collision index + time, + generatorID, + sourceID); } - mMCColToEvSrc.emplace_back(std::vector{icol, sourceID, eventID}); // point background and injected signal events to one collision + // point background and injected signal events to one collision + eventInfo.emplace_back(std::make_tuple(icol, sourceID, eventID)); } } } - std::sort(mMCColToEvSrc.begin(), mMCColToEvSrc.end(), - [](const std::vector& left, const std::vector& right) { return (left[0] < right[0]); }); - - // filling mc particles table - fillMCParticlesTable(*mcReader, mcParticlesCursor); - - mMCColToEvSrc.clear(); - mToStore.clear(); - - originCursor(0, tfNumber); + // Sort the event information + std::sort(eventInfo.begin(), eventInfo.end(), + [](typename EventInfo::const_reference left, + typename EventInfo::const_reference right) { // + return (std::get<0>(left) < std::get<0>(right)); + }); + + // Loop over available events and update the tracks table + size_t offset = 0; + for (auto& colInfo : eventInfo) { + int event = std::get<2>(colInfo); + int source = std::get<1>(colInfo); + int collisionID = std::get<0>(colInfo); + auto tracks = reader->getTracks(source, event); + + TrackToIndex preselect; + offset = updateParticles(particlesCursor.cursor, + collisionID, + tracks, + preselect, + offset, + mFilterMC, + source == 0, + mMcParticleW, + mMcParticleMom, + mMcParticlePos); + + reader->releaseTracksForSourceAndEvent(source, event); + } - // sending metadata to writer - if (!mIsMDSent) { - TString dataType = "MC"; - TString O2Version = o2::fullVersion(); - TString ROOTVersion = ROOT_RELEASE; - mMetaDataKeys = {"DataType", "Run", "O2Version", "ROOTVersion", "RecoPassName", "AnchorProduction", "AnchorPassName", "LPMProductionTag"}; - mMetaDataVals = {dataType, "3", O2Version, ROOTVersion, mRecoPass, mAnchorProd, mAnchorPass, mLPMProdTag}; - pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0, Lifetime::Timeframe}, mMetaDataKeys); - pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0, Lifetime::Timeframe}, mMetaDataVals); + // --- Update the origin and the time-frame ------------------------ + originCursor(tfNumber); + + // --- Sending metadata to writer if not done already -------------- + if (not mIsMDSent) { + TString o2Version = o2::fullVersion(); + std::vector metaDataKeys = {"DataType", + "Run", + "O2Version", + "ROOTVersion", + "RecoPassName", + "AnchorProduction", + "AnchorPassName", + "LPMProductionTag"}; + std::vector metaDataVals = {"MC", + "3", + TString{o2::fullVersion()}, + ROOT_RELEASE, + mRecoPass, + mAnchorProd, + mAnchorPass, + mLPMProdTag}; + pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0}, metaDataKeys); + pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0}, metaDataVals); mIsMDSent = true; } - pc.outputs().snapshot(Output{"TFN", "TFNumber", 0, Lifetime::Timeframe}, tfNumber); - pc.outputs().snapshot(Output{"TFF", "TFFilename", 0, Lifetime::Timeframe}, ""); + pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, tfNumber); + pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, ""); pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); @@ -313,7 +278,7 @@ void AODMcProducerWorkflowDPL::run(ProcessingContext& pc) mTimer.Stop(); } -void AODMcProducerWorkflowDPL::endOfStream(EndOfStreamContext& ec) +void AODMcProducerWorkflowDPL::endOfStream(EndOfStreamContext&) { LOGF(info, "aod producer dpl total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); @@ -321,15 +286,24 @@ void AODMcProducerWorkflowDPL::endOfStream(EndOfStreamContext& ec) DataProcessorSpec getAODMcProducerWorkflowSpec() { - std::vector outputs; - - outputs.emplace_back(OutputLabel{"O2mccollision"}, "AOD", "MCCOLLISION", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mcparticle_001"}, "AOD", "MCPARTICLE_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2origin"}, "AOD", "ORIGIN", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); - outputs.emplace_back(OutputSpec{"TFF", "TFFilename"}); - outputs.emplace_back(OutputSpec{"AMD", "AODMetadataKeys"}); - outputs.emplace_back(OutputSpec{"AMD", "AODMetadataVals"}); + using McCollisions = AODMcProducerWorkflowDPL::McCollisions; + using McParticles = AODMcProducerWorkflowDPL::McParticles; + using Origins = AODMcProducerWorkflowDPL::Origins; + using XSections = AODMcProducerWorkflowDPL::XSections; + using PdfInfos = AODMcProducerWorkflowDPL::PdfInfos; + using HeavyIons = AODMcProducerWorkflowDPL::HeavyIons; + + std::vector outputs{ + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputSpec{"TFN", "TFNumber"}, + OutputSpec{"TFF", "TFFilename"}, + OutputSpec{"AMD", "AODMetadataKeys"}, + OutputSpec{"AMD", "AODMetadataVals"}}; return DataProcessorSpec{ "aod-mc-producer-workflow", @@ -345,7 +319,11 @@ DataProcessorSpec getAODMcProducerWorkflowSpec() ConfigParamSpec{"reco-pass", VariantType::String, "", {"RecoPassName"}}, ConfigParamSpec{"filter-mctracks", VariantType::Int, 1, {"Store only physical primary MC tracks and their mothers/daughters. 0 -- off, != 0 -- on"}}, ConfigParamSpec{"enable-embedding", VariantType::Int, 0, {"Use collisioncontext.root to process embedded events"}}, - ConfigParamSpec{"mckine-fname", VariantType::String, "o2sim", {"MC kinematics file name prefix: e.g. 'o2sim', 'bkg', 'sgn_1'. Used only if 'enable-embedding' is 0"}}}}; + ConfigParamSpec{"mckine-fname", VariantType::String, "o2sim", {"MC kinematics file name prefix: e.g. 'o2sim', 'bkg', 'sgn_1'. Used only if 'enable-embedding' is 0"}}, + ConfigParamSpec{"hepmc-update", VariantType::String, "always", {"When to update HepMC Aux tables: always - force update, never - never update, all - if all keys are present, any - when any key is present (not valid yet)"}}}}; } } // namespace o2::aodmcproducer +// +// EOF +// diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index eb29ce2197bed..385a9930d8f66 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -12,8 +12,13 @@ /// @file AODProducerWorkflowSpec.cxx #include "AODProducerWorkflow/AODProducerWorkflowSpec.h" +#include "AODProducerWorkflow/AODMcProducerHelpers.h" +#include "AODProducerWorkflow/AODProducerHelpers.h" +#include "DataFormatsEMCAL/TriggerRecord.h" +#include "DataFormatsEMCAL/EventHandler.h" #include "DataFormatsFT0/RecPoints.h" #include "DataFormatsFDD/RecPoint.h" +#include "DataFormatsFV0/RecPoints.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DataFormatsCTP/Digits.h" #include "DataFormatsCTP/Configuration.h" @@ -25,43 +30,48 @@ #include "DataFormatsMCH/Cluster.h" #include "DataFormatsMID/Track.h" #include "DataFormatsMFT/TrackMFT.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "DataFormatsPHOS/EventHandler.h" #include "DataFormatsTPC/TrackTPC.h" -#include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsZDC/BCRecData.h" #include "DataFormatsZDC/ZDCEnergy.h" #include "DataFormatsZDC/ZDCTDCData.h" #include "DataFormatsParameters/GRPECSObject.h" -#include "CommonUtils/NameConf.h" #include "MathUtils/Utils.h" -#include "DetectorsBase/GeometryManager.h" #include "CCDB/BasicCCDBManager.h" #include "CommonConstants/Triggers.h" #include "CommonConstants/PhysicsConstants.h" #include "CommonDataFormat/InteractionRecord.h" #include "DataFormatsTRD/TrackTRD.h" #include "DataFormatsTRD/TrackTriggerRecord.h" +#include "DataFormatsTRD/CalibratedTracklet.h" +#include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsTRD/Tracklet64.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "Framework/AnalysisDataModel.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/DataTypes.h" -#include "Framework/InputRecordWalker.h" -#include "Framework/Logger.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "Framework/CCDBParamSpec.h" -#include "FDDBase/Constants.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "CommonUtils/KeyValParam.h" +#include "CommonUtils/NameConf.h" #include "FT0Base/Geometry.h" -#include "FV0Base/Geometry.h" #include "GlobalTracking/MatchTOF.h" #include "ReconstructionDataFormats/Cascade.h" +#include "GlobalTracking/MatchGlobalFwd.h" #include "MCHTracking/TrackExtrap.h" #include "MCHTracking/TrackParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsVertexing/PVertexerParams.h" +#include "ReconstructionDataFormats/GlobalFwdTrack.h" #include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/StrangeTrack.h" #include "ReconstructionDataFormats/Track.h" #include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/TrackMCHMID.h" -#include "ReconstructionDataFormats/GlobalFwdTrack.h" +#include "ReconstructionDataFormats/MatchInfoHMP.h" #include "ReconstructionDataFormats/V0.h" #include "ReconstructionDataFormats/VtxTrackIndex.h" #include "ReconstructionDataFormats/VtxTrackRef.h" @@ -70,6 +80,7 @@ #include "SimulationDataFormat/MCTrack.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "SimulationDataFormat/MCUtils.h" +#include "SimulationDataFormat/MCGenProperties.h" #include "ZDCBase/Constants.h" #include "TPCBase/ParameterElectronics.h" #include "GPUTPCGMMergedTrackHit.h" @@ -78,18 +89,26 @@ #include "TMath.h" #include "MathUtils/Utils.h" #include "Math/SMatrix.h" -#include "TMatrixD.h" #include "TString.h" -#include "TObjString.h" +#include +#include #include +#include +#include #include #include #include #include #include +#include "TLorentzVector.h" +#include "TVector3.h" +#include "MathUtils/Tsallis.h" +#include #ifdef WITH_OPENMP #include #endif +#include +#include using namespace o2::framework; using namespace o2::math_utils::detail; @@ -97,6 +116,7 @@ using PVertex = o2::dataformats::PrimaryVertex; using GIndex = o2::dataformats::VtxTrackIndex; using DataRequest = o2::globaltracking::DataRequest; using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; using SMatrix55Sym = ROOT::Math::SMatrix>; namespace o2::aodproducer @@ -171,7 +191,7 @@ void AODProducerWorkflowDPL::createCTPReadout(const o2::globaltracking::RecoCont uint64_t globalBC = trigger.getBCData().toLong(); auto t0entry = bcsMapT0triggers.find(globalBC); if (t0entry != bcsMapT0triggers.end()) { - auto ctpdig = std::find_if(ctpDigits.begin(), ctpDigits.end(), [globalBC](const o2::ctp::CTPDigit& dig) { return dig.intRecord.toLong() == globalBC; }); + auto ctpdig = std::find_if(ctpDigits.begin(), ctpDigits.end(), [globalBC](const o2::ctp::CTPDigit& dig) { return static_cast(dig.intRecord.toLong()) == globalBC; }); if (ctpdig != ctpDigits.end()) { // CTP digit existing from other trigger, merge detector class mask ctpdig->CTPClassMask |= std::bitset<64>(classmask); @@ -276,44 +296,12 @@ void AODProducerWorkflowDPL::collectBCs(const o2::globaltracking::RecoContainer& } } -uint64_t AODProducerWorkflowDPL::getTFNumber(const o2::InteractionRecord& tfStartIR, int runNumber) -{ - auto& mgr = o2::ccdb::BasicCCDBManager::instance(); - o2::ccdb::CcdbApi ccdb_api; - const std::string rct_path = "RCT/Info/RunInformation/"; - const std::string start_orbit_path = "Trigger/StartOrbit"; - - mgr.setURL(o2::base::NameConf::getCCDBServer()); - ccdb_api.init(o2::base::NameConf::getCCDBServer()); - - std::map* mapStartOrbit = mgr.get>(start_orbit_path); - int64_t ts = 0; - std::map metadata; - std::map headers; - const std::string run_path = Form("%s/%i", rct_path.data(), runNumber); - headers = ccdb_api.retrieveHeaders(run_path, metadata, -1); - ts = atol(headers["SOR"].c_str()); - - // ccdb returns timestamp in mus - // mus to ms - ts = ts / 1000; - - uint32_t initialOrbit = mapStartOrbit->at(runNumber); - uint16_t firstRecBC = tfStartIR.bc; - uint32_t firstRecOrbit = tfStartIR.orbit; - const o2::InteractionRecord firstRec(firstRecBC, firstRecOrbit); - ts += firstRec.bc2ns() / 1000000; - - return ts; -}; - template void AODProducerWorkflowDPL::addToTracksTable(TracksCursorType& tracksCursor, TracksCovCursorType& tracksCovCursor, - const o2::track::TrackParCov& track, int collisionID) + const o2::track::TrackParCov& track, int collisionID, aod::track::TrackTypeEnum type) { - tracksCursor(0, - collisionID, - o2::aod::track::TrackIU, + tracksCursor(collisionID, + type, truncateFloatFraction(track.getX(), mTrackX), truncateFloatFraction(track.getAlpha(), mTrackAlpha), track.getY(), @@ -324,8 +312,7 @@ void AODProducerWorkflowDPL::addToTracksTable(TracksCursorType& tracksCursor, Tr // trackscov float sY = TMath::Sqrt(track.getSigmaY2()), sZ = TMath::Sqrt(track.getSigmaZ2()), sSnp = TMath::Sqrt(track.getSigmaSnp2()), sTgl = TMath::Sqrt(track.getSigmaTgl2()), sQ2Pt = TMath::Sqrt(track.getSigma1Pt2()); - tracksCovCursor(0, - truncateFloatFraction(sY, mTrackCovDiag), + tracksCovCursor(truncateFloatFraction(sY, mTrackCovDiag), truncateFloatFraction(sZ, mTrackCovDiag), truncateFloatFraction(sSnp, mTrackCovDiag), truncateFloatFraction(sTgl, mTrackCovDiag), @@ -345,13 +332,20 @@ void AODProducerWorkflowDPL::addToTracksTable(TracksCursorType& tracksCursor, Tr template void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracksExtraCursor, TrackExtraInfo& extraInfoHolder) { + // In case of TPC-only tracks, do not truncate the time error since we encapsulate there a special encoding of + // the deltaFwd/Bwd times + auto trackTimeRes = extraInfoHolder.trackTimeRes; + if (!extraInfoHolder.isTPConly) { + trackTimeRes = truncateFloatFraction(trackTimeRes, mTrackTimeError); + } + // extra - tracksExtraCursor(0, - truncateFloatFraction(extraInfoHolder.tpcInnerParam, mTrack1Pt), + tracksExtraCursor(truncateFloatFraction(extraInfoHolder.tpcInnerParam, mTrack1Pt), extraInfoHolder.flags, - extraInfoHolder.itsClusterMap, + extraInfoHolder.itsClusterSizes, extraInfoHolder.tpcNClsFindable, extraInfoHolder.tpcNClsFindableMinusFound, + extraInfoHolder.tpcNClsFindableMinusPID, extraInfoHolder.tpcNClsFindableMinusCrossedRows, extraInfoHolder.tpcNClsShared, extraInfoHolder.trdPattern, @@ -366,11 +360,158 @@ void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracks truncateFloatFraction(extraInfoHolder.trackEtaEMCAL, mTrackPosEMCAL), truncateFloatFraction(extraInfoHolder.trackPhiEMCAL, mTrackPosEMCAL), truncateFloatFraction(extraInfoHolder.trackTime, mTrackTime), - truncateFloatFraction(extraInfoHolder.trackTimeRes, mTrackTimeError)); + trackTimeRes); +} + +template +void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACursor, TrackQA& trackQAInfoHolder) +{ + tracksQACursor( + trackQAInfoHolder.trackID, + mTrackQCRetainOnlydEdx ? 0.0f : truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), + truncateFloatFraction(trackQAInfoHolder.tpcdEdxNorm, mTrackSignal), + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.tpcdcaR, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.tpcdcaZ, + trackQAInfoHolder.tpcClusterByteMask, + trackQAInfoHolder.tpcdEdxMax0R, + trackQAInfoHolder.tpcdEdxMax1R, + trackQAInfoHolder.tpcdEdxMax2R, + trackQAInfoHolder.tpcdEdxMax3R, + trackQAInfoHolder.tpcdEdxTot0R, + trackQAInfoHolder.tpcdEdxTot1R, + trackQAInfoHolder.tpcdEdxTot2R, + trackQAInfoHolder.tpcdEdxTot3R, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContY, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContSnp, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContTgl, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContQ2Pt, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloY, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloSnp, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloTgl, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloQ2Pt, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dTofdX, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dTofdZ); +} + +template +void AODProducerWorkflowDPL::addToTRDsExtra(const o2::globaltracking::RecoContainer& recoData, TRDsExtraCursorType& trdExtraCursor, const GIndex& trkIdx, int trkTableIdx) +{ + int q0s[6] = {-1}, q1s[6] = {-1}, q2s[6] = {-1}; + float q0sCor[6] = {-1}, q1sCor[6] = {-1}, q2sCor[6] = {-1}; + float ttgls[6] = {-999}, tphis[6] = {-999}; + + auto contributorsGID = recoData.getSingleDetectorRefs(trkIdx); + if (!contributorsGID[GIndex::Source::TRD].isIndexSet()) { // should be redunant + return; + } + const auto& trk = recoData.getTrack(contributorsGID[GIndex::Source::TRD]); + o2::track::TrackPar trkC{contributorsGID[GIndex::Source::ITSTPC].isIndexSet() ? recoData.getTPCITSTrack(contributorsGID[GIndex::Source::ITSTPC]).getParamOut() : recoData.getTPCTrack(contributorsGID[GIndex::Source::TPC]).getParamOut()}; + const auto& trklets = recoData.getTRDTracklets(); + const auto& ctrklets = recoData.getTRDCalibratedTracklets(); + for (int iLay{0}; iLay < 6; ++iLay) { + q0s[iLay] = q1s[iLay] = q2s[iLay] = -1; + q0sCor[iLay] = q1sCor[iLay] = q2sCor[iLay] = -1; + tphis[iLay] = ttgls[iLay] = -999; + auto trkltId = trk.getTrackletIndex(iLay); + if (trkltId < 0) { + continue; + } + const auto& tracklet = trklets[trkltId]; + if (mTRDNoiseMap->isTrackletFromNoisyMCM(tracklet)) { + continue; + } + // we need to propagate into TRD local system + int trkltDet = tracklet.getDetector(); + int trkltSec = trkltDet / 30; + if (trkltSec != o2::math_utils::angle2Sector(trkC.getAlpha())) { + if (!trkC.rotate(o2::math_utils::sector2Angle(trkltSec))) { + break; + } + } + if (!o2::base::Propagator::Instance()->PropagateToXBxByBz(trkC, ctrklets[trkltId].getX(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr)) { + break; + } + + auto tphi = trkC.getSnp() / std::sqrt((1.f - trkC.getSnp()) * (1.f + trkC.getSnp())); + auto trackletLength = std::sqrt(1.f + tphi * tphi + trkC.getTgl() * trkC.getTgl()); + float cor = mTRDLocalGain->getValue(tracklet.getHCID() / 2, tracklet.getPadCol(), tracklet.getPadRow()) * mTRDGainCalib->getMPVdEdx(tracklet.getDetector()) / o2::trd::constants::MPVDEDXDEFAULT * trackletLength; + q0s[iLay] = tracklet.getQ0(); + q1s[iLay] = tracklet.getQ1(); + q2s[iLay] = tracklet.getQ2(); + q0sCor[iLay] = (float)tracklet.getQ0() / cor; + q1sCor[iLay] = (float)tracklet.getQ1() / cor; + q2sCor[iLay] = (float)tracklet.getQ2() / cor; + ttgls[iLay] = trkC.getTgl(); + tphis[iLay] = tphi; + + // z-row merging, we want to merge only with tracklets from the same trigger record + if (trk.getIsCrossingNeighbor(iLay) && trk.getHasNeighbor()) { + // find the trigger the tracklet belongs to + auto trigsTRD = recoData.getTRDTriggerRecords(); + size_t trdSelID = -1; + + const auto& trig = trigsTRD[mCurrentTRDTrigID]; + bool foundTRDTrigger = false; + // first check current trigger + if (trkltId >= trig.getFirstTracklet() && trkltId < trig.getFirstTracklet() + trig.getNumberOfTracklets()) { + trdSelID = mCurrentTRDTrigID; + foundTRDTrigger = true; + } else { + // then check next trigger + if (mCurrentTRDTrigID < trigsTRD.size() - 1) { + const auto& trig = trigsTRD[mCurrentTRDTrigID + 1]; + if (trkltId >= trig.getFirstTracklet() && trkltId < trig.getFirstTracklet() + trig.getNumberOfTracklets()) { + trdSelID = mCurrentTRDTrigID + 1; + foundTRDTrigger = true; + } + } + } + + size_t low = 0, up = trigsTRD.size() - 1; + + // otherwise binary search + while (low <= up && !foundTRDTrigger) { + trdSelID = low + std::floor((up - low) / 2); + const auto& trig = trigsTRD[trdSelID]; + if (trig.getFirstTracklet() > trkltId) { + up = trdSelID - 1; + } else { + if (trig.getFirstTracklet() + trig.getNumberOfTracklets() <= trkltId) { + low = trdSelID + 1; + } else { + foundTRDTrigger = true; + } + } + } + //------------------- + mCurrentTRDTrigID = trdSelID; + const auto& trigSel = trigsTRD[trdSelID]; + + // loop on other tracklets from the same trigger record + for (const auto& trklt : trklets.subspan(trigSel.getFirstTracklet(), trigSel.getNumberOfTracklets())) { + if (tracklet.getTrackletWord() == trklt.getTrackletWord() || tracklet.getDetector() != trklt.getDetector()) { + continue; + } + if (std::abs(tracklet.getPadCol() - trklt.getPadCol()) <= 1 && std::abs(tracklet.getPadRow() - trklt.getPadRow()) == 1) { + cor = mTRDLocalGain->getValue(trklt.getHCID() / 2, trklt.getPadCol(), trklt.getPadRow()) * mTRDGainCalib->getMPVdEdx(tracklet.getDetector()) / o2::trd::constants::MPVDEDXDEFAULT * trackletLength; + q0s[iLay] += trklt.getQ0(); + q1s[iLay] += trklt.getQ1(); + q2s[iLay] += trklt.getQ2(); + q0sCor[iLay] += (float)trklt.getQ0() / cor; + q1sCor[iLay] += (float)trklt.getQ1() / cor; + q2sCor[iLay] += (float)trklt.getQ2() / cor; + } + } + } + } + + trdExtraCursor(trkTableIdx, q0s, q1s, q2s, q0sCor, q1sCor, q2sCor, ttgls, tphis); } -template -void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, +template +void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap) { @@ -378,9 +519,9 @@ void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksC int bcSlice[2] = {-1, -1}; const auto& track = data.getMFTTrack(trackID); const auto& rof = data.getMFTTracksROFRecords()[mMFTROFs[trackID.getIndex()]]; - float trackTime = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mMFTROFrameHalfLengthNS; + float trackTime = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mMFTROFrameHalfLengthNS + mMFTROFBiasNS; float trackTimeRes = mMFTROFrameHalfLengthNS; - bool needBCSlice = collisionID < 0 || trackID.isAmbiguous(); + bool needBCSlice = collisionID < 0; std::uint64_t bcOfTimeRef; if (needBCSlice) { double error = mTimeMarginTrackTime + trackTimeRes; @@ -390,26 +531,52 @@ void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksC } trackTime -= bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS; - mftTracksCursor(0, - collisionID, + // the Cellular Automaton track-finding algorithm flag is stored in first of the 4 bits not used for the cluster size + uint64_t mftClusterSizesAndTrackFlags = track.getClusterSizes(); + mftClusterSizesAndTrackFlags |= (track.isCA()) ? (1ULL << (60)) : 0; + + mftTracksCursor(collisionID, track.getX(), track.getY(), truncateFloatFraction(track.getZ(), mTrackX), // for the forward tracks Z has the same role as X in barrel truncateFloatFraction(track.getPhi(), mTrackAlpha), truncateFloatFraction(track.getTanl(), mTrackTgl), truncateFloatFraction(track.getInvQPt(), mTrack1Pt), - track.getNumberOfPoints(), + mftClusterSizesAndTrackFlags, truncateFloatFraction(track.getTrackChi2(), mTrackChi2), truncateFloatFraction(trackTime, mTrackTime), truncateFloatFraction(trackTimeRes, mTrackTimeError)); + if (mStoreAllMFTCov) { + float sX = TMath::Sqrt(track.getSigma2X()); + float sY = TMath::Sqrt(track.getSigma2Y()); + float sPhi = TMath::Sqrt(track.getSigma2Phi()); + float sTgl = TMath::Sqrt(track.getSigma2Tanl()); + float sQ2Pt = TMath::Sqrt(track.getSigma2InvQPt()); + + mftTracksCovCursor(mTableTrMFTID, + truncateFloatFraction(sX, mTrackCovDiag), + truncateFloatFraction(sY, mTrackCovDiag), + truncateFloatFraction(sPhi, mTrackCovDiag), + truncateFloatFraction(sTgl, mTrackCovDiag), + truncateFloatFraction(sQ2Pt, mTrackCovDiag), + (Char_t)(128. * track.getCovariances()(0, 1) / (sX * sY)), + (Char_t)(128. * track.getCovariances()(0, 2) / (sPhi * sX)), + (Char_t)(128. * track.getCovariances()(1, 2) / (sPhi * sY)), + (Char_t)(128. * track.getCovariances()(0, 3) / (sTgl * sX)), + (Char_t)(128. * track.getCovariances()(1, 3) / (sTgl * sY)), + (Char_t)(128. * track.getCovariances()(2, 3) / (sTgl * sPhi)), + (Char_t)(128. * track.getCovariances()(0, 4) / (sQ2Pt * sX)), + (Char_t)(128. * track.getCovariances()(1, 4) / (sQ2Pt * sY)), + (Char_t)(128. * track.getCovariances()(2, 4) / (sQ2Pt * sPhi)), + (Char_t)(128. * track.getCovariances()(3, 4) / (sQ2Pt * sTgl))); + } if (needBCSlice) { - ambigMFTTracksCursor(0, mTableTrMFTID, bcSlice); + ambigMFTTracksCursor(mTableTrMFTID, bcSlice); } } - -template +template void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, std::uint64_t collisionBC, const o2::dataformats::VtxTrackRef& trackRef, @@ -418,12 +585,16 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, TracksCursorType& tracksCursor, TracksCovCursorType& tracksCovCursor, TracksExtraCursorType& tracksExtraCursor, + TracksQACursorType& tracksQACursor, + TRDsExtraCursor& trdsExtraCursor, AmbigTracksCursorType& ambigTracksCursor, MFTTracksCursorType& mftTracksCursor, + MFTTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, AmbigFwdTracksCursorType& ambigFwdTracksCursor, + FwdTrkClsCursorType& fwdTrkClsCursor, const std::map& bcsMap) { for (int src = GIndex::NSources; src--;) { @@ -432,39 +603,109 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, } int start = trackRef.getFirstEntryOfSource(src); int end = start + trackRef.getEntriesOfSource(src); + int nToReserve = end - start; // + last index for a given table + if (src == GIndex::Source::MFT) { + mftTracksCursor.reserve(nToReserve + mftTracksCursor.lastIndex() + 1); + if (mStoreAllMFTCov) { + mftTracksCovCursor.reserve(nToReserve + mftTracksCovCursor.lastIndex() + 1); + } + } else if (src == GIndex::Source::MCH || src == GIndex::Source::MFTMCH || src == GIndex::Source::MCHMID) { + fwdTracksCursor.reserve(nToReserve + fwdTracksCursor.lastIndex() + 1); + fwdTracksCovCursor.reserve(nToReserve + fwdTracksCovCursor.lastIndex() + 1); + if (!mStoreAllMFTCov && src == GIndex::Source::MFTMCH) { + mftTracksCovCursor.reserve(nToReserve + mftTracksCovCursor.lastIndex() + 1); + } + } else { + tracksCursor.reserve(nToReserve + tracksCursor.lastIndex() + 1); + tracksCovCursor.reserve(nToReserve + tracksCovCursor.lastIndex() + 1); + tracksExtraCursor.reserve(nToReserve + tracksExtraCursor.lastIndex() + 1); + } for (int ti = start; ti < end; ti++) { - auto& trackIndex = GIndices[ti]; + const auto& trackIndex = GIndices[ti]; if (GIndex::includesSource(src, mInputSources)) { if (src == GIndex::Source::MFT) { // MFT tracks are treated separately since they are stored in a different table if (trackIndex.isAmbiguous() && mGIDToTableMFTID.find(trackIndex) != mGIDToTableMFTID.end()) { // was it already stored ? continue; } - addToMFTTracksTable(mftTracksCursor, ambigMFTTracksCursor, trackIndex, data, collisionID, collisionBC, bcsMap); + addToMFTTracksTable(mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, trackIndex, data, collisionID, collisionBC, bcsMap); mGIDToTableMFTID.emplace(trackIndex, mTableTrMFTID); mTableTrMFTID++; } else if (src == GIndex::Source::MCH || src == GIndex::Source::MFTMCH || src == GIndex::Source::MCHMID) { // FwdTracks tracks are treated separately since they are stored in a different table if (trackIndex.isAmbiguous() && mGIDToTableFwdID.find(trackIndex) != mGIDToTableFwdID.end()) { // was it already stored ? continue; } - addToFwdTracksTable(fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, trackIndex, data, collisionID, collisionBC, bcsMap); + addToFwdTracksTable(fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, mftTracksCovCursor, trackIndex, data, collisionID, collisionBC, bcsMap); mGIDToTableFwdID.emplace(trackIndex, mTableTrFwdID); + addClustersToFwdTrkClsTable(data, fwdTrkClsCursor, trackIndex, mTableTrFwdID); mTableTrFwdID++; } else { // barrel track: normal tracks table if (trackIndex.isAmbiguous() && mGIDToTableID.find(trackIndex) != mGIDToTableID.end()) { // was it already stored ? continue; } + + float weight = 0; + static std::uniform_real_distribution<> distr(0., 1.); + bool writeQAData = o2::math_utils::Tsallis::downsampleTsallisCharged(data.getTrackParam(trackIndex).getPt(), mTrackQCFraction, mSqrtS, weight, distr(mGenerator)) || ((src != GIndex::TPC || mGIDUsedBySVtx.find(trackIndex) != mGIDUsedBySVtx.end() || mGIDUsedByStr.find(trackIndex) != mGIDUsedByStr.end()) && mTrackQCKeepGlobalTracks); auto extraInfoHolder = processBarrelTrack(collisionID, collisionBC, trackIndex, data, bcsMap); - if (extraInfoHolder.trackTimeRes < 0.f) { // failed or rejected? + + if (writeQAData) { + auto trackQAInfoHolder = processBarrelTrackQA(collisionID, collisionBC, trackIndex, data, bcsMap); + if (std::bitset<8>(trackQAInfoHolder.tpcClusterByteMask).count() >= mTrackQCNTrCut) { + trackQAInfoHolder.trackID = mTableTrID; + // LOGP(info, "orig time0 in bc: {} diffBCRef: {}, ttime: {} -> {}", trackQAInfoHolder.tpcTime0*8, extraInfoHolder.diffBCRef, extraInfoHolder.trackTime, (trackQAInfoHolder.tpcTime0 * 8 - extraInfoHolder.diffBCRef) * o2::constants::lhc::LHCBunchSpacingNS - extraInfoHolder.trackTime); + trackQAInfoHolder.tpcTime0 = (trackQAInfoHolder.tpcTime0 * 8 - extraInfoHolder.diffBCRef) * o2::constants::lhc::LHCBunchSpacingNS - extraInfoHolder.trackTime; + // difference between TPC track time0 and stored track nominal time in ns instead of TF start + addToTracksQATable(tracksQACursor, trackQAInfoHolder); + } else { + writeQAData = false; + } + } + + // include specific selection of tpc standalone tracks if thinning is active + if (mThinTracks && extraInfoHolder.isTPConly && !writeQAData) { // if trackQA is written then no check has to be done + auto trk = data.getTPCTrack(trackIndex); + if (trk.getNClusters() >= mTrackQCNCls && trk.getPt() >= mTrackQCPt) { + o2::dataformats::DCA dcaInfo{999.f, 999.f, 999.f, 999.f, 999.f}; + o2::dataformats::VertexBase v = mVtx.getMeanVertex(collisionID < 0 ? 0.f : data.getPrimaryVertex(collisionID).getZ()); + if (o2::base::Propagator::Instance()->propagateToDCABxByBz(v, trk, 2., mMatCorr, &dcaInfo) && std::abs(dcaInfo.getY()) < mTrackQCDCAxy) { + writeQAData = true; // just setting this to not thin the track + } + } + } + + // Skip thinning if not enabled or track is not tpc standalone or assoc. to a V0 or qa'ed + if (mThinTracks && src == GIndex::Source::TPC && mGIDUsedBySVtx.find(trackIndex) == mGIDUsedBySVtx.end() && mGIDUsedByStr.find(trackIndex) == mGIDUsedByStr.end() && !writeQAData) { + mGIDToTableID.emplace(trackIndex, -1); // pretend skipped tracks are stored; this is safe since they are are not written to disk and -1 indicates to all users to not use this track + continue; + } + + if (!extraInfoHolder.isTPConly && extraInfoHolder.trackTimeRes < 0.f) { // failed or rejected? LOG(warning) << "Barrel track " << trackIndex << " has no time set, rejection is not expected : time=" << extraInfoHolder.trackTime << " timeErr=" << extraInfoHolder.trackTimeRes << " BCSlice: " << extraInfoHolder.bcSlice[0] << ":" << extraInfoHolder.bcSlice[1]; continue; } - addToTracksTable(tracksCursor, tracksCovCursor, data.getTrackParam(trackIndex), collisionID); + const auto& trOrig = data.getTrackParam(trackIndex); + bool isProp = false; + if (mPropTracks && trOrig.getX() < mMaxPropXiu && + mGIDUsedBySVtx.find(trackIndex) == mGIDUsedBySVtx.end() && + mGIDUsedByStr.find(trackIndex) == mGIDUsedByStr.end()) { // Do not propagate track assoc. to V0s and str. tracking + auto trackPar(trOrig); + isProp = propagateTrackToPV(trackPar, data, collisionID); + if (isProp) { + addToTracksTable(tracksCursor, tracksCovCursor, trackPar, collisionID, aod::track::Track); + } + } + if (!isProp) { + addToTracksTable(tracksCursor, tracksCovCursor, trOrig, collisionID, aod::track::TrackIU); + } addToTracksExtraTable(tracksExtraCursor, extraInfoHolder); - // collecting table indices of barrel tracks for V0s table - if (extraInfoHolder.bcSlice[0] >= 0) { - ambigTracksCursor(0, mTableTrID, extraInfoHolder.bcSlice); + if (mEnableTRDextra && trackIndex.includesDet(GIndex::Source::TRD)) { + addToTRDsExtra(data, trdsExtraCursor, trackIndex, mTableTrID); + } + // collecting table indices of barrel tracks for V0s table + if (extraInfoHolder.bcSlice[0] >= 0 && collisionID < 0) { + ambigTracksCursor(mTableTrID, extraInfoHolder.bcSlice); } mGIDToTableID.emplace(trackIndex, mTableTrID); mTableTrID++; @@ -472,6 +713,25 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, } } } + if (collisionID < 0) { + return; + } + /// Add strangeness tracks to the table + auto sTracks = data.getStrangeTracks(); + tracksCursor.reserve(mVertexStrLUT[collisionID + 1] + tracksCursor.lastIndex() + 1); + tracksCovCursor.reserve(mVertexStrLUT[collisionID + 1] + tracksCovCursor.lastIndex() + 1); + tracksExtraCursor.reserve(mVertexStrLUT[collisionID + 1] + tracksExtraCursor.lastIndex() + 1); + for (int iS{mVertexStrLUT[collisionID]}; iS < mVertexStrLUT[collisionID + 1]; ++iS) { + auto& collStrTrk = mCollisionStrTrk[iS]; + auto& sTrk = sTracks[collStrTrk.second]; + TrackExtraInfo extraInfo; + extraInfo.itsChi2NCl = sTrk.mTopoChi2; // TODO: this is the total chi2 of adding the ITS clusters, the topology chi2 meaning might change in the future + extraInfo.itsClusterSizes = sTrk.getClusterSizes(); + addToTracksTable(tracksCursor, tracksCovCursor, sTrk.mMother, collisionID, aod::track::StrangeTrack); + addToTracksExtraTable(tracksExtraCursor, extraInfo); + mStrTrkIndices[collStrTrk.second] = mTableTrID; + mTableTrID++; + } } void AODProducerWorkflowDPL::fillIndexTablesPerCollision(const o2::dataformats::VtxTrackRef& trackRef, const gsl::span& GIndices, const o2::globaltracking::RecoContainer& data) @@ -510,9 +770,9 @@ void AODProducerWorkflowDPL::fillIndexTablesPerCollision(const o2::dataformats:: } } -template +template void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, - AmbigFwdTracksCursorType& ambigFwdTracksCursor, GIndex trackID, + AmbigFwdTracksCursorType& ambigFwdTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap) { @@ -565,10 +825,12 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC } o2::mch::TrackParam trackParamAtVertex(track.getZ(), track.getParameters(), track.getCovariances()); - double errVtx{0.0}; // FIXME: get errors associated with vertex if available - double errVty{0.0}; - if (!o2::mch::TrackExtrap::extrapToVertex(trackParamAtVertex, vx, vy, vz, errVtx, errVty)) { - return false; + if (mPropMuons) { + double errVtx{0.0}; // FIXME: get errors associated with vertex if available + double errVty{0.0}; + if (!o2::mch::TrackExtrap::extrapToVertex(trackParamAtVertex, vx, vy, vz, errVtx, errVty)) { + return false; + } } // extrapolate to DCA @@ -590,43 +852,37 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC double xAbs = trackParamAtRAbs.getNonBendingCoor(); double yAbs = trackParamAtRAbs.getBendingCoor(); - double px = trackParamAtVertex.px(); - double py = trackParamAtVertex.py(); - double pz = trackParamAtVertex.pz(); - - double pt = std::sqrt(px * px + py * py); - double dphi = std::atan2(py, px); - double dtanl = pz / pt; - double dinvqpt = 1.0 / (trackParamAtVertex.getCharge() * pt); double dpdca = track.getP() * dca; double dchi2 = track.getChi2OverNDF(); - fwdInfo.x = trackParamAtVertex.getNonBendingCoor(); - fwdInfo.y = trackParamAtVertex.getBendingCoor(); - fwdInfo.z = trackParamAtVertex.getZ(); + auto fwdmuon = mMatching.MCHtoFwd(trackParamAtVertex); + + fwdInfo.x = fwdmuon.getX(); + fwdInfo.y = fwdmuon.getY(); + fwdInfo.z = fwdmuon.getZ(); + fwdInfo.phi = fwdmuon.getPhi(); + fwdInfo.tanl = fwdmuon.getTgl(); + fwdInfo.invqpt = fwdmuon.getInvQPt(); fwdInfo.rabs = std::sqrt(xAbs * xAbs + yAbs * yAbs); - fwdInfo.phi = dphi; - fwdInfo.tanl = dtanl; - fwdInfo.invqpt = dinvqpt; fwdInfo.chi2 = dchi2; fwdInfo.pdca = dpdca; fwdInfo.nClusters = track.getNClusters(); - fwdCovInfo.sigX = TMath::Sqrt(trackParamAtVertex.getCovariances()(0, 0)); - fwdCovInfo.sigY = TMath::Sqrt(trackParamAtVertex.getCovariances()(1, 1)); - fwdCovInfo.sigPhi = TMath::Sqrt(trackParamAtVertex.getCovariances()(2, 2)); - fwdCovInfo.sigTgl = TMath::Sqrt(trackParamAtVertex.getCovariances()(3, 3)); - fwdCovInfo.sig1Pt = TMath::Sqrt(trackParamAtVertex.getCovariances()(4, 4)); - fwdCovInfo.rhoXY = (Char_t)(128. * trackParamAtVertex.getCovariances()(0, 1) / (fwdCovInfo.sigX * fwdCovInfo.sigY)); - fwdCovInfo.rhoPhiX = (Char_t)(128. * trackParamAtVertex.getCovariances()(0, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigX)); - fwdCovInfo.rhoPhiY = (Char_t)(128. * trackParamAtVertex.getCovariances()(1, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigY)); - fwdCovInfo.rhoTglX = (Char_t)(128. * trackParamAtVertex.getCovariances()(0, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigX)); - fwdCovInfo.rhoTglY = (Char_t)(128. * trackParamAtVertex.getCovariances()(1, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigY)); - fwdCovInfo.rhoTglPhi = (Char_t)(128. * trackParamAtVertex.getCovariances()(2, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigPhi)); - fwdCovInfo.rho1PtX = (Char_t)(128. * trackParamAtVertex.getCovariances()(0, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigX)); - fwdCovInfo.rho1PtY = (Char_t)(128. * trackParamAtVertex.getCovariances()(1, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigY)); - fwdCovInfo.rho1PtPhi = (Char_t)(128. * trackParamAtVertex.getCovariances()(2, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigPhi)); - fwdCovInfo.rho1PtTgl = (Char_t)(128. * trackParamAtVertex.getCovariances()(3, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigTgl)); + fwdCovInfo.sigX = TMath::Sqrt(fwdmuon.getCovariances()(0, 0)); + fwdCovInfo.sigY = TMath::Sqrt(fwdmuon.getCovariances()(1, 1)); + fwdCovInfo.sigPhi = TMath::Sqrt(fwdmuon.getCovariances()(2, 2)); + fwdCovInfo.sigTgl = TMath::Sqrt(fwdmuon.getCovariances()(3, 3)); + fwdCovInfo.sig1Pt = TMath::Sqrt(fwdmuon.getCovariances()(4, 4)); + fwdCovInfo.rhoXY = (Char_t)(128. * fwdmuon.getCovariances()(0, 1) / (fwdCovInfo.sigX * fwdCovInfo.sigY)); + fwdCovInfo.rhoPhiX = (Char_t)(128. * fwdmuon.getCovariances()(0, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigX)); + fwdCovInfo.rhoPhiY = (Char_t)(128. * fwdmuon.getCovariances()(1, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigY)); + fwdCovInfo.rhoTglX = (Char_t)(128. * fwdmuon.getCovariances()(0, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigX)); + fwdCovInfo.rhoTglY = (Char_t)(128. * fwdmuon.getCovariances()(1, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigY)); + fwdCovInfo.rhoTglPhi = (Char_t)(128. * fwdmuon.getCovariances()(2, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigPhi)); + fwdCovInfo.rho1PtX = (Char_t)(128. * fwdmuon.getCovariances()(0, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigX)); + fwdCovInfo.rho1PtY = (Char_t)(128. * fwdmuon.getCovariances()(1, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigY)); + fwdCovInfo.rho1PtPhi = (Char_t)(128. * fwdmuon.getCovariances()(2, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigPhi)); + fwdCovInfo.rho1PtTgl = (Char_t)(128. * fwdmuon.getCovariances()(3, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigTgl)); return true; }; @@ -658,6 +914,8 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC fwdInfo.trackTimeRes = time.getTimeStampError() * 1.e3; } else { // This is a GlobalMuonTrack or a GlobalForwardTrack const auto& track = data.getGlobalFwdTrack(trackID); + const auto& mftTracks = data.getMFTTracks(); + const auto& mfttrack = mftTracks[track.getMFTTrackID()]; if (!extrapMCHTrack(track.getMCHTrackID())) { LOGF(warn, "Unable to extrapolate MCH track with ID %d! Dummy parameters will be used", track.getMCHTrackID()); } @@ -697,20 +955,41 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC fwdCovInfo.rho1PtTgl = (Char_t)(128. * track.getCovariances()(3, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigTgl)); fwdInfo.trackTypeId = (fwdInfo.chi2matchmchmid >= 0) ? o2::aod::fwdtrack::GlobalMuonTrack : o2::aod::fwdtrack::GlobalForwardTrack; + + float sX = TMath::Sqrt(mfttrack.getSigma2X()), sY = TMath::Sqrt(mfttrack.getSigma2Y()), sPhi = TMath::Sqrt(mfttrack.getSigma2Phi()), + sTgl = TMath::Sqrt(mfttrack.getSigma2Tanl()), sQ2Pt = TMath::Sqrt(mfttrack.getSigma2InvQPt()); + + if (!mStoreAllMFTCov) { + mftTracksCovCursor(fwdInfo.matchmfttrackid, + truncateFloatFraction(sX, mTrackCovDiag), + truncateFloatFraction(sY, mTrackCovDiag), + truncateFloatFraction(sPhi, mTrackCovDiag), + truncateFloatFraction(sTgl, mTrackCovDiag), + truncateFloatFraction(sQ2Pt, mTrackCovDiag), + (Char_t)(128. * mfttrack.getCovariances()(0, 1) / (sX * sY)), + (Char_t)(128. * mfttrack.getCovariances()(0, 2) / (sPhi * sX)), + (Char_t)(128. * mfttrack.getCovariances()(1, 2) / (sPhi * sY)), + (Char_t)(128. * mfttrack.getCovariances()(0, 3) / (sTgl * sX)), + (Char_t)(128. * mfttrack.getCovariances()(1, 3) / (sTgl * sY)), + (Char_t)(128. * mfttrack.getCovariances()(2, 3) / (sTgl * sPhi)), + (Char_t)(128. * mfttrack.getCovariances()(0, 4) / (sQ2Pt * sX)), + (Char_t)(128. * mfttrack.getCovariances()(1, 4) / (sQ2Pt * sY)), + (Char_t)(128. * mfttrack.getCovariances()(2, 4) / (sQ2Pt * sPhi)), + (Char_t)(128. * mfttrack.getCovariances()(3, 4) / (sQ2Pt * sTgl))); + } } std::uint64_t bcOfTimeRef; - bool needBCSlice = trackID.isAmbiguous() || collisionID < 0; + bool needBCSlice = collisionID < 0; if (needBCSlice) { // need to store BC slice float err = mTimeMarginTrackTime + fwdInfo.trackTimeRes; bcOfTimeRef = fillBCSlice(bcSlice, fwdInfo.trackTime - err, fwdInfo.trackTime + err, bcsMap); } else { - bcOfTimeRef = collisionBC - mStartIR.toLong(); // by default (unambiguous) track time is wrt collision BC + bcOfTimeRef = collisionBC - mStartIR.toLong(); // by default track time is wrt collision BC (unless no collision assigned) } fwdInfo.trackTime -= bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS; - fwdTracksCursor(0, - collisionID, + fwdTracksCursor(collisionID, fwdInfo.trackTypeId, fwdInfo.x, fwdInfo.y, @@ -733,8 +1012,7 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC truncateFloatFraction(fwdInfo.trackTime, mTrackTime), truncateFloatFraction(fwdInfo.trackTimeRes, mTrackTimeError)); - fwdTracksCovCursor(0, - truncateFloatFraction(fwdCovInfo.sigX, mTrackCovDiag), + fwdTracksCovCursor(truncateFloatFraction(fwdCovInfo.sigX, mTrackCovDiag), truncateFloatFraction(fwdCovInfo.sigY, mTrackCovDiag), truncateFloatFraction(fwdCovInfo.sigPhi, mTrackCovDiag), truncateFloatFraction(fwdCovInfo.sigTgl, mTrackCovDiag), @@ -751,51 +1029,100 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC fwdCovInfo.rho1PtTgl); if (needBCSlice) { - ambigFwdTracksCursor(0, mTableTrFwdID, bcSlice); + ambigFwdTracksCursor(mTableTrFwdID, bcSlice); } } -void dimensionMCKeepStore(std::vector*>>& store, int Nsources, int NEvents) +//------------------------------------------------------------------ +void AODProducerWorkflowDPL::updateMCHeader(MCCollisionCursor& collisionCursor, + XSectionCursor& xSectionCursor, + PdfInfoCursor& pdfInfoCursor, + HeavyIonCursor& heavyIonCursor, + const MCEventHeader& header, + int collisionID, + int bcID, + float time, + short generatorID, + int sourceID) +{ + using aodmchelpers::updateHepMCHeavyIon; + using aodmchelpers::updateHepMCPdfInfo; + using aodmchelpers::updateHepMCXSection; + using aodmchelpers::updateMCCollisions; + + auto genID = updateMCCollisions(collisionCursor, + bcID, + time, + header, + generatorID, + sourceID, + mCollisionPosition); + mXSectionUpdate = (updateHepMCXSection(xSectionCursor, // + collisionID, // + genID, // + header, // + mXSectionUpdate) + ? HepMCUpdate::always + : HepMCUpdate::never); + mPdfInfoUpdate = (updateHepMCPdfInfo(pdfInfoCursor, // + collisionID, // + genID, // + header, // + mPdfInfoUpdate) + ? HepMCUpdate::always + : HepMCUpdate::never); + mHeavyIonUpdate = (updateHepMCHeavyIon(heavyIonCursor, // + collisionID, // + genID, // + header, + mHeavyIonUpdate) + ? HepMCUpdate::always + : HepMCUpdate::never); +} + +void dimensionMCKeepStore(std::vector>>& store, int Nsources, int NEvents) { store.resize(Nsources); for (int s = 0; s < Nsources; ++s) { - store[s].resize(NEvents, nullptr); + store[s].resize(NEvents); } } -void clearMCKeepStore(std::vector*>>& store) +void clearMCKeepStore(std::vector>>& store) { - for (int s = 0; s < store.size(); ++s) { - for (int e = 0; e < store[s].size(); ++e) { - if (store[s][e]) { - store[s][e]->clear(); - } + for (auto s = 0U; s < store.size(); ++s) { + for (auto e = 0U; e < store[s].size(); ++e) { + store[s][e].clear(); } } } // helper function to add a particle/track to the MC keep store -void keepMCParticle(std::vector*>>& store, int source, int event, int track, int value = 1) +void keepMCParticle(std::vector>>& store, int source, int event, int track, int value = 1, bool useSigFilt = false) { - if (!store[source][event]) { - store[source][event] = new std::unordered_map; + if (track < 0) { + LOG(warn) << "trackID is smaller than 0. Neglecting"; + return; + } + if (useSigFilt && source == 0) { + store[source][event][track] = -1; + } else { + store[source][event][track] = value; } - (*store[source][event])[track] = value; } -template void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader, - const MCParticlesCursorType& mcParticlesCursor, + MCParticlesCursor& mcParticlesCursor, const gsl::span& primVer2TRefs, const gsl::span& GIndices, const o2::globaltracking::RecoContainer& data, - const std::vector>& mcColToEvSrc) + const std::vector& mcColToEvSrc) { int NSources = 0; int NEvents = 0; for (auto& p : mcColToEvSrc) { - NSources = std::max(p[1], NSources); - NEvents = std::max(p[2], NEvents); + NSources = std::max(p.sourceID, NSources); + NEvents = std::max(p.eventID, NEvents); } NSources++; // 0 - indexed NEvents++; @@ -810,7 +1137,7 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcLabel.isValid()) { return; } - keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID()); + keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID(), 1, mUseSigFiltMC); }; // mark reconstructed MC particles to store them into the table @@ -825,7 +1152,7 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcTruth.isValid()) { continue; } - keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID()); + keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID(), 1, mUseSigFiltMC); // treating contributors of global tracks auto contributorsGID = data.getSingleDetectorRefs(trackIndex); if (contributorsGID[GIndex::Source::TPC].isIndexSet()) { @@ -840,167 +1167,81 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcLabel.isValid()) { continue; } - keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID()); + keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID(), 1, mUseSigFiltMC); } } } } } } - int tableIndex = 1; - for (auto& colInfo : mcColToEvSrc) { // loop over " <-> combined MC col. ID" key pairs - int event = colInfo[2]; - int source = colInfo[1]; - int mcColId = colInfo[0]; - std::vector const& mcParticles = mcReader.getTracks(source, event); - // mark tracks to be stored per event - // loop over stack of MC particles from end to beginning: daughters are stored after mothers - if (mRecoOnly) { - for (int particle = mcParticles.size() - 1; particle >= 0; particle--) { - // we store all primary particles == particles put by generator - if (mcParticles[particle].isPrimary()) { - keepMCParticle(mToStore, source, event, particle); - } else if (o2::mcutils::MCTrackNavigator::isPhysicalPrimary(mcParticles[particle], mcParticles)) { - keepMCParticle(mToStore, source, event, particle); - } else if (o2::mcutils::MCTrackNavigator::isKeepPhysics(mcParticles[particle], mcParticles)) { - keepMCParticle(mToStore, source, event, particle); - } - - // skip treatment if this particle has not been marked during reconstruction - // or based on criteria just above - if (mToStore[source][event] && mToStore[source][event]->find(particle) == mToStore[source][event]->end()) { - continue; - } - - int mother0 = mcParticles[particle].getMotherTrackId(); - // we store mothers and daughters of particles that are reconstructed - if (mother0 != -1) { - keepMCParticle(mToStore, source, event, mother0); - } - int mother1 = mcParticles[particle].getSecondMotherTrackId(); - if (mother1 != -1) { - keepMCParticle(mToStore, source, event, mother1); - } - int daughter0 = mcParticles[particle].getFirstDaughterTrackId(); - if (daughter0 != -1) { - keepMCParticle(mToStore, source, event, daughter0); - } - int daughterL = mcParticles[particle].getLastDaughterTrackId(); - if (daughterL != -1) { - keepMCParticle(mToStore, source, event, daughterL); - } - } - LOG(info) << "The fraction of MC particles kept is " << mToStore[source][event]->size() / (1. * mcParticles.size()) << " for source " << source << " and event " << event; - - particleIDsToKeep.clear(); - for (auto& p : *mToStore[source][event]) { - particleIDsToKeep.push_back(p.first); - } - std::sort(particleIDsToKeep.begin(), particleIDsToKeep.end()); - for (auto pid : particleIDsToKeep) { - (*mToStore[source][event])[pid] = tableIndex - 1; - tableIndex++; - } - } else { - // if all mc particles are stored, all mc particles will be enumerated - particleIDsToKeep.clear(); - for (int particle = 0; particle < mcParticles.size(); particle++) { - keepMCParticle(mToStore, source, event, particle, tableIndex - 1); - tableIndex++; - particleIDsToKeep.push_back(particle); + // mark calorimeter signals as reconstructed particles + if (mInputSources[GIndex::EMC]) { + auto& mcCaloEMCCellLabels = data.getEMCALCellsMCLabels()->getTruthArray(); + for (auto& mcTruth : mcCaloEMCCellLabels) { + if (!mcTruth.isValid()) { + continue; } + keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID(), 1, mUseSigFiltMC); } - - // second part: fill survived mc tracks into the AOD table - for (auto particle : particleIDsToKeep) { - int statusCode = 0; - uint8_t flags = 0; - if (!mcParticles[particle].isPrimary()) { - flags |= o2::aod::mcparticle::enums::ProducedByTransport; // mark as produced by transport - statusCode = mcParticles[particle].getProcess(); - } else { - statusCode = mcParticles[particle].getStatusCode().fullEncoding; - } - if (source == 0) { - flags |= o2::aod::mcparticle::enums::FromBackgroundEvent; // mark as particle from background event - } - if (o2::mcutils::MCTrackNavigator::isPhysicalPrimary(mcParticles[particle], mcParticles)) { - flags |= o2::aod::mcparticle::enums::PhysicalPrimary; // mark as physical primary - } - float weight = mcParticles[particle].getWeight(); - std::vector mothers; - int mcMother0 = mcParticles[particle].getMotherTrackId(); - auto item = mToStore[source][event]->find(mcMother0); - if (item != mToStore[source][event]->end()) { - mothers.push_back(item->second); - } - int mcMother1 = mcParticles[particle].getSecondMotherTrackId(); - item = mToStore[source][event]->find(mcMother1); - if (item != mToStore[source][event]->end()) { - mothers.push_back(item->second); - } - int daughters[2] = {-1, -1}; // slice - int mcDaughter0 = mcParticles[particle].getFirstDaughterTrackId(); - item = mToStore[source][event]->find(mcDaughter0); - if (item != mToStore[source][event]->end()) { - daughters[0] = item->second; - } - int mcDaughterL = mcParticles[particle].getLastDaughterTrackId(); - item = mToStore[source][event]->find(mcDaughterL); - if (item != mToStore[source][event]->end()) { - daughters[1] = item->second; - if (daughters[0] < 0) { - LOG(error) << "AOD problematic daughter case observed"; - daughters[0] = daughters[1]; /// Treat the case of first negative label (pruned in the kinematics) - } - } else { - daughters[1] = daughters[0]; - } - if (daughters[0] > daughters[1]) { - std::swap(daughters[0], daughters[1]); + } + if (mInputSources[GIndex::PHS]) { + auto& mcCaloPHOSCellLabels = data.getPHOSCellsMCLabels()->getTruthArray(); + for (auto& mcTruth : mcCaloPHOSCellLabels) { + if (!mcTruth.isValid()) { + continue; } - auto pX = (float)mcParticles[particle].Px(); - auto pY = (float)mcParticles[particle].Py(); - auto pZ = (float)mcParticles[particle].Pz(); - auto energy = (float)mcParticles[particle].GetEnergy(); - mcParticlesCursor(0, - mcColId, - mcParticles[particle].GetPdgCode(), - statusCode, - flags, - mothers, - daughters, - truncateFloatFraction(weight, mMcParticleW), - truncateFloatFraction(pX, mMcParticleMom), - truncateFloatFraction(pY, mMcParticleMom), - truncateFloatFraction(pZ, mMcParticleMom), - truncateFloatFraction(energy, mMcParticleMom), - truncateFloatFraction((float)mcParticles[particle].Vx(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].Vy(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].Vz(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].T(), mMcParticlePos)); + keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID(), 1, mUseSigFiltMC); } + } + using namespace aodmchelpers; + using MCTrackNavigator = o2::mcutils::MCTrackNavigator; + + size_t offset = 0; + for (auto& colInfo : mcColToEvSrc) { // loop over " <-> combined MC col. ID" key pairs + int event = colInfo.eventID; + int source = colInfo.sourceID; + int mcColId = colInfo.colIndex; + std::vector const& mcParticles = mcReader.getTracks(source, event); + LOG(debug) << "Event=" << event << " source=" << source << " collision=" << mcColId; + auto& preselect = mToStore[source][event]; + + offset = updateParticles(mcParticlesCursor, + mcColId, + mcParticles, + preselect, + offset, + mRecoOnly, + source == 0, // background + mMcParticleW, + mMcParticleMom, + mMcParticlePos, + mUseSigFiltMC); + mcReader.releaseTracksForSourceAndEvent(source, event); } } template -void AODProducerWorkflowDPL::fillMCTrackLabelsTable(const MCTrackLabelCursorType& mcTrackLabelCursor, - const MCMFTTrackLabelCursorType& mcMFTTrackLabelCursor, - const MCFwdTrackLabelCursorType& mcFwdTrackLabelCursor, +void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTrackLabelCursor, + MCMFTTrackLabelCursorType& mcMFTTrackLabelCursor, + MCFwdTrackLabelCursorType& mcFwdTrackLabelCursor, const o2::dataformats::VtxTrackRef& trackRef, const gsl::span& primVerGIs, - const o2::globaltracking::RecoContainer& data) + const o2::globaltracking::RecoContainer& data, + int vertexId) { // labelMask (temporary) usage: - // bit 13 -- ITS/TPC or TPC/TOF labels are not equal + // bit 13 -- ITS/TPC with ITS label (track of AB tracklet) different from TPC // bit 14 -- isNoise() == true - // bit 15 -- isFake() == true + // bit 15 -- isFake() == true (defined by the fakeness of the top level global track, i.e. if TOF is present, fake means that the track of the TPC label does not contribute to TOF cluster) // labelID = -1 -- label is not set for (int src = GIndex::NSources; src--;) { int start = trackRef.getFirstEntryOfSource(src); int end = start + trackRef.getEntriesOfSource(src); + mcMFTTrackLabelCursor.reserve(end - start + mcMFTTrackLabelCursor.lastIndex() + 1); + mcFwdTrackLabelCursor.reserve(end - start + mcFwdTrackLabelCursor.lastIndex() + 1); + mcTrackLabelCursor.reserve(end - start + mcTrackLabelCursor.lastIndex() + 1); for (int ti = start; ti < end; ti++) { const auto trackIndex = primVerGIs[ti]; @@ -1016,13 +1257,13 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(const MCTrackLabelCursorType if (GIndex::includesSource(src, mInputSources)) { auto mcTruth = data.getTrackMCLabel(trackIndex); - MCLabels labelHolder; + MCLabels labelHolder{}; if ((src == GIndex::Source::MFT) || (src == GIndex::Source::MFTMCH) || (src == GIndex::Source::MCH) || (src == GIndex::Source::MCHMID)) { // treating mft and fwd labels separately if (!needToStore(src == GIndex::Source::MFT ? mGIDToTableMFTID : mGIDToTableFwdID)) { continue; } if (mcTruth.isValid()) { // if not set, -1 will be stored - labelHolder.labelID = (*mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; + labelHolder.labelID = (mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; } if (mcTruth.isFake()) { labelHolder.fwdLabelMask |= (0x1 << 7); @@ -1031,84 +1272,84 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(const MCTrackLabelCursorType labelHolder.fwdLabelMask |= (0x1 << 6); } if (src == GIndex::Source::MFT) { - mcMFTTrackLabelCursor(0, - labelHolder.labelID, + mcMFTTrackLabelCursor(labelHolder.labelID, labelHolder.fwdLabelMask); } else { - mcFwdTrackLabelCursor(0, - labelHolder.labelID, + mcFwdTrackLabelCursor(labelHolder.labelID, labelHolder.fwdLabelMask); } } else { if (!needToStore(mGIDToTableID)) { continue; } - if (mcTruth.isValid()) { // if not set, -1 will be stored - labelHolder.labelID = (*mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; - } - // treating possible mismatches and fakes for global tracks - auto contributorsGID = data.getSingleDetectorRefs(trackIndex); - bool isSetTPC = contributorsGID[GIndex::Source::TPC].isIndexSet(); - bool isSetITS = contributorsGID[GIndex::Source::ITS].isIndexSet(); - bool isSetTOF = contributorsGID[GIndex::Source::TOF].isIndexSet(); - bool isTOFFake = true; - if (isSetTPC && (isSetITS || isSetTOF)) { - auto mcTruthTPC = data.getTrackMCLabel(contributorsGID[GIndex::Source::TPC]); - if (mcTruthTPC.isValid()) { - labelHolder.labelTPC = (*mToStore[mcTruthTPC.getSourceID()][mcTruthTPC.getEventID()])[mcTruthTPC.getTrackID()]; - labelHolder.labelID = labelHolder.labelTPC; + if (mcTruth.isValid()) { // if not set, -1 will be stored + labelHolder.labelID = (mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; // defined by TPC if it contributes, otherwise: by ITS + if (mcTruth.isFake()) { + labelHolder.labelMask |= (0x1 << 15); } - if (isSetITS) { - auto mcTruthITS = data.getTrackMCLabel(contributorsGID[GIndex::Source::ITS]); - if (mcTruthITS.isValid()) { - labelHolder.labelITS = (*mToStore[mcTruthITS.getSourceID()][mcTruthITS.getEventID()])[mcTruthITS.getTrackID()]; - } - if (labelHolder.labelITS != labelHolder.labelTPC) { - LOG(debug) << "ITS-TPC MCTruth: labelIDs do not match at " << trackIndex.getIndex() << ", src = " << src; - labelHolder.labelMask |= (0x1 << 13); + if (trackIndex.includesDet(DetID::TPC) && trackIndex.getSource() != GIndex::Source::TPC) { // this is global track + auto contributorsGID = data.getSingleDetectorRefs(trackIndex); + if (contributorsGID[GIndex::Source::ITSTPC].isIndexSet()) { // there is a match to ITS tracks or ITSAB tracklet! + if (data.getTrackMCLabel(contributorsGID[GIndex::Source::ITSTPC]).isFake()) { + labelHolder.labelMask |= (0x1 << 13); + } } } - if (isSetTOF) { - const auto& labelsTOF = data.getTOFClustersMCLabels()->getLabels(contributorsGID[GIndex::Source::TOF]); - for (auto& mcLabel : labelsTOF) { - if (!mcLabel.isValid()) { - continue; - } - if (mcLabel == labelHolder.labelTPC) { - isTOFFake = false; - break; + if (trackIndex.includesDet(DetID::ITS)) { + auto itsGID = data.getITSContributorGID(trackIndex); + auto itsSource = itsGID.getSource(); + if (itsSource == GIndex::ITS) { + auto& itsTrack = data.getITSTrack(itsGID); + for (unsigned int iL = 0; iL < 7; ++iL) { + if (itsTrack.isFakeOnLayer(iL)) { + labelHolder.labelMask |= (0x1 << iL); + } } + } else if (itsSource == GIndex::ITSAB) { + labelHolder.labelMask |= (data.getTrackMCLabel(itsGID).isFake() << 12); } } - } - if (mcTruth.isFake() || (isSetTOF && isTOFFake)) { - labelHolder.labelMask |= (0x1 << 15); - } - if (mcTruth.isNoise()) { + + } else if (mcTruth.isNoise()) { labelHolder.labelMask |= (0x1 << 14); } - mcTrackLabelCursor(0, - labelHolder.labelID, - labelHolder.labelMask); + mcTrackLabelCursor(labelHolder.labelID, labelHolder.labelMask); } } } } + + // filling the tables with the strangeness tracking labels + auto sTrackLabels = data.getStrangeTracksMCLabels(); + // check if vertexId and vertexId + 1 maps into mVertexStrLUT + if (!(vertexId < 0 || vertexId >= mVertexStrLUT.size() - 1)) { + mcTrackLabelCursor.reserve(mVertexStrLUT[vertexId + 1] + mcTrackLabelCursor.lastIndex() + 1); + for (int iS{mVertexStrLUT[vertexId]}; iS < mVertexStrLUT[vertexId + 1]; ++iS) { + auto& collStrTrk = mCollisionStrTrk[iS]; + auto& label = sTrackLabels[collStrTrk.second]; + MCLabels labelHolder; + labelHolder.labelID = label.isValid() ? (mToStore[label.getSourceID()][label.getEventID()])[label.getTrackID()] : -1; + labelHolder.labelMask = (label.isFake() << 15) | (label.isNoise() << 14); + mcTrackLabelCursor(labelHolder.labelID, labelHolder.labelMask); + } + } } template void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::RecoContainer& recoData, V0CursorType& v0Cursor, CascadeCursorType& cascadeCursor, Decay3BodyCursorType& decay3BodyCursor) { - auto v0s = recoData.getV0s(); - auto cascades = recoData.getCascades(); - auto decays3Body = recoData.getDecays3Body(); + auto v0s = recoData.getV0sIdx(); + auto cascades = recoData.getCascadesIdx(); + auto decays3Body = recoData.getDecays3BodyIdx(); + v0Cursor.reserve(v0s.size()); // filling v0s table for (size_t iv0 = 0; iv0 < v0s.size(); iv0++) { const auto& v0 = v0s[iv0]; auto trPosID = v0.getProngID(0); auto trNegID = v0.getProngID(1); + uint8_t v0flags = v0.getBits(); int posTableIdx = -1, negTableIdx = -1, collID = -1; auto item = mGIDToTableID.find(trPosID); if (item != mGIDToTableID.end()) { @@ -1129,12 +1370,13 @@ void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::Rec collID = itemV->second; } if (posTableIdx != -1 and negTableIdx != -1 and collID != -1) { - v0Cursor(0, collID, posTableIdx, negTableIdx); + v0Cursor(collID, posTableIdx, negTableIdx, v0flags); mV0ToTableID[int(iv0)] = mTableV0ID++; } } // filling cascades table + cascadeCursor.reserve(cascades.size()); for (auto& cascade : cascades) { auto itemV0 = mV0ToTableID.find(cascade.getV0ID()); if (itemV0 == mV0ToTableID.end()) { @@ -1156,10 +1398,11 @@ void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::Rec LOG(warn) << "Could not find cascade collisionID for the vertex ID " << cascade.getVertexID(); continue; } - cascadeCursor(0, collID, v0tableID, bachTableIdx); + cascadeCursor(collID, v0tableID, bachTableIdx); } // filling 3 body decays table + decay3BodyCursor.reserve(decays3Body.size()); for (size_t i3Body = 0; i3Body < decays3Body.size(); i3Body++) { const auto& decay3Body = decays3Body[i3Body]; GIndex trIDs[3]{ @@ -1173,7 +1416,7 @@ void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::Rec if (item != mGIDToTableID.end()) { tableIdx[i] = item->second; } else { - LOG(warn) << fmt::format("Could not find a track index for prong ID {}", trIDs[i]); + LOG(warn) << fmt::format("Could not find a track index for prong ID {}", (int)trIDs[i]); missing = true; } } @@ -1187,7 +1430,177 @@ void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::Rec if (missing) { continue; } - decay3BodyCursor(0, collID, tableIdx[0], tableIdx[1], tableIdx[2]); + decay3BodyCursor(collID, tableIdx[0], tableIdx[1], tableIdx[2]); + } +} + +template +void AODProducerWorkflowDPL::addClustersToFwdTrkClsTable(const o2::globaltracking::RecoContainer& recoData, FwdTrkClsCursorType& fwdTrkClsCursor, GIndex trackID, int fwdTrackId) +{ + const auto& mchTracks = recoData.getMCHTracks(); + const auto& mchmidMatches = recoData.getMCHMIDMatches(); + const auto& mchClusters = recoData.getMCHTrackClusters(); + + int mchTrackID = -1; + if (trackID.getSource() == GIndex::MCH) { // This is an MCH track + mchTrackID = trackID.getIndex(); + } else if (trackID.getSource() == GIndex::MCHMID) { // This is an MCH-MID track + auto mchmidMatch = mchmidMatches[trackID.getIndex()]; + mchTrackID = mchmidMatch.getMCHRef().getIndex(); + } // Others are Global Forward Tracks, their clusters will be or were added with the corresponding MCH track + + if (mchTrackID > -1 && mchTrackID < mchTracks.size()) { + const auto& mchTrack = mchTracks[mchTrackID]; + int first = mchTrack.getFirstClusterIdx(); + int last = mchTrack.getLastClusterIdx(); + fwdTrkClsCursor.reserve(last - first + 1 + fwdTrkClsCursor.lastIndex() + 1); + for (int i = first; i <= last; i++) { + const auto& cluster = mchClusters[i]; + fwdTrkClsCursor(fwdTrackId, + truncateFloatFraction(cluster.x, mMuonCl), + truncateFloatFraction(cluster.y, mMuonCl), + truncateFloatFraction(cluster.z, mMuonCl), + (((cluster.ey < 5.) & 0x1) << 12) | (((cluster.ex < 5.) & 0x1) << 11) | cluster.getDEId()); + } + } +} + +template +void AODProducerWorkflowDPL::fillHMPID(const o2::globaltracking::RecoContainer& recoData, HMPCursorType& hmpCursor) +{ + auto hmpMatches = recoData.getHMPMatches(); + + hmpCursor.reserve(hmpMatches.size()); + + // filling HMPs table + for (size_t iHmp = 0; iHmp < hmpMatches.size(); iHmp++) { + + const auto& match = hmpMatches[iHmp]; + + float xTrk, yTrk, theta, phi; + float xMip, yMip; + int charge, nph; + + match.getHMPIDtrk(xTrk, yTrk, theta, phi); + match.getHMPIDmip(xMip, yMip, charge, nph); + + auto photChargeVec = match.getPhotCharge(); + + float photChargeVec2[10]; // = {0.,0.,0.,0.,0.,0.,0.,0.,0.,0.}; + + for (Int_t i = 0; i < 10; i++) { + photChargeVec2[i] = photChargeVec[i]; + } + auto tref = mGIDToTableID.find(match.getTrackRef()); + if (tref != mGIDToTableID.end()) { + hmpCursor(tref->second, match.getHMPsignal(), xTrk, yTrk, xMip, yMip, nph, charge, match.getIdxHMPClus(), match.getHmpMom(), photChargeVec2); + } else { + LOG(error) << "Could not find AOD track table entry for HMP-matched track " << match.getTrackRef().asString(); + } + } +} + +void AODProducerWorkflowDPL::prepareStrangenessTracking(const o2::globaltracking::RecoContainer& recoData) +{ + auto v0s = recoData.getV0sIdx(); + auto cascades = recoData.getCascadesIdx(); + auto decays3Body = recoData.getDecays3BodyIdx(); + + int sTrkID = 0; + mCollisionStrTrk.clear(); + mCollisionStrTrk.reserve(recoData.getStrangeTracks().size()); + mVertexStrLUT.clear(); + mVertexStrLUT.resize(recoData.getPrimaryVertices().size() + 1, 0); + for (auto& sTrk : recoData.getStrangeTracks()) { + auto ITSIndex = GIndex{sTrk.mITSRef, GIndex::ITS}; + int vtxId{0}; + if (sTrk.mPartType == dataformats::kStrkV0) { + vtxId = v0s[sTrk.mDecayRef].getVertexID(); + } else if (sTrk.mPartType == dataformats::kStrkCascade) { + vtxId = cascades[sTrk.mDecayRef].getVertexID(); + } else { + vtxId = decays3Body[sTrk.mDecayRef].getVertexID(); + } + mCollisionStrTrk.emplace_back(vtxId, sTrkID++); + mVertexStrLUT[vtxId]++; + } + std::exclusive_scan(mVertexStrLUT.begin(), mVertexStrLUT.end(), mVertexStrLUT.begin(), 0); + + // sort by collision ID + std::sort(mCollisionStrTrk.begin(), mCollisionStrTrk.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); + mStrTrkIndices.clear(); + mStrTrkIndices.resize(mCollisionStrTrk.size(), -1); +} + +template +void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltracking::RecoContainer& recoData, V0C& v0Curs, CC& cascCurs, D3BC& d3BodyCurs) +{ + int itsTableIdx = -1; + int sTrkID = 0; + int nV0 = 0; + int nCasc = 0; + int nD3Body = 0; + + for (const auto& sTrk : recoData.getStrangeTracks()) { + if (sTrk.mPartType == dataformats::kStrkV0) { + nV0++; + } else if (sTrk.mPartType == dataformats::kStrkCascade) { + nCasc++; + } else { + nD3Body++; + } + } + + v0Curs.reserve(nV0); + cascCurs.reserve(nCasc); + d3BodyCurs.reserve(nD3Body); + + for (const auto& sTrk : recoData.getStrangeTracks()) { + auto ITSIndex = GIndex{sTrk.mITSRef, GIndex::ITS}; + auto item = mGIDToTableID.find(ITSIndex); + if (item != mGIDToTableID.end()) { + itsTableIdx = item->second; + } else { + LOG(warn) << "Could not find a ITS strange track index " << ITSIndex; + continue; + } + if (sTrk.mPartType == dataformats::kStrkV0) { + v0Curs(mStrTrkIndices[sTrkID++], + itsTableIdx, + sTrk.mDecayRef, + sTrk.mDecayVtx[0], + sTrk.mDecayVtx[1], + sTrk.mDecayVtx[2], + sTrk.mMasses[0], + sTrk.mMasses[1], + sTrk.mMatchChi2, + sTrk.mTopoChi2, + sTrk.getAverageClusterSize()); + } else if (sTrk.mPartType == dataformats::kStrkCascade) { + cascCurs(mStrTrkIndices[sTrkID++], + itsTableIdx, + sTrk.mDecayRef, + sTrk.mDecayVtx[0], + sTrk.mDecayVtx[1], + sTrk.mDecayVtx[2], + sTrk.mMasses[0], + sTrk.mMasses[1], + sTrk.mMatchChi2, + sTrk.mTopoChi2, + sTrk.getAverageClusterSize()); + } else { + d3BodyCurs(mStrTrkIndices[sTrkID++], + itsTableIdx, + sTrk.mDecayRef, + sTrk.mDecayVtx[0], + sTrk.mDecayVtx[1], + sTrk.mDecayVtx[2], + sTrk.mMasses[0], + sTrk.mMasses[1], + sTrk.mMatchChi2, + sTrk.mTopoChi2, + sTrk.getAverageClusterSize()); + } } } @@ -1216,7 +1629,7 @@ void AODProducerWorkflowDPL::countTPCClusters(const o2::globaltracking::RecoCont o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef()); unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex; clMap[rowIndex] = true; - if (tpcClusShMap[absoluteIndex] & GPUCA_NAMESPACE::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { if (!shMap[rowIndex]) { counters.shared++; } @@ -1236,6 +1649,7 @@ void AODProducerWorkflowDPL::countTPCClusters(const o2::globaltracking::RecoCont for (int j = i + 1; j < lim; j++) { if (clMap[j]) { counters.crossed++; + break; } } } @@ -1260,54 +1674,116 @@ uint8_t AODProducerWorkflowDPL::getTRDPattern(const o2::trd::TrackTRD& track) return pattern; } -template -void AODProducerWorkflowDPL::addToCaloTable(const TCaloHandler& caloHandler, const TCaloCursor& caloCellCursor, const TCaloTRGCursor& caloTRGCursor, - int eventID, int bcID, int8_t caloType) +template +void AODProducerWorkflowDPL::addToCaloTable(TCaloHandler& caloHandler, TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor, + TMCCaloLabelCursor& mcCaloCellLabelCursor, int eventID, int bcID, int8_t caloType) { auto inputEvent = caloHandler.buildEvent(eventID); - auto cellsInEvent = inputEvent.mCells; // get cells belonging to current event - for (auto& cell : cellsInEvent) { // loop over all cells in collision - caloCellCursor(0, - bcID, - CellHelper::getCellNumber(cell), - truncateFloatFraction(CellHelper::getAmplitude(cell), mCaloAmp), - truncateFloatFraction(CellHelper::getTimeStamp(cell), mCaloTime), - cell.getType(), + auto cellsInEvent = inputEvent.mCells; // get cells belonging to current event + auto cellMClabels = inputEvent.mMCCellLabels; // get MC labels belonging to current event (only implemented for EMCal currently!) + caloCellCursor.reserve(cellsInEvent.size() + caloCellCursor.lastIndex() + 1); + caloTRGCursor.reserve(cellsInEvent.size() + caloTRGCursor.lastIndex() + 1); + if (mUseMC) { + mcCaloCellLabelCursor.reserve(cellsInEvent.size() + mcCaloCellLabelCursor.lastIndex() + 1); + } + for (auto iCell = 0U; iCell < cellsInEvent.size(); iCell++) { + caloCellCursor(bcID, + CellHelper::getCellNumber(cellsInEvent[iCell]), + truncateFloatFraction(CellHelper::getAmplitude(cellsInEvent[iCell]), mCaloAmp), + truncateFloatFraction(CellHelper::getTimeStamp(cellsInEvent[iCell]), mCaloTime), + cellsInEvent[iCell].getType(), caloType); // 1 = emcal, -1 = undefined, 0 = phos // todo: fix dummy values in CellHelper when it is clear what is filled for trigger information - if (CellHelper::isTRU(cell)) { // Write only trigger cells into this table - caloTRGCursor(0, - bcID, - CellHelper::getFastOrAbsID(cell), - CellHelper::getLnAmplitude(cell), - CellHelper::getTriggerBits(cell), + if (CellHelper::isTRU(cellsInEvent[iCell])) { // Write only trigger cells into this table + caloTRGCursor(bcID, + CellHelper::getFastOrAbsID(cellsInEvent[iCell]), + CellHelper::getLnAmplitude(cellsInEvent[iCell]), + CellHelper::getTriggerBits(cellsInEvent[iCell]), caloType); } - } + if (mUseMC) { + // Common for PHOS and EMCAL + // loop over all MC Labels for the current cell + std::vector particleIds; + std::vector amplitudeFraction; + if (!mEMCselectLeading) { + particleIds.reserve(cellMClabels.size()); + amplitudeFraction.reserve(cellMClabels.size()); + } + float tmpMaxAmplitude = 0; + int32_t tmpindex = 0; + for (auto& mclabel : cellMClabels[iCell]) { + // do not fill noise lables! + if (mclabel.isValid()) { + if (mEMCselectLeading) { + if (mclabel.getAmplitudeFraction() > tmpMaxAmplitude) { + // Check if this MCparticle added to be kept? + if (mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()).find(mclabel.getTrackID()) != + mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()).end()) { + tmpMaxAmplitude = mclabel.getAmplitudeFraction(); + tmpindex = (mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID())).at(mclabel.getTrackID()); + } + } + } else { + auto trackStore = mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()); + auto iter = trackStore.find(mclabel.getTrackID()); + if (iter != trackStore.end()) { + amplitudeFraction.emplace_back(mclabel.getAmplitudeFraction()); + particleIds.emplace_back(iter->second); + } else { + particleIds.emplace_back(-1); // should the mc particle not be in mToStore make sure something (e.g. -1) is saved in particleIds so the length of particleIds is the same es amplitudeFraction! + amplitudeFraction.emplace_back(0.f); + LOG(warn) << "CaloTable: Could not find track for mclabel (" << mclabel.getSourceID() << "," << mclabel.getEventID() << "," << mclabel.getTrackID() << ") in the AOD MC store"; + if (mMCKineReader) { + auto mctrack = mMCKineReader->getTrack(mclabel); + TVector3 vec; + mctrack->GetStartVertex(vec); + LOG(warn) << " ... this track is of PDG " << mctrack->GetPdgCode() << " produced by " << mctrack->getProdProcessAsString() << " at (" << vec.X() << "," << vec.Y() << "," << vec.Z() << ")"; + } + } + } + } + } // end of loop over all MC Labels for the current cell + if (mEMCselectLeading) { + amplitudeFraction.emplace_back(tmpMaxAmplitude); + particleIds.emplace_back(tmpindex); + } + if (particleIds.size() == 0) { + particleIds.emplace_back(-1); + amplitudeFraction.emplace_back(0.f); + } + mcCaloCellLabelCursor(particleIds, + amplitudeFraction); + } + } // end of loop over cells in current event } // fill calo related tables (cells and calotrigger table) -template -void AODProducerWorkflowDPL::fillCaloTable(const TCaloCursor& caloCellCursor, const TCaloTRGCursor& caloTRGCursor, - const std::map& bcsMap, +template +void AODProducerWorkflowDPL::fillCaloTable(TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor, + TMCCaloLabelCursor& mcCaloCellLabelCursor, const std::map& bcsMap, const o2::globaltracking::RecoContainer& data) { // get calo information auto caloEMCCells = data.getEMCALCells(); auto caloEMCCellsTRGR = data.getEMCALTriggers(); + auto mcCaloEMCCellLabels = data.getEMCALCellsMCLabels(); auto caloPHOSCells = data.getPHOSCells(); auto caloPHOSCellsTRGR = data.getPHOSTriggers(); + auto mcCaloPHOSCellLabels = data.getPHOSCellsMCLabels(); if (!mInputSources[GIndex::PHS]) { caloPHOSCells = {}; caloPHOSCellsTRGR = {}; + mcCaloPHOSCellLabels = {}; } if (!mInputSources[GIndex::EMC]) { caloEMCCells = {}; caloEMCCellsTRGR = {}; + mcCaloEMCCellLabels = {}; } o2::emcal::EventHandler emcEventHandler; @@ -1316,9 +1792,11 @@ void AODProducerWorkflowDPL::fillCaloTable(const TCaloCursor& caloCellCursor, co // get cell belonging to an eveffillnt instead of timeframe emcEventHandler.reset(); emcEventHandler.setCellData(caloEMCCells, caloEMCCellsTRGR); + emcEventHandler.setCellMCTruthContainer(mcCaloEMCCellLabels); phsEventHandler.reset(); phsEventHandler.setCellData(caloPHOSCells, caloPHOSCellsTRGR); + phsEventHandler.setCellMCTruthContainer(mcCaloPHOSCellLabels); int emcNEvents = emcEventHandler.getNumberOfEvents(); int phsNEvents = phsEventHandler.getNumberOfEvents(); @@ -1352,11 +1830,11 @@ void AODProducerWorkflowDPL::fillCaloTable(const TCaloCursor& caloCellCursor, co } else { LOG(warn) << "Error: could not find a corresponding BC ID for a calo point; globalBC = " << globalBC << ", caloType = " << (int)caloType; } - if (caloType == 0) { // phs - addToCaloTable(phsEventHandler, caloCellCursor, caloTRGCursor, eventID, bcID, caloType); + if (caloType == 0) { // phos + addToCaloTable(phsEventHandler, caloCellCursor, caloTRGCursor, mcCaloCellLabelCursor, eventID, bcID, caloType); } if (caloType == 1) { // emc - addToCaloTable(emcEventHandler, caloCellCursor, caloTRGCursor, eventID, bcID, caloType); + addToCaloTable(emcEventHandler, caloCellCursor, caloTRGCursor, mcCaloCellLabelCursor, eventID, bcID, caloType); } } @@ -1367,16 +1845,51 @@ void AODProducerWorkflowDPL::init(InitContext& ic) { mTimer.Stop(); o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mLPMProdTag = ic.options().get("lpmp-prod-tag"); - mAnchorPass = ic.options().get("anchor-pass"); - mAnchorProd = ic.options().get("anchor-prod"); - mRecoPass = ic.options().get("reco-pass"); + mLPMProdTag = ic.options().get("lpmp-prod-tag"); + mAnchorPass = ic.options().get("anchor-pass"); + mAnchorProd = ic.options().get("anchor-prod"); + mUser = ic.options().get("created-by"); + mRecoPass = ic.options().get("reco-pass"); + mAODParent = ic.options().get("aod-parent"); mTFNumber = ic.options().get("aod-timeframe-id"); mRecoOnly = ic.options().get("reco-mctracks-only"); mTruncate = ic.options().get("enable-truncation"); mRunNumber = ic.options().get("run-number"); mCTPReadout = ic.options().get("ctpreadout-create"); mNThreads = std::max(1, ic.options().get("nthreads")); + mEMCselectLeading = ic.options().get("emc-select-leading"); + mThinTracks = ic.options().get("thin-tracks"); + mPropTracks = ic.options().get("propagate-tracks"); + mMaxPropXiu = ic.options().get("propagate-tracks-max-xiu"); + mPropMuons = ic.options().get("propagate-muons"); + mStoreAllMFTCov = ic.options().get("store-all-mft-cov"); + if (auto s = ic.options().get("with-streamers"); !s.empty()) { + mStreamerFlags.set(s); + if (mStreamerFlags) { + LOGP(info, "Writing streamer data with mask:"); + LOG(info) << mStreamerFlags; + } else { + LOGP(warn, "Specified non-default empty streamer mask!"); + } + } + mTrackQCKeepGlobalTracks = ic.options().get("trackqc-keepglobaltracks"); + mTrackQCRetainOnlydEdx = ic.options().get("trackqc-retainonlydedx"); + mTrackQCFraction = ic.options().get("trackqc-fraction"); + mTrackQCNTrCut = ic.options().get("trackqc-NTrCut"); + mTrackQCDCAxy = ic.options().get("trackqc-tpc-dca"); + mTrackQCPt = ic.options().get("trackqc-tpc-pt"); + mTrackQCNCls = ic.options().get("trackqc-tpc-cls"); + if (auto seed = ic.options().get("seed"); seed == 0) { + LOGP(info, "Using random device for seeding"); + std::random_device rd; + std::array seed_data{}; + std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + mGenerator = std::mt19937(seq); + } else { + LOGP(info, "Using seed {} for sampling", seed); + mGenerator.seed(seed); + } #ifdef WITH_OPENMP LOGP(info, "Multi-threaded parts will run with {} OpenMP threads", mNThreads); #else @@ -1390,6 +1903,10 @@ void AODProducerWorkflowDPL::init(InitContext& ic) LOG(info) << "The Run number will be obtained from DPL headers"; } + mUseSigFiltMC = ic.options().get("mc-signal-filt"); + + mCollectConfigFiles = ic.options().get("collect-config-files"); + // set no truncation if selected by user if (mTruncate != 1) { LOG(info) << "Truncation is not used!"; @@ -1405,6 +1922,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mTrackCovOffDiag = 0xFFFFFFFF; mTrackSignal = 0xFFFFFFFF; mTrackTime = 0xFFFFFFFF; + mTPCTime0 = 0xFFFFFFFF; mTrackTimeError = 0xFFFFFFFF; mTrackPosEMCAL = 0xFFFFFFFF; mTracklets = 0xFFFFFFFF; @@ -1425,21 +1943,69 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mMuonCl = 0xFFFFFFFF; mMuonClErr = 0xFFFFFFFF; mV0Time = 0xFFFFFFFF; + mV0ChannelTime = 0xFFFFFFFF; mFDDTime = 0xFFFFFFFF; + mFDDChannelTime = 0xFFFFFFFF; mT0Time = 0xFFFFFFFF; + mT0ChannelTime = 0xFFFFFFFF; mV0Amplitude = 0xFFFFFFFF; mFDDAmplitude = 0xFFFFFFFF; mT0Amplitude = 0xFFFFFFFF; } - - // initialize zdc helper maps - for (auto& ChannelName : o2::zdc::ChannelNames) { - mZDCEnergyMap[(string)ChannelName] = 0; - mZDCTDCMap[(string)ChannelName] = 999; + // Initialize ZDC helper maps + for (int ic = 0; ic < o2::zdc::NChannels; ic++) { + mZDCEnergyMap[ic] = -std::numeric_limits::infinity(); + } + for (int ic = 0; ic < o2::zdc::NTDCChannels; ic++) { + mZDCTDCMap[ic] = -std::numeric_limits::infinity(); } + std::string hepmcUpdate = ic.options().get("hepmc-update"); + HepMCUpdate when = (hepmcUpdate == "never" ? HepMCUpdate::never : hepmcUpdate == "always" ? HepMCUpdate::always + : hepmcUpdate == "all" ? HepMCUpdate::allKeys + : HepMCUpdate::anyKey); + mXSectionUpdate = when; + mPdfInfoUpdate = when; + mHeavyIonUpdate = when; + mTimer.Reset(); + + if (mStreamerFlags) { + mStreamer = std::make_unique("AO2DStreamer.root", "RECREATE"); + } +} + +namespace +{ +void add_additional_meta_info(std::vector& keys, std::vector& values) +{ + // see if we should put additional meta info (e.g. from MC) + auto aod_external_meta_info_file = getenv("AOD_ADDITIONAL_METADATA_FILE"); + if (aod_external_meta_info_file != nullptr) { + LOG(info) << "Trying to inject additional AOD meta-data from " << aod_external_meta_info_file; + if (std::filesystem::exists(aod_external_meta_info_file)) { + std::ifstream input_file(aod_external_meta_info_file); + if (input_file) { + nlohmann::json json_data; + try { + input_file >> json_data; + } catch (nlohmann::json::parse_error& e) { + std::cerr << "JSON Parse Error: " << e.what() << "\n"; + std::cerr << "Exception ID: " << e.id << "\n"; + std::cerr << "Byte position: " << e.byte << "\n"; + return; + } + // If parsing succeeds, iterate over key-value pairs + for (const auto& [key, value] : json_data.items()) { + LOG(info) << "Adding AOD MetaData" << key << " : " << value; + keys.push_back(key.c_str()); + values.push_back(value.get()); + } + } + } + } } +} // namespace void AODProducerWorkflowDPL::run(ProcessingContext& pc) { @@ -1486,68 +2052,78 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) LOG(debug) << "FOUND " << cpvTrigRecs.size() << " CPV trigger records"; LOG(info) << "FOUND " << primVertices.size() << " primary vertices"; - auto& bcBuilder = pc.outputs().make(Output{"AOD", "BC"}); - auto& cascadesBuilder = pc.outputs().make(Output{"AOD", "CASCADE_001"}); - auto& collisionsBuilder = pc.outputs().make(Output{"AOD", "COLLISION_001"}); - auto& decay3BodyBuilder = pc.outputs().make(Output{"AOD", "DECAY3BODY"}); - auto& fddBuilder = pc.outputs().make(Output{"AOD", "FDD_001"}); - auto& ft0Builder = pc.outputs().make(Output{"AOD", "FT0"}); - auto& fv0aBuilder = pc.outputs().make(Output{"AOD", "FV0A"}); - auto& fwdTracksBuilder = pc.outputs().make(Output{"AOD", "FWDTRACK"}); - auto& fwdTracksCovBuilder = pc.outputs().make(Output{"AOD", "FWDTRACKCOV"}); - auto& mcColLabelsBuilder = pc.outputs().make(Output{"AOD", "MCCOLLISIONLABEL"}); - auto& mcCollisionsBuilder = pc.outputs().make(Output{"AOD", "MCCOLLISION"}); - auto& mcMFTTrackLabelBuilder = pc.outputs().make(Output{"AOD", "MCMFTTRACKLABEL"}); - auto& mcFwdTrackLabelBuilder = pc.outputs().make(Output{"AOD", "MCFWDTRACKLABEL"}); - auto& mcParticlesBuilder = pc.outputs().make(Output{"AOD", "MCPARTICLE_001"}); - auto& mcTrackLabelBuilder = pc.outputs().make(Output{"AOD", "MCTRACKLABEL"}); - auto& mftTracksBuilder = pc.outputs().make(Output{"AOD", "MFTTRACK"}); - auto& tracksBuilder = pc.outputs().make(Output{"AOD", "TRACK_IU"}); - auto& tracksCovBuilder = pc.outputs().make(Output{"AOD", "TRACKCOV_IU"}); - auto& tracksExtraBuilder = pc.outputs().make(Output{"AOD", "TRACKEXTRA"}); - auto& ambigTracksBuilder = pc.outputs().make(Output{"AOD", "AMBIGUOUSTRACK"}); - auto& ambigMFTTracksBuilder = pc.outputs().make(Output{"AOD", "AMBIGUOUSMFTTR"}); - auto& ambigFwdTracksBuilder = pc.outputs().make(Output{"AOD", "AMBIGUOUSFWDTR"}); - auto& v0sBuilder = pc.outputs().make(Output{"AOD", "V0_001"}); - auto& zdcBuilder = pc.outputs().make(Output{"AOD", "ZDC"}); - auto& caloCellsBuilder = pc.outputs().make(Output{"AOD", "CALO"}); - auto& caloCellsTRGTableBuilder = pc.outputs().make(Output{"AOD", "CALOTRIGGER"}); - auto& cpvClustersBuilder = pc.outputs().make(Output{"AOD", "CPVCLUSTER"}); - auto& originTableBuilder = pc.outputs().make(Output{"AOD", "ORIGIN"}); - - auto bcCursor = bcBuilder.cursor(); - auto cascadesCursor = cascadesBuilder.cursor(); - auto collisionsCursor = collisionsBuilder.cursor(); - auto decay3BodyCursor = decay3BodyBuilder.cursor(); - auto fddCursor = fddBuilder.cursor(); - auto ft0Cursor = ft0Builder.cursor(); - auto fv0aCursor = fv0aBuilder.cursor(); - auto fwdTracksCursor = fwdTracksBuilder.cursor(); - auto fwdTracksCovCursor = fwdTracksCovBuilder.cursor(); - auto mcColLabelsCursor = mcColLabelsBuilder.cursor(); - auto mcCollisionsCursor = mcCollisionsBuilder.cursor(); - auto mcMFTTrackLabelCursor = mcMFTTrackLabelBuilder.cursor(); - auto mcFwdTrackLabelCursor = mcFwdTrackLabelBuilder.cursor(); - auto mcParticlesCursor = mcParticlesBuilder.cursor(); - auto mcTrackLabelCursor = mcTrackLabelBuilder.cursor(); - auto mftTracksCursor = mftTracksBuilder.cursor(); - auto tracksCovCursor = tracksCovBuilder.cursor(); - auto tracksCursor = tracksBuilder.cursor(); - auto tracksExtraCursor = tracksExtraBuilder.cursor(); - auto ambigTracksCursor = ambigTracksBuilder.cursor(); - auto ambigMFTTracksCursor = ambigMFTTracksBuilder.cursor(); - auto ambigFwdTracksCursor = ambigFwdTracksBuilder.cursor(); - auto v0sCursor = v0sBuilder.cursor(); - auto zdcCursor = zdcBuilder.cursor(); - auto caloCellsCursor = caloCellsBuilder.cursor(); - auto caloCellsTRGTableCursor = caloCellsTRGTableBuilder.cursor(); - auto cpvClustersCursor = cpvClustersBuilder.cursor(); - auto originCursor = originTableBuilder.cursor(); + + using namespace o2::aodhelpers; + + auto bcCursor = createTableCursor(pc); + auto bcFlagsCursor = createTableCursor(pc); + auto cascadesCursor = createTableCursor(pc); + auto collisionsCursor = createTableCursor(pc); + auto decay3BodyCursor = createTableCursor(pc); + auto trackedCascadeCursor = createTableCursor(pc); + auto trackedV0Cursor = createTableCursor(pc); + auto tracked3BodyCurs = createTableCursor(pc); + auto fddCursor = createTableCursor(pc); + auto fddExtraCursor = createTableCursor(pc); + auto ft0Cursor = createTableCursor(pc); + auto ft0ExtraCursor = createTableCursor(pc); + auto fv0aCursor = createTableCursor(pc); + auto fv0aExtraCursor = createTableCursor(pc); + auto fwdTracksCursor = createTableCursor(pc); + auto fwdTracksCovCursor = createTableCursor(pc); + auto fwdTrkClsCursor = createTableCursor(pc); + auto mftTracksCursor = createTableCursor(pc); + auto mftTracksCovCursor = createTableCursor(pc); + auto tracksCursor = createTableCursor(pc); + auto tracksCovCursor = createTableCursor(pc); + auto tracksExtraCursor = createTableCursor(pc); + auto tracksQACursor = createTableCursor(pc); + auto ambigTracksCursor = createTableCursor(pc); + auto ambigMFTTracksCursor = createTableCursor(pc); + auto ambigFwdTracksCursor = createTableCursor(pc); + auto v0sCursor = createTableCursor(pc); + auto zdcCursor = createTableCursor(pc); + auto hmpCursor = createTableCursor(pc); + auto caloCellsCursor = createTableCursor(pc); + auto caloCellsTRGTableCursor = createTableCursor(pc); + auto cpvClustersCursor = createTableCursor(pc); + auto originCursor = createTableCursor(pc); + + /// Extra tables + o2::framework::Produces trdExtraCursor; + if (mEnableTRDextra) { + trdExtraCursor = createTableCursor(pc); + } + + // Declare MC cursors type without adding the output for a table + o2::framework::Produces mcColLabelsCursor; + o2::framework::Produces mcCollisionsCursor; + o2::framework::Produces hepmcXSectionsCursor; + o2::framework::Produces hepmcPdfInfosCursor; + o2::framework::Produces hepmcHeavyIonsCursor; + o2::framework::Produces mcMFTTrackLabelCursor; + o2::framework::Produces mcFwdTrackLabelCursor; + o2::framework::Produces mcParticlesCursor; + o2::framework::Produces mcTrackLabelCursor; + o2::framework::Produces mcCaloLabelsCursor; + if (mUseMC) { // This creates the actual writercursor + mcColLabelsCursor = createTableCursor(pc); + mcCollisionsCursor = createTableCursor(pc); + hepmcXSectionsCursor = createTableCursor(pc); + hepmcPdfInfosCursor = createTableCursor(pc); + hepmcHeavyIonsCursor = createTableCursor(pc); + mcMFTTrackLabelCursor = createTableCursor(pc); + mcFwdTrackLabelCursor = createTableCursor(pc); + mcParticlesCursor = createTableCursor(pc); + mcTrackLabelCursor = createTableCursor(pc); + mcCaloLabelsCursor = createTableCursor(pc); + } std::unique_ptr mcReader; if (mUseMC) { mcReader = std::make_unique("collisioncontext.root"); } + mMCKineReader = mcReader.get(); // for use in different functions std::map bcsMap; collectBCs(recoData, mUseMC ? mcReader->getDigitizationContext()->getEventRecords() : std::vector{}, bcsMap); if (!primVer2TRefs.empty()) { // if the vertexing was done, the last slot refers to orphan tracks @@ -1565,15 +2141,18 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) tfNumber = mTFNumber; } - std::vector aAmplitudes; + std::vector aAmplitudes, aTimes; std::vector aChannels; + fv0aCursor.reserve(fv0RecPoints.size()); for (auto& fv0RecPoint : fv0RecPoints) { aAmplitudes.clear(); aChannels.clear(); + aTimes.clear(); const auto channelData = fv0RecPoint.getBunchChannelData(fv0ChData); for (auto& channel : channelData) { if (channel.charge > 0) { aAmplitudes.push_back(truncateFloatFraction(channel.charge, mV0Amplitude)); + aTimes.push_back(truncateFloatFraction(channel.time * 1.E-3, mV0ChannelTime)); aChannels.push_back(channel.channel); } } @@ -1585,14 +2164,21 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } else { LOG(fatal) << "Error: could not find a corresponding BC ID for a FV0 rec. point; BC = " << bc; } - fv0aCursor(0, - bcID, + fv0aCursor(bcID, aAmplitudes, aChannels, truncateFloatFraction(fv0RecPoint.getCollisionGlobalMeanTime() * 1E-3, mV0Time), // ps to ns fv0RecPoint.getTrigger().getTriggersignals()); + + if (mEnableFITextra) { + fv0aExtraCursor(bcID, + aTimes); + } } + std::vector zdcEnergy, zdcAmplitudes, zdcTime; + std::vector zdcChannelsE, zdcChannelsT; + zdcCursor.reserve(zdcBCRecData.size()); for (auto zdcRecData : zdcBCRecData) { uint64_t bc = zdcRecData.ir.toLong(); auto item = bcsMap.find(bc); @@ -1602,137 +2188,126 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } else { LOG(fatal) << "Error: could not find a corresponding BC ID for a ZDC rec. point; BC = " << bc; } - float energyZEM1 = 0; - float energyZEM2 = 0; - float energyCommonZNA = 0; - float energyCommonZNC = 0; - float energyCommonZPA = 0; - float energyCommonZPC = 0; - float energySectorZNA[4] = {0.}; - float energySectorZNC[4] = {0.}; - float energySectorZPA[4] = {0.}; - float energySectorZPC[4] = {0.}; int fe, ne, ft, nt, fi, ni; zdcRecData.getRef(fe, ne, ft, nt, fi, ni); + zdcEnergy.clear(); + zdcChannelsE.clear(); + zdcAmplitudes.clear(); + zdcTime.clear(); + zdcChannelsT.clear(); for (int ie = 0; ie < ne; ie++) { auto& zdcEnergyData = zdcEnergies[fe + ie]; - float energy = zdcEnergyData.energy(); - string chName = o2::zdc::channelName(zdcEnergyData.ch()); - mZDCEnergyMap.at(chName) = energy; + zdcEnergy.emplace_back(zdcEnergyData.energy()); + zdcChannelsE.emplace_back(zdcEnergyData.ch()); } for (int it = 0; it < nt; it++) { auto& tdc = zdcTDCData[ft + it]; - float tdcValue = tdc.value(); - int channelID = o2::zdc::TDCSignal[tdc.ch()]; - auto channelName = o2::zdc::ChannelNames[channelID]; - mZDCTDCMap.at((string)channelName) = tdcValue; - } - energySectorZNA[0] = mZDCEnergyMap.at("ZNA1"); - energySectorZNA[1] = mZDCEnergyMap.at("ZNA2"); - energySectorZNA[2] = mZDCEnergyMap.at("ZNA3"); - energySectorZNA[3] = mZDCEnergyMap.at("ZNA4"); - energySectorZNC[0] = mZDCEnergyMap.at("ZNC1"); - energySectorZNC[1] = mZDCEnergyMap.at("ZNC2"); - energySectorZNC[2] = mZDCEnergyMap.at("ZNC3"); - energySectorZNC[3] = mZDCEnergyMap.at("ZNC4"); - energySectorZPA[0] = mZDCEnergyMap.at("ZPA1"); - energySectorZPA[1] = mZDCEnergyMap.at("ZPA2"); - energySectorZPA[2] = mZDCEnergyMap.at("ZPA3"); - energySectorZPA[3] = mZDCEnergyMap.at("ZPA4"); - energySectorZPC[0] = mZDCEnergyMap.at("ZPC1"); - energySectorZPC[1] = mZDCEnergyMap.at("ZPC2"); - energySectorZPC[2] = mZDCEnergyMap.at("ZPC3"); - energySectorZPC[3] = mZDCEnergyMap.at("ZPC4"); - zdcCursor(0, - bcID, - mZDCEnergyMap.at("ZEM1"), - mZDCEnergyMap.at("ZEM2"), - mZDCEnergyMap.at("ZNAC"), - mZDCEnergyMap.at("ZNCC"), - mZDCEnergyMap.at("ZPAC"), - mZDCEnergyMap.at("ZPCC"), - energySectorZNA, - energySectorZNC, - energySectorZPA, - energySectorZPC, - mZDCTDCMap.at("ZEM1"), - mZDCTDCMap.at("ZEM2"), - mZDCTDCMap.at("ZNAC"), - mZDCTDCMap.at("ZNCC"), - mZDCTDCMap.at("ZPAC"), - mZDCTDCMap.at("ZPCC")); - } - - // keep track event/source id for each mc-collision - // using map and not unordered_map to ensure - // correct ordering when iterating over container elements - std::vector> mcColToEvSrc; + zdcAmplitudes.emplace_back(tdc.amplitude()); + zdcTime.emplace_back(tdc.value()); + zdcChannelsT.emplace_back(o2::zdc::TDCSignal[tdc.ch()]); + } + zdcCursor(bcID, + zdcEnergy, + zdcChannelsE, + zdcAmplitudes, + zdcTime, + zdcChannelsT); + } + + // keep track of event_id + source_id + bc for each mc-collision + std::vector mcColToEvSrc; if (mUseMC) { - // TODO: figure out collision weight - float mcColWeight = 1.; + using namespace o2::aodmchelpers; + // filling mcCollision table int nMCCollisions = mcReader->getDigitizationContext()->getNCollisions(); const auto& mcRecords = mcReader->getDigitizationContext()->getEventRecords(); const auto& mcParts = mcReader->getDigitizationContext()->getEventParts(); + + // if signal filtering enabled, let's check if there are more than one source; otherwise fatalise + if (mUseSigFiltMC) { + std::vector sourceIDs{}; + for (int iCol = 0; iCol < nMCCollisions; iCol++) { + for (auto const& colPart : mcParts[iCol]) { + int sourceID = colPart.sourceID; + if (std::find(sourceIDs.begin(), sourceIDs.end(), sourceID) == sourceIDs.end()) { + sourceIDs.push_back(sourceID); + } + if (sourceIDs.size() > 1) { // we found more than one, exit + break; + } + } + if (sourceIDs.size() > 1) { // we found more than one, exit + break; + } + } + if (sourceIDs.size() <= 1) { + LOGP(fatal, "Signal filtering cannot be enabled without embedding. Please fix the configuration either enabling the embedding, or turning off the signal filtering."); + } + } + + // count all parts + int totalNParts = 0; + for (int iCol = 0; iCol < nMCCollisions; iCol++) { + totalNParts += mcParts[iCol].size(); + } + mcCollisionsCursor.reserve(totalNParts); + for (int iCol = 0; iCol < nMCCollisions; iCol++) { - auto time = mcRecords[iCol].getTimeNS(); + const auto time = mcRecords[iCol].getTimeOffsetWrtBC(); auto globalBC = mcRecords[iCol].toLong(); auto item = bcsMap.find(globalBC); int bcID = -1; if (item != bcsMap.end()) { bcID = item->second; } else { - LOG(fatal) << "Error: could not find a corresponding BC ID for MC collision; BC = " << globalBC << ", mc collision = " << iCol; + LOG(fatal) << "Error: could not find a corresponding BC ID " + << "for MC collision; BC = " << globalBC + << ", mc collision = " << iCol; } auto& colParts = mcParts[iCol]; auto nParts = colParts.size(); for (auto colPart : colParts) { auto eventID = colPart.entryID; auto sourceID = colPart.sourceID; - // enable embedding: if several colParts exist, then they are saved as one collision + // enable embedding: if several colParts exist, then they are + // saved as one collision if (nParts == 1 || sourceID == 0) { // FIXME: // use generators' names for generatorIDs (?) - short generatorID = sourceID; auto& header = mcReader->getMCEventHeader(sourceID, eventID); - mcCollisionsCursor(0, - bcID, - generatorID, - truncateFloatFraction(header.GetX(), mCollisionPosition), - truncateFloatFraction(header.GetY(), mCollisionPosition), - truncateFloatFraction(header.GetZ(), mCollisionPosition), - truncateFloatFraction(time, mCollisionPosition), - truncateFloatFraction(mcColWeight, mCollisionPosition), - header.GetB()); + updateMCHeader(mcCollisionsCursor.cursor, + hepmcXSectionsCursor.cursor, + hepmcPdfInfosCursor.cursor, + hepmcHeavyIonsCursor.cursor, + header, + iCol, + bcID, + time, + 0, + sourceID); } - mcColToEvSrc.emplace_back(std::vector{iCol, sourceID, eventID}); // point background and injected signal events to one collision + mcColToEvSrc.emplace_back(MCColInfo{iCol, sourceID, eventID, globalBC}); // point background and injected signal events to one collision } } } std::sort(mcColToEvSrc.begin(), mcColToEvSrc.end(), - [](const std::vector& left, const std::vector& right) { return (left[0] < right[0]); }); + [](const MCColInfo& left, const MCColInfo& right) { return (left.colIndex < right.colIndex); }); // vector of FDD amplitudes - int16_t aFDDAmplitudesA[8] = {0u}; - int16_t aFDDAmplitudesC[8] = {0u}; + int16_t aFDDAmplitudesA[8] = {0u}, aFDDAmplitudesC[8] = {0u}; + float aFDDTimesA[8] = {0.f}, aFDDTimesC[8] = {0.f}; // filling FDD table + fddCursor.reserve(fddRecPoints.size()); for (const auto& fddRecPoint : fddRecPoints) { for (int i = 0; i < 8; i++) { aFDDAmplitudesA[i] = 0; aFDDAmplitudesC[i] = 0; + aFDDTimesA[i] = 0.f; + aFDDTimesC[i] = 0.f; } - - const auto channelData = fddRecPoint.getBunchChannelData(fddChData); - for (const auto& channel : channelData) { - if (channel.mPMNumber < 8) { - aFDDAmplitudesC[channel.mPMNumber] = channel.mChargeADC; // amplitude - } else { - aFDDAmplitudesA[channel.mPMNumber - 8] = channel.mChargeADC; // amplitude - } - } - uint64_t globalBC = fddRecPoint.getInteractionRecord().toLong(); uint64_t bc = globalBC; auto item = bcsMap.find(bc); @@ -1742,21 +2317,39 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } else { LOG(fatal) << "Error: could not find a corresponding BC ID for a FDD rec. point; BC = " << bc; } - fddCursor(0, - bcID, + const auto channelData = fddRecPoint.getBunchChannelData(fddChData); + for (const auto& channel : channelData) { + if (channel.mPMNumber < 8) { + aFDDAmplitudesC[channel.mPMNumber] = channel.mChargeADC; // amplitude + aFDDTimesC[channel.mPMNumber] = truncateFloatFraction(channel.mTime * 1E-3, mFDDChannelTime); // time + } else { + aFDDAmplitudesA[channel.mPMNumber - 8] = channel.mChargeADC; // amplitude + aFDDTimesA[channel.mPMNumber - 8] = truncateFloatFraction(channel.mTime * 1E-3, mFDDChannelTime); // time + } + } + + fddCursor(bcID, aFDDAmplitudesA, aFDDAmplitudesC, truncateFloatFraction(fddRecPoint.getCollisionTimeA() * 1E-3, mFDDTime), // ps to ns truncateFloatFraction(fddRecPoint.getCollisionTimeC() * 1E-3, mFDDTime), // ps to ns fddRecPoint.getTrigger().getTriggersignals()); + if (mEnableFITextra) { + fddExtraCursor(bcID, + aFDDTimesA, + aFDDTimesC); + } } // filling FT0 table - std::vector aAmplitudesA, aAmplitudesC; + std::vector aAmplitudesA, aAmplitudesC, aTimesA, aTimesC; std::vector aChannelsA, aChannelsC; + ft0Cursor.reserve(ft0RecPoints.size()); for (auto& ft0RecPoint : ft0RecPoints) { aAmplitudesA.clear(); aAmplitudesC.clear(); + aTimesA.clear(); + aTimesC.clear(); aChannelsA.clear(); aChannelsC.clear(); const auto channelData = ft0RecPoint.getBunchChannelData(ft0ChData); @@ -1767,9 +2360,11 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) if (channel.ChId < nFT0ChannelsAside) { aChannelsA.push_back(channel.ChId); aAmplitudesA.push_back(truncateFloatFraction(channel.QTCAmpl, mT0Amplitude)); + aTimesA.push_back(truncateFloatFraction(channel.CFDTime * 1E-3, mT0ChannelTime)); } else { aChannelsC.push_back(channel.ChId - nFT0ChannelsAside); aAmplitudesC.push_back(truncateFloatFraction(channel.QTCAmpl, mT0Amplitude)); + aTimesC.push_back(truncateFloatFraction(channel.CFDTime * 1E-3, mT0ChannelTime)); } } } @@ -1782,8 +2377,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } else { LOG(fatal) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc; } - ft0Cursor(0, - bcID, + ft0Cursor(bcID, aAmplitudesA, aChannelsA, aAmplitudesC, @@ -1791,19 +2385,55 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) truncateFloatFraction(ft0RecPoint.getCollisionTimeA() * 1E-3, mT0Time), // ps to ns truncateFloatFraction(ft0RecPoint.getCollisionTimeC() * 1E-3, mT0Time), // ps to ns ft0RecPoint.getTrigger().getTriggersignals()); + if (mEnableFITextra) { + ft0ExtraCursor(bcID, + aTimesA, + aTimesC); + } } if (mUseMC) { - // filling MC collision labels - for (auto& label : primVerLabels) { - auto it = std::find_if(mcColToEvSrc.begin(), mcColToEvSrc.end(), - [&label](const auto& mcColInfo) { return mcColInfo[1] == label.getSourceID() && mcColInfo[2] == label.getEventID(); }); + // Fill MC collision labels using information from the primary vertexer. + mcColLabelsCursor.reserve(primVerLabels.size()); + for (size_t ivert = 0; ivert < primVerLabels.size(); ++ivert) { + const auto& label = primVerLabels[ivert]; + + // Collect all MC collision candidates matching this (sourceID, eventID) label. + // In the non-embedding case there is exactly one candidate. In the embedding + // case the same (sourceID, eventID) pair can appear in multiple collisions, + // so we need to disambiguate. + std::vector> candidates; // (colIndex, bc) + for (const auto& colInfo : mcColToEvSrc) { + if (colInfo.sourceID == label.getSourceID() && + colInfo.eventID == label.getEventID()) { + candidates.emplace_back(colInfo.colIndex, colInfo.bc); + } + } + int32_t mcCollisionID = -1; - if (it != mcColToEvSrc.end()) { - mcCollisionID = it->at(0); + if (candidates.size() == 1) { + mcCollisionID = candidates[0].first; + } else if (candidates.size() > 1) { + // Disambiguate by BC: pick the MCCollision whose BC is closest + // to the reconstructed collision's BC. + // TODO: Consider a complementary strategy using the MC labels of tracks + // associated to the primary vertex, and/or by allowing the primary + // vertexer to return multiple MC collision labels per vertex. + const auto& timeStamp = primVertices[ivert].getTimeStamp(); + const double interactionTime = timeStamp.getTimeStamp() * 1E3; // us -> ns + const auto recoBC = relativeTime_to_GlobalBC(interactionTime); + int64_t bestDiff = std::numeric_limits::max(); + for (const auto& [colIndex, bc] : candidates) { + const auto bcDiff = std::abs(static_cast(bc) - static_cast(recoBC)); + if (bcDiff < bestDiff) { + bestDiff = bcDiff; + mcCollisionID = colIndex; + } + } } - uint16_t mcMask = 0; // todo: set mask using normalized weights? - mcColLabelsCursor(0, mcCollisionID, mcMask); + + uint16_t mcMask = 0; // TODO: set mask using normalised weights + mcColLabelsCursor(mcCollisionID, mcMask); } } @@ -1823,19 +2453,49 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) collisionID++; } + /// Strangeness tracking requires its index LUTs to be filled before the tracks are filled + prepareStrangenessTracking(recoData); + mGIDToTableFwdID.clear(); // reset the tables to be used by 'fillTrackTablesPerCollision' mGIDToTableMFTID.clear(); + if (mPropTracks || mThinTracks) { + auto v0s = recoData.getV0sIdx(); + auto cascades = recoData.getCascadesIdx(); + auto decays3Body = recoData.getDecays3BodyIdx(); + mGIDUsedBySVtx.reserve(v0s.size() * 2 + cascades.size() + decays3Body.size() * 3); + for (const auto& v0 : v0s) { + mGIDUsedBySVtx.insert(v0.getProngID(0)); + mGIDUsedBySVtx.insert(v0.getProngID(1)); + } + for (const auto& cascade : cascades) { + mGIDUsedBySVtx.insert(cascade.getBachelorID()); + } + for (const auto& id3Body : decays3Body) { + mGIDUsedBySVtx.insert(id3Body.getProngID(0)); + mGIDUsedBySVtx.insert(id3Body.getProngID(1)); + mGIDUsedBySVtx.insert(id3Body.getProngID(2)); + } + + mGIDUsedByStr.reserve(recoData.getStrangeTracks().size()); + for (const auto& sTrk : recoData.getStrangeTracks()) { + mGIDUsedByStr.emplace(sTrk.mITSRef, GIndex::ITS); + } + } + + mCurrentTRDTrigID = 0; // reinitialize index for TRD trigger record search // filling unassigned tracks first // so that all unassigned tracks are stored in the beginning of the table together auto& trackRef = primVer2TRefs.back(); // references to unassigned tracks are at the end // fixme: interaction time is undefined for unassigned tracks (?) - fillTrackTablesPerCollision(-1, std::uint64_t(-1), trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, - ambigTracksCursor, mftTracksCursor, ambigMFTTracksCursor, - fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, bcsMap); + fillTrackTablesPerCollision(-1, std::uint64_t(-1), trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, trdExtraCursor, + ambigTracksCursor, mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, + fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap); + mCurrentTRDTrigID = 0; // reinitialize index for TRD trigger record search // filling collisions and tracks into tables collisionID = 0; + collisionsCursor.reserve(primVertices.size()); for (auto& vertex : primVertices) { auto& cov = vertex.getCov(); auto& timeStamp = vertex.getTimeStamp(); // this is a relative time @@ -1852,8 +2512,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } else { LOG(fatal) << "Error: could not find a corresponding BC ID for a collision; BC = " << globalBC << ", collisionID = " << collisionID; } - collisionsCursor(0, - bcID, + collisionsCursor(bcID, truncateFloatFraction(vertex.getX(), mCollisionPosition), truncateFloatFraction(vertex.getY(), mCollisionPosition), truncateFloatFraction(vertex.getZ(), mCollisionPosition), @@ -1872,53 +2531,66 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) auto& trackRef = primVer2TRefs[collisionID]; // passing interaction time in [ps] - fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, ambigTracksCursor, - mftTracksCursor, ambigMFTTracksCursor, - fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, bcsMap); + fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, trdExtraCursor, ambigTracksCursor, + mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, + fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap); collisionID++; } fillSecondaryVertices(recoData, v0sCursor, cascadesCursor, decay3BodyCursor); + fillHMPID(recoData, hmpCursor); + fillStrangenessTrackingTables(recoData, trackedV0Cursor, trackedCascadeCursor, tracked3BodyCurs); // helper map for fast search of a corresponding class mask for a bc - std::unordered_map bcToClassMask; + auto emcalIncomplete = filterEMCALIncomplete(recoData.getEMCALTriggers()); + std::unordered_map> bcToClassMask; if (mInputSources[GID::CTP]) { LOG(debug) << "CTP input available"; for (auto& ctpDigit : ctpDigits) { uint64_t bc = ctpDigit.intRecord.toLong(); uint64_t classMask = ctpDigit.CTPClassMask.to_ulong(); - bcToClassMask[bc] = classMask; + uint64_t inputMask = ctpDigit.CTPInputMask.to_ulong(); + if (emcalIncomplete.find(bc) != emcalIncomplete.end()) { + // reject EMCAL triggers as BC was rejected as incomplete at readout level + auto classMaskOrig = classMask; + classMask = classMask & ~mEMCALTrgClassMask; + LOG(debug) << "Found EMCAL incomplete event, mask before " << std::bitset<64>(classMaskOrig) << ", after " << std::bitset<64>(classMask); + } + bcToClassMask[bc] = {classMask, inputMask}; // LOG(debug) << Form("classmask:0x%llx", classMask); } } // filling BC table - uint64_t triggerMask = 0; + bcCursor.reserve(bcsMap.size()); for (auto& item : bcsMap) { uint64_t bc = item.first; + std::pair masks{0, 0}; if (mInputSources[GID::CTP]) { auto bcClassPair = bcToClassMask.find(bc); if (bcClassPair != bcToClassMask.end()) { - triggerMask = bcClassPair->second; - } else { - triggerMask = 0; + masks = bcClassPair->second; } } - bcCursor(0, - runNumber, + bcCursor(runNumber, bc, - triggerMask); + masks.first, + masks.second); } bcToClassMask.clear(); - if (mInputSources[GIndex::PHS] || mInputSources[GIndex::EMC]) { - fillCaloTable(caloCellsCursor, caloCellsTRGTableCursor, bcsMap, recoData); + // filling BC flags table: + auto bcFlags = fillBCFlags(recoData, bcsMap); + bcFlagsCursor.reserve(bcFlags.size()); + for (auto f : bcFlags) { + bcFlagsCursor(f); } // fill cpvcluster table if (mInputSources[GIndex::CPV]) { float posX, posZ; + cpvClustersCursor.reserve(cpvClusters.size()); for (auto& cpvEvent : cpvTrigRecs) { uint64_t bc = cpvEvent.getBCData().toLong(); auto item = bcsMap.find(bc); @@ -1931,8 +2603,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) for (int iClu = cpvEvent.getFirstEntry(); iClu < cpvEvent.getFirstEntry() + cpvEvent.getNumberOfObjects(); iClu++) { auto& clu = cpvClusters[iClu]; clu.getLocalPosition(posX, posZ); - cpvClustersCursor(0, - bcID, + cpvClustersCursor(bcID, truncateFloatFraction(posX, mCPVPos), truncateFloatFraction(posZ, mCPVPos), truncateFloatFraction(clu.getEnergy(), mCPVAmpl), @@ -1941,14 +2612,12 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } } - bcsMap.clear(); - if (mUseMC) { TStopwatch timer; timer.Start(); // filling mc particles table fillMCParticlesTable(*mcReader, - mcParticlesCursor, + mcParticlesCursor.cursor, primVer2TRefs, primVerGIs, recoData, @@ -1962,11 +2631,18 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) // need to go through labels in the same order as for tracks fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, primVer2TRefs.back(), primVerGIs, recoData); - for (int iref = 0; iref < primVer2TRefs.size() - 1; iref++) { + for (auto iref = 0U; iref < primVer2TRefs.size() - 1; iref++) { auto& trackRef = primVer2TRefs[iref]; - fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, trackRef, primVerGIs, recoData); + fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, trackRef, primVerGIs, recoData, iref); } } + + // Fill calo tables and if MC also the MCCaloTable, therefore, has to be after fillMCParticlesTable call! + if (mInputSources[GIndex::PHS] || mInputSources[GIndex::EMC]) { + fillCaloTable(caloCellsCursor, caloCellsTRGTableCursor, mcCaloLabelsCursor, bcsMap, recoData); + } + + bcsMap.clear(); clearMCKeepStore(mToStore); mGIDToTableID.clear(); mTableTrID = 0; @@ -1986,19 +2662,28 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) mBCLookup.clear(); - originCursor(0, tfNumber); + mGIDUsedBySVtx.clear(); + mGIDUsedByStr.clear(); + + originCursor(tfNumber); // sending metadata to writer TString dataType = mUseMC ? "MC" : "RAW"; TString O2Version = o2::fullVersion(); TString ROOTVersion = ROOT_RELEASE; - mMetaDataKeys = {"DataType", "Run", "O2Version", "ROOTVersion", "RecoPassName", "AnchorProduction", "AnchorPassName", "LPMProductionTag"}; - mMetaDataVals = {dataType, "3", O2Version, ROOTVersion, mRecoPass, mAnchorProd, mAnchorPass, mLPMProdTag}; - pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0, Lifetime::Timeframe}, mMetaDataKeys); - pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0, Lifetime::Timeframe}, mMetaDataVals); + mMetaDataKeys = {"DataType", "Run", "O2Version", "ROOTVersion", "RecoPassName", "AnchorProduction", "AnchorPassName", "LPMProductionTag", "CreatedBy"}; + mMetaDataVals = {dataType, "3", O2Version, ROOTVersion, mRecoPass, mAnchorProd, mAnchorPass, mLPMProdTag, mUser}; + add_additional_meta_info(mMetaDataKeys, mMetaDataVals); - pc.outputs().snapshot(Output{"TFN", "TFNumber", 0, Lifetime::Timeframe}, tfNumber); - pc.outputs().snapshot(Output{"TFF", "TFFilename", 0, Lifetime::Timeframe}, ""); + if (mCollectConfigFiles) { + collectConfigFiles(mMetaDataKeys, mMetaDataVals); + } + + pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0}, mMetaDataKeys); + pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0}, mMetaDataVals); + + pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, tfNumber); + pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, mAODParent); mTimer.Stop(); } @@ -2079,8 +2764,8 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac if (collisionID < 0) { extraInfoHolder.flags |= o2::aod::track::OrphanTrack; } - bool needBCSlice = collisionID < 0 || trackIndex.isAmbiguous(); // track is associated to multiple vertices - uint64_t bcOfTimeRef = collisionBC - mStartIR.toLong(); // by default (unambiguous) track time is wrt collision BC + bool needBCSlice = collisionID < 0; // track is associated to multiple vertices + uint64_t bcOfTimeRef = collisionBC - mStartIR.toLong(); // by default track time is wrt collision BC (unless no collision assigned) auto setTrackTime = [&](double t, double terr, bool gaussian) { // set track time and error, for ambiguous tracks define the bcSlice as it was used in vertex-track association @@ -2094,6 +2779,7 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac bcOfTimeRef = fillBCSlice(extraInfoHolder.bcSlice, t - error, t + error, bcsMap); } extraInfoHolder.trackTime = float(t - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS); + extraInfoHolder.diffBCRef = int(bcOfTimeRef); LOGP(debug, "time : {}/{} -> {}/{} -> trunc: {}/{} CollID: {} Amb: {}", t, terr, t - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS, terr, truncateFloatFraction(extraInfoHolder.trackTime, mTrackTime), truncateFloatFraction(extraInfoHolder.trackTimeRes, mTrackTimeError), collisionID, trackIndex.isAmbiguous()); @@ -2110,7 +2796,10 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac extraInfoHolder.length = intLen; const float mass = o2::constants::physics::MassPionCharged; // default pid = pion if (tofInt.getTOF(o2::track::PID::Pion) > 0.f) { - const float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed)); + float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed)); + if (expBeta > o2::constants::math::Almost1) { + expBeta = o2::constants::math::Almost1; + } extraInfoHolder.tofExpMom = mass * expBeta / std::sqrt(1.f - expBeta * expBeta); } // correct the time of the track @@ -2137,30 +2826,52 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac int nClusters = itsTrack.getNClusters(); float chi2 = itsTrack.getChi2(); extraInfoHolder.itsChi2NCl = nClusters != 0 ? chi2 / (float)nClusters : 0; - extraInfoHolder.itsClusterMap = itsTrack.getPattern(); + extraInfoHolder.itsClusterSizes = itsTrack.getClusterSizes(); if (src == GIndex::ITS) { // standalone ITS track should set its time from the ROF const auto& rof = data.getITSTracksROFRecords()[mITSROFs[trackIndex.getIndex()]]; - double t = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mITSROFrameHalfLengthNS; + double t = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mITSROFrameHalfLengthNS + mITSROFBiasNS; setTrackTime(t, mITSROFrameHalfLengthNS, false); } } else if (contributorsGID[GIndex::Source::ITSAB].isIndexSet()) { // this is an ITS-TPC afterburner contributor - extraInfoHolder.itsClusterMap = data.getITSABRefs()[contributorsGID[GIndex::Source::ITSAB].getIndex()].pattern; + extraInfoHolder.itsClusterSizes = data.getITSABRefs()[contributorsGID[GIndex::Source::ITSAB].getIndex()].getClusterSizes(); } if (contributorsGID[GIndex::Source::TPC].isIndexSet()) { const auto& tpcOrig = data.getTPCTrack(contributorsGID[GIndex::TPC]); const auto& tpcClData = mTPCCounters[contributorsGID[GIndex::TPC]]; - extraInfoHolder.tpcInnerParam = tpcOrig.getP(); + const auto& dEdx = tpcOrig.getdEdx().dEdxTotTPC > 0 ? tpcOrig.getdEdx() : tpcOrig.getdEdxAlt(); + if (tpcOrig.getdEdx().dEdxTotTPC == 0) { + extraInfoHolder.flags |= o2::aod::track::TPCdEdxAlt; + } + if (tpcOrig.hasASideClusters()) { + extraInfoHolder.flags |= o2::aod::track::TPCSideA; + } + if (tpcOrig.hasCSideClusters()) { + extraInfoHolder.flags |= o2::aod::track::TPCSideC; + } + extraInfoHolder.tpcInnerParam = tpcOrig.getP() / tpcOrig.getAbsCharge(); extraInfoHolder.tpcChi2NCl = tpcOrig.getNClusters() ? tpcOrig.getChi2() / tpcOrig.getNClusters() : 0; - extraInfoHolder.tpcSignal = tpcOrig.getdEdx().dEdxTotTPC; + extraInfoHolder.tpcSignal = dEdx.dEdxTotTPC; extraInfoHolder.tpcNClsFindable = tpcOrig.getNClusters(); extraInfoHolder.tpcNClsFindableMinusFound = tpcOrig.getNClusters() - tpcClData.found; extraInfoHolder.tpcNClsFindableMinusCrossedRows = tpcOrig.getNClusters() - tpcClData.crossed; extraInfoHolder.tpcNClsShared = tpcClData.shared; - if (src == GIndex::TPC) { // standalone TPC track should set its time from their timebins range - double terr = 0.5 * (tpcOrig.getDeltaTFwd() + tpcOrig.getDeltaTBwd()) * mTPCBinNS; // half-span of the interval - double t = (tpcOrig.getTime0() + 0.5 * (tpcOrig.getDeltaTFwd() - tpcOrig.getDeltaTBwd())) * mTPCBinNS; // central value - LOG(debug) << "TPC tracks t0:" << tpcOrig.getTime0() << " tbwd: " << tpcOrig.getDeltaTBwd() << " tfwd: " << tpcOrig.getDeltaTFwd() << " t: " << t << " te: " << terr; - setTrackTime(t, terr, false); + uint32_t clsUsedForPID = dEdx.NHitsIROC + dEdx.NHitsOROC1 + dEdx.NHitsOROC2 + dEdx.NHitsOROC3; + extraInfoHolder.tpcNClsFindableMinusPID = tpcOrig.getNClusters() - clsUsedForPID; + if (src == GIndex::TPC) { // standalone TPC track should set its time from their timebins range + if (needBCSlice) { + double t = (tpcOrig.getTime0() + 0.5 * (tpcOrig.getDeltaTFwd() - tpcOrig.getDeltaTBwd())) * mTPCBinNS; // central value + double terr = 0.5 * (tpcOrig.getDeltaTFwd() + tpcOrig.getDeltaTBwd()) * mTPCBinNS; + double err = mTimeMarginTrackTime + terr; + bcOfTimeRef = fillBCSlice(extraInfoHolder.bcSlice, t - err, t + err, bcsMap); + } + aod::track::extensions::TPCTimeErrEncoding p; + p.setDeltaTFwd(tpcOrig.getDeltaTFwd()); + p.setDeltaTBwd(tpcOrig.getDeltaTBwd()); + extraInfoHolder.trackTimeRes = p.getTimeErr(); + extraInfoHolder.trackTime = float(tpcOrig.getTime0() * mTPCBinNS - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS); + extraInfoHolder.diffBCRef = int(bcOfTimeRef); + extraInfoHolder.isTPConly = true; // no truncation + extraInfoHolder.flags |= o2::aod::track::TrackTimeAsym; } else if (src == GIndex::ITSTPC) { // its-tpc matched tracks have gaussian time error and the time was not set above const auto& trITSTPC = data.getTPCITSTrack(trackIndex); auto ts = trITSTPC.getTimeMUS(); @@ -2176,6 +2887,181 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac return extraInfoHolder; } +AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int collisionID, std::uint64_t collisionBC, GIndex trackIndex, + const o2::globaltracking::RecoContainer& data, const std::map& bcsMap) +{ + TrackQA trackQAHolder; + auto contributorsGID = data.getTPCContributorGID(trackIndex); + const auto& trackPar = data.getTrackParam(trackIndex); + if (contributorsGID.isIndexSet()) { + auto prop = o2::base::Propagator::Instance(); + const auto& tpcOrig = data.getTPCTrack(contributorsGID); + /// getDCA - should be done with the copy of TPC only track + o2::track::TrackParametrization tpcTMP = tpcOrig; /// get backup of the track + const o2::base::Propagator::MatCorrType mMatType = o2::base::Propagator::MatCorrType::USEMatCorrLUT; /// should be parameterized + const o2::dataformats::VertexBase v = mVtx.getMeanVertex(collisionID < 0 ? 0.f : data.getPrimaryVertex(collisionID).getZ()); + std::array dcaInfo{-999., -999.}; + if (prop->propagateToDCABxByBz({v.getX(), v.getY(), v.getZ()}, tpcTMP, 2.f, mMatType, &dcaInfo)) { + trackQAHolder.tpcdcaR = 100. * dcaInfo[0] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt()); + trackQAHolder.tpcdcaZ = 100. * dcaInfo[1] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt()); + } + // This allows to safely clamp any float to one byte, using the + // minmal/maximum values as under-/overflow borders and rounding to the nearest integer + auto safeInt8Clamp = [](auto value) -> int8_t { + using ValType = decltype(value); + return static_cast(TMath::Nint(std::clamp(value, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max())))); + }; + auto safeUInt8Clamp = [](auto value) -> uint8_t { + using ValType = decltype(value); + return static_cast(TMath::Nint(std::clamp(value, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max())))); + }; + + /// get tracklet byteMask + uint8_t clusterCounters[8] = {0}; + { + uint8_t sectorIndex, rowIndex; + uint32_t clusterIndex; + const auto& tpcClusRefs = data.getTPCTracksClusterRefs(); + for (int i = 0; i < tpcOrig.getNClusterReferences(); i++) { + o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, tpcOrig.getClusterRef()); + char indexTracklet = (rowIndex % 152) / 19; + clusterCounters[indexTracklet]++; + } + } + uint8_t byteMask = 0; + for (int i = 0; i < 8; i++) { + if (clusterCounters[i] > 5) { + byteMask |= (1 << i); + } + } + trackQAHolder.tpcTime0 = tpcOrig.getTime0(); + trackQAHolder.tpcClusterByteMask = byteMask; + const auto& dEdxInfoAlt = tpcOrig.getdEdxAlt(); // tpcOrig.getdEdx() + const float dEdxNorm = (dEdxInfoAlt.dEdxTotTPC > 0) ? 100. / dEdxInfoAlt.dEdxTotTPC : 0; + trackQAHolder.tpcdEdxNorm = dEdxInfoAlt.dEdxTotTPC; + trackQAHolder.tpcdEdxMax0R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxIROC * dEdxNorm); + trackQAHolder.tpcdEdxMax1R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC1 * dEdxNorm); + trackQAHolder.tpcdEdxMax2R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC2 * dEdxNorm); + trackQAHolder.tpcdEdxMax3R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC3 * dEdxNorm); + // + trackQAHolder.tpcdEdxTot0R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotIROC * dEdxNorm); + trackQAHolder.tpcdEdxTot1R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC1 * dEdxNorm); + trackQAHolder.tpcdEdxTot2R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC2 * dEdxNorm); + trackQAHolder.tpcdEdxTot3R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC3 * dEdxNorm); + /// + float scaleTOF{0}; + auto contributorsGIDA = data.getSingleDetectorRefs(trackIndex); + if (contributorsGIDA[GIndex::Source::TOF].isIndexSet()) { // ITS-TPC-TRD-TOF, ITS-TPC-TOF, TPC-TRD-TOF, TPC-TOF + const auto& tofMatch = data.getTOFMatch(trackIndex); + const float qpt = trackPar.getQ2Pt(); + scaleTOF = std::sqrt(o2::aod::track::trackQAScaledTOF[0] * o2::aod::track::trackQAScaledTOF[0] + qpt * qpt * o2::aod::track::trackQAScaledTOF[1] * o2::aod::track::trackQAScaledTOF[1]) / (2. * o2::aod::track::trackQAScaleBins); + trackQAHolder.dTofdX = safeInt8Clamp(tofMatch.getDXatTOF() / scaleTOF); + trackQAHolder.dTofdZ = safeInt8Clamp(tofMatch.getDZatTOF() / scaleTOF); + } + + // Add matching information at a reference point (defined by + // o2::aod::track::trackQARefRadius) in the same frame as the global track + // without material corrections and error propagation + if (auto itsContGID = data.getITSContributorGID(trackIndex); itsContGID.isIndexSet() && itsContGID.getSource() != GIndex::ITSAB) { + const auto& itsOrig = data.getITSTrack(itsContGID); + o2::track::TrackPar gloCopy = trackPar; + o2::track::TrackPar itsCopy = itsOrig.getParamOut(); + o2::track::TrackPar tpcCopy = tpcOrig; + if (prop->propagateToX(gloCopy, o2::aod::track::trackQARefRadius, prop->getNominalBz(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr) && + prop->propagateToAlphaX(tpcCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr) && + prop->propagateToAlphaX(itsCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr)) { + // All tracks are now at the same radius and in the same frame and we can calculate the deltas wrt. to the global track + // The scale is defined by the global track scaling depending on beta0 + const float beta0 = std::sqrt(std::min(50.f / tpcOrig.getdEdx().dEdxMaxTPC, 1.f)); + const float qpt = gloCopy.getQ2Pt(); + const float x = qpt / beta0; + // scaling is defined as sigmaBins/sqrt(p0^2 + (p1 * q/pt / beta)^2) + auto scaleCont = [&x](int i) -> float { + return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleContP0[i] * o2::aod::track::trackQAScaleContP0[i] + (o2::aod::track::trackQAScaleContP1[i] * x) * (o2::aod::track::trackQAScaleContP1[i] * x)); + }; + auto scaleGlo = [&x](int i) -> float { + return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleGloP0[i] * o2::aod::track::trackQAScaleGloP0[i] + (o2::aod::track::trackQAScaleGloP1[i] * x) * (o2::aod::track::trackQAScaleGloP1[i] * x)); + }; + + // Calculate deltas for contributors + trackQAHolder.dRefContY = safeInt8Clamp((itsCopy.getY() - tpcCopy.getY()) * scaleCont(0)); + trackQAHolder.dRefContZ = safeInt8Clamp((itsCopy.getZ() - tpcCopy.getZ()) * scaleCont(1)); + trackQAHolder.dRefContSnp = safeInt8Clamp((itsCopy.getSnp() - tpcCopy.getSnp()) * scaleCont(2)); + trackQAHolder.dRefContTgl = safeInt8Clamp((itsCopy.getTgl() - tpcCopy.getTgl()) * scaleCont(3)); + trackQAHolder.dRefContQ2Pt = safeInt8Clamp((itsCopy.getQ2Pt() - tpcCopy.getQ2Pt()) * scaleCont(4)); + // Calculate deltas for global track against averaged contributors + trackQAHolder.dRefGloY = safeInt8Clamp(((itsCopy.getY() + tpcCopy.getY()) * 0.5f - gloCopy.getY()) * scaleGlo(0)); + trackQAHolder.dRefGloZ = safeInt8Clamp(((itsCopy.getZ() + tpcCopy.getZ()) * 0.5f - gloCopy.getZ()) * scaleGlo(1)); + trackQAHolder.dRefGloSnp = safeInt8Clamp(((itsCopy.getSnp() + tpcCopy.getSnp()) * 0.5f - gloCopy.getSnp()) * scaleGlo(2)); + trackQAHolder.dRefGloTgl = safeInt8Clamp(((itsCopy.getTgl() + tpcCopy.getTgl()) * 0.5f - gloCopy.getTgl()) * scaleGlo(3)); + trackQAHolder.dRefGloQ2Pt = safeInt8Clamp(((itsCopy.getQ2Pt() + tpcCopy.getQ2Pt()) * 0.5f - gloCopy.getQ2Pt()) * scaleGlo(4)); + // + + if (mStreamerFlags[AODProducerStreamerFlags::TrackQA]) { + (*mStreamer) << "trackQA" + << "trackITSOrig=" << itsOrig + << "trackTPCOrig=" << tpcOrig + << "trackITSTPCOrig=" << trackPar + << "trackITSProp=" << itsCopy + << "trackTPCProp=" << tpcCopy + << "trackITSTPCProp=" << gloCopy + << "refRadius=" << o2::aod::track::trackQARefRadius + << "scaleBins=" << o2::aod::track::trackQAScaleBins + << "scaleCont0=" << scaleCont(0) + << "scaleCont1=" << scaleCont(1) + << "scaleCont2=" << scaleCont(2) + << "scaleCont3=" << scaleCont(3) + << "scaleCont4=" << scaleCont(4) + << "scaleGlo0=" << scaleGlo(0) + << "scaleGlo1=" << scaleGlo(1) + << "scaleGlo2=" << scaleGlo(2) + << "scaleGlo3=" << scaleGlo(3) + << "scaleGlo4=" << scaleGlo(4) + << "trackQAHolder.tpcTime0=" << trackQAHolder.tpcTime0 + << "trackQAHolder.tpcdEdxNorm=" << trackQAHolder.tpcdEdxNorm + << "trackQAHolder.tpcdcaR=" << trackQAHolder.tpcdcaR + << "trackQAHolder.tpcdcaZ=" << trackQAHolder.tpcdcaZ + << "trackQAHolder.tpcdcaClusterByteMask=" << trackQAHolder.tpcClusterByteMask + << "trackQAHolder.tpcdEdxMax0R=" << trackQAHolder.tpcdEdxMax0R + << "trackQAHolder.tpcdEdxMax1R=" << trackQAHolder.tpcdEdxMax1R + << "trackQAHolder.tpcdEdxMax2R=" << trackQAHolder.tpcdEdxMax2R + << "trackQAHolder.tpcdEdxMax3R=" << trackQAHolder.tpcdEdxMax3R + << "trackQAHolder.tpcdEdxTot0R=" << trackQAHolder.tpcdEdxTot0R + << "trackQAHolder.tpcdEdxTot1R=" << trackQAHolder.tpcdEdxTot1R + << "trackQAHolder.tpcdEdxTot2R=" << trackQAHolder.tpcdEdxTot2R + << "trackQAHolder.tpcdEdxTot3R=" << trackQAHolder.tpcdEdxTot3R + << "trackQAHolder.dRefContY=" << trackQAHolder.dRefContY + << "trackQAHolder.dRefContZ=" << trackQAHolder.dRefContZ + << "trackQAHolder.dRefContSnp=" << trackQAHolder.dRefContSnp + << "trackQAHolder.dRefContTgl=" << trackQAHolder.dRefContTgl + << "trackQAHolder.dRefContQ2Pt=" << trackQAHolder.dRefContQ2Pt + << "trackQAHolder.dRefGloY=" << trackQAHolder.dRefGloY + << "trackQAHolder.dRefGloZ=" << trackQAHolder.dRefGloZ + << "trackQAHolder.dRefGloSnp=" << trackQAHolder.dRefGloSnp + << "trackQAHolder.dRefGloTgl=" << trackQAHolder.dRefGloTgl + << "trackQAHolder.dRefGloQ2Pt=" << trackQAHolder.dRefGloQ2Pt + << "trackQAHolder.dTofdX=" << trackQAHolder.dTofdX + << "trackQAHolder.dTofdZ=" << trackQAHolder.dTofdZ + << "scaleTOF=" << scaleTOF + << "\n"; + } + } + } + } + + return trackQAHolder; +} + +bool AODProducerWorkflowDPL::propagateTrackToPV(o2::track::TrackParametrizationWithError& trackPar, + const o2::globaltracking::RecoContainer& data, + int colID) +{ + o2::dataformats::DCA dcaInfo; + dcaInfo.set(999.f, 999.f, 999.f, 999.f, 999.f); + o2::dataformats::VertexBase v = mVtx.getMeanVertex(colID < 0 ? 0.f : data.getPrimaryVertex(colID).getZ()); + return o2::base::Propagator::Instance()->propagateToDCABxByBz(v, trackPar, 2.f, mMatCorr, &dcaInfo); +} + void AODProducerWorkflowDPL::extrapolateToCalorimeters(TrackExtraInfo& extraInfoHolder, const o2::track::TrackPar& track) { constexpr float XEMCAL = 440.f, XPHOS = 460.f, XEMCAL2 = XEMCAL * XEMCAL; @@ -2193,7 +3079,8 @@ void AODProducerWorkflowDPL::extrapolateToCalorimeters(TrackExtraInfo& extraInfo SPHOS, // 12 PHOS only SPHOS | SEMCAL, SPHOS | SEMCAL, SPHOS | SEMCAL, // 13:15 PHOS & DCAL SEMCAL, // 16 DCAL only - SNONE}; // 17 + SNONE // 17 + }; o2::track::TrackPar outTr{track}; auto prop = o2::base::Propagator::Instance(); @@ -2272,6 +3159,18 @@ void AODProducerWorkflowDPL::extrapolateToCalorimeters(TrackExtraInfo& extraInfo // } +std::set AODProducerWorkflowDPL::filterEMCALIncomplete(const gsl::span triggers) +{ + std::set emcalIncompletes; + for (const auto& trg : triggers) { + if (trg.getTriggerBits() & o2::emcal::triggerbits::Inc) { + // trigger record masked at incomplete at readout level + emcalIncompletes.insert(trg.getBCData().toLong()); + } + } + return emcalIncompletes; +} + void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); @@ -2279,12 +3178,12 @@ void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc) if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; // Note: DPLAlpideParam for ITS and MFT will be loaded by the RecoContainer - + mSqrtS = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS(); // apply settings auto grpECS = o2::base::GRPGeomHelper::instance().getGRPECS(); o2::BunchFilling bcf = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getBunchFilling(); std::bitset<3564> bs = bcf.getBCPattern(); - for (int i = 0; i < bs.size(); i++) { + for (auto i = 0U; i < bs.size(); i++) { if (bs.test(i)) { o2::tof::Utils::addInteractionBC(i); } @@ -2292,9 +3191,10 @@ void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc) const auto& alpParamsITS = o2::itsmft::DPLAlpideParam::Instance(); mITSROFrameHalfLengthNS = 0.5 * (grpECS->isDetContinuousReadOut(o2::detectors::DetID::ITS) ? alpParamsITS.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS : alpParamsITS.roFrameLengthTrig); - + mITSROFBiasNS = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingNS; const auto& alpParamsMFT = o2::itsmft::DPLAlpideParam::Instance(); mMFTROFrameHalfLengthNS = 0.5 * (grpECS->isDetContinuousReadOut(o2::detectors::DetID::MFT) ? alpParamsMFT.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS : alpParamsMFT.roFrameLengthTrig); + mMFTROFBiasNS = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingNS; // RS FIXME: this is not yet fetched from the CCDB auto& elParam = o2::tpc::ParameterElectronics::Instance(); @@ -2304,6 +3204,16 @@ void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc) mNSigmaTimeTrack = pvParams.nSigmaTimeTrack; mTimeMarginTrackTime = pvParams.timeMarginTrackTime * 1.e3; mFieldON = std::abs(o2::base::Propagator::Instance()->getNominalBz()) > 0.01; + + pc.inputs().get("ctpconfig"); + if (mEnableTRDextra) { + mTRDLocalGain = pc.inputs().get("trdlocalgainfactors").get(); + mTRDNoiseMap = pc.inputs().get("trdnoisemap").get(); + mTRDGainCalib = pc.inputs().get("trdgaincalib").get(); // time dependent gain + } + } + if (mPropTracks) { + pc.inputs().get("meanvtx"); } } @@ -2312,6 +3222,9 @@ void AODProducerWorkflowDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob { // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + if (matcher == ConcreteDataMatcher("GLO", "GRPMAGFIELD", 0)) { + o2::mch::TrackExtrap::setField(); + } return; } if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { @@ -2326,6 +3239,22 @@ void AODProducerWorkflowDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob par.printKeyValues(); return; } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mVtx = *(const o2::dataformats::MeanVertexObject*)obj; + return; + } + if (matcher == ConcreteDataMatcher("CTP", "CTPCONFIG", 0)) { + // construct mask with EMCAL trigger classes for rejection of incomplete triggers + auto ctpconfig = *(const o2::ctp::CTPConfiguration*)obj; + mEMCALTrgClassMask = 0; + for (const auto& trgclass : ctpconfig.getCTPClasses()) { + if (trgclass.cluster->maskCluster[o2::detectors::DetID::EMC]) { + mEMCALTrgClassMask |= trgclass.classMask; + } + } + LOG(info) << "Loaded EMCAL trigger class mask: " << std::bitset<64>(mEMCALTrgClassMask); + } } void AODProducerWorkflowDPL::addRefGlobalBCsForTOF(const o2::dataformats::VtxTrackRef& trackRef, const gsl::span& GIndices, @@ -2368,6 +3297,9 @@ void AODProducerWorkflowDPL::addRefGlobalBCsForTOF(const o2::dataformats::VtxTra float tofExpMom = 0.; if (tofInt.getTOF(o2::track::PID::Pion) > 0.f) { float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed)); + if (expBeta > o2::constants::math::Almost1) { + expBeta = o2::constants::math::Almost1; + } tofExpMom = o2::constants::physics::MassPionCharged * expBeta / std::sqrt(1.f - expBeta * expBeta); } else { continue; @@ -2452,25 +3384,159 @@ std::uint64_t AODProducerWorkflowDPL::fillBCSlice(int (&slice)[2], double tmin, return bcOfTimeRef; } -void AODProducerWorkflowDPL::endOfStream(EndOfStreamContext& ec) +std::vector AODProducerWorkflowDPL::fillBCFlags(const o2::globaltracking::RecoContainer& data, std::map& bcsMap) const +{ + std::vector flags(bcsMap.size()); + + // flag BCs belonging to UPC mode ITS ROFs + auto bcIt = bcsMap.begin(); + auto itsrofs = data.getITSTracksROFRecords(); + auto lROF = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; + auto bROF = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; + for (auto& rof : itsrofs) { + if (!rof.getFlag(o2::itsmft::ROFRecord::VtxUPCMode)) { + continue; + } + uint64_t globalBC0 = rof.getBCData().toLong() + bROF, globalBC1 = globalBC0 + lROF - 1; + // BCs are sorted, iterate until the start of ROF + while (bcIt != bcsMap.end()) { + if (bcIt->first < globalBC0) { + ++bcIt; + continue; + } + if (bcIt->first > globalBC1) { + break; + } + flags[bcIt->second] |= o2::aod::bc::ITSUPCMode; + ++bcIt; + } + } + return flags; +} + +bool AODProducerWorkflowDPL::collectConfigFiles(std::vector& keys, std::vector& values, int indent) +{ + // collect JSON-files of ConfigParams dumped by different upstream processors and add to medata + static std::string pattern, directory; + static size_t cachedNumberOfFiles = 0, cachedTotalFileSize = 0; + static bool first = true, discard = false; + if (discard) { + return false; + } + std::error_code ec; + if (first) { + first = false; + pattern = o2::base::NameConf::Instance().getConfigOutputFileName("*"); + auto dir = o2::conf::KeyValParam::Instance().getOutputDir(); + if (dir == "/dev/null") { + LOGP(warn, "ConfigParams output is disabled, abandoning {} files collection for metadata", pattern); + discard = true; + return false; + } + directory = (dir.empty() || dir == "none") ? "." : dir; + if (!std::filesystem::is_directory(directory, ec)) { + LOGP(error, R"(No directory "{}" is found to look for {} configuration files)", directory, pattern); + discard = true; + return false; + } + } + static std::unordered_map cachedMap; + std::vector files; + size_t currentTotalFileSize = 0; + + for (const auto& entry : std::filesystem::directory_iterator(directory)) { + if (!entry.is_regular_file()) { + continue; + } + const std::string fileName = entry.path().filename().string(); + if (fnmatch(pattern.c_str(), fileName.c_str(), 0) != 0) { + continue; + } + const auto fileSize = entry.file_size(ec); + if (ec) { + LOGP(error, "Cannot determine size of file {}, reason: {}", entry.path().string(), ec.message()); + } + files.push_back(entry.path()); + currentTotalFileSize += static_cast(fileSize); + } + + if (files.size() != cachedNumberOfFiles || currentTotalFileSize != cachedTotalFileSize) { // need to create a new map + cachedNumberOfFiles = files.size(); + cachedTotalFileSize = currentTotalFileSize; + cachedMap.clear(); + } + + if (!files.empty() && cachedMap.empty()) { + for (const auto& fname : files) { + std::ifstream input(fname); + if (!input) { + LOGP(error, "Cannot open JSON file {}", fname.string()); + cachedTotalFileSize = 0; // will trigger a new trial next time + continue; + } + nlohmann::json document; + try { + input >> document; + } catch (const nlohmann::json::parse_error& e) { + LOGP(error, "Cannot parse JSON file {}, reason: {}", fname.string(), e.what()); + cachedTotalFileSize = 0; // will trigger a new trial next time + continue; + } + + if (!document.is_object()) { + LOGP(error, "Top-level JSON value is not an object in file: {}", fname.string()); + cachedTotalFileSize = 0; // will trigger a new trial next time + continue; + } + + for (auto it = document.begin(); it != document.end(); ++it) { + const std::string& key = it.key(); + if (cachedMap.find(key) != cachedMap.end()) { + LOGP(error, "Duplicate top-level key {} in file {}", key, fname.string()); + continue; + } + LOGP(info, "Adding json config {} from file {} to AOD metadata", key, fname.string()); + nlohmann::json valueDocument = nlohmann::json::object(); + valueDocument[key] = it.value(); + cachedMap[key] = valueDocument.dump(indent); + } + } + } + + for (const auto& kv : cachedMap) { + keys.push_back(kv.first.c_str()); + values.push_back(kv.second.c_str()); + } + return true; +} + +void AODProducerWorkflowDPL::endOfStream(EndOfStreamContext& /*ec*/) { LOGF(info, "aod producer dpl total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + + mStreamer.reset(); } -DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool useMC, bool CTPConfigPerRun) +DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableStrangenessTracking, bool useMC, bool CTPConfigPerRun, bool enableFITextra, bool enableTRDextra) { - std::vector outputs; auto dataRequest = std::make_shared(); dataRequest->inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", CTPConfigPerRun)); dataRequest->requestTracks(src, useMC); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); if (src[GID::CTP]) { dataRequest->requestCTPDigits(useMC); } if (enableSV) { - dataRequest->requestSecondaryVertertices(useMC); + dataRequest->requestSecondaryVertices(useMC); + } + if (enableStrangenessTracking) { + dataRequest->requestStrangeTracks(useMC); + LOGF(info, "requestStrangeTracks Finish"); + } + if (src[GID::ITS]) { + dataRequest->requestClusters(GIndex::getSourcesMask("ITS"), false); } if (src[GID::TPC]) { dataRequest->requestClusters(GIndex::getSourcesMask("TPC"), false); // no need to ask for TOF clusters as they are requested with TOF tracks @@ -2500,44 +3566,80 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo dataRequest->inputs, true); // query only once all objects except mag.field - outputs.emplace_back(OutputLabel{"O2bc"}, "AOD", "BC", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2cascade_001"}, "AOD", "CASCADE_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2collision_001"}, "AOD", "COLLISION_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2decay3body"}, "AOD", "DECAY3BODY", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2fdd_001"}, "AOD", "FDD_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2ft0"}, "AOD", "FT0", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2fv0a"}, "AOD", "FV0A", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2fwdtrack"}, "AOD", "FWDTRACK", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2fwdtrackcov"}, "AOD", "FWDTRACKCOV", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mccollision"}, "AOD", "MCCOLLISION", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mccollisionlabel"}, "AOD", "MCCOLLISIONLABEL", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mcmfttracklabel"}, "AOD", "MCMFTTRACKLABEL", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mcfwdtracklabel"}, "AOD", "MCFWDTRACKLABEL", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mcparticle_001"}, "AOD", "MCPARTICLE_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mctracklabel"}, "AOD", "MCTRACKLABEL", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mfttrack"}, "AOD", "MFTTRACK", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2track_iu"}, "AOD", "TRACK_IU", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2trackcov_iu"}, "AOD", "TRACKCOV_IU", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2trackextra"}, "AOD", "TRACKEXTRA", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2ambiguoustrack"}, "AOD", "AMBIGUOUSTRACK", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2ambiguousMFTtrack"}, "AOD", "AMBIGUOUSMFTTR", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2ambiguousFwdtrack"}, "AOD", "AMBIGUOUSFWDTR", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2v0_001"}, "AOD", "V0_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2zdc"}, "AOD", "ZDC", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2caloCell"}, "AOD", "CALO", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2caloCellTRGR"}, "AOD", "CALOTRIGGER", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2cpvCluster"}, "AOD", "CPVCLUSTER", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2origin"}, "AOD", "ORIGIN", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); - outputs.emplace_back(OutputSpec{"TFF", "TFFilename"}); - outputs.emplace_back(OutputSpec{"AMD", "AODMetadataKeys"}); - outputs.emplace_back(OutputSpec{"AMD", "AODMetadataVals"}); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + + using namespace o2::aod; + using namespace o2::aodproducer; + + std::vector outputs{ + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputSpec{"TFN", "TFNumber"}, + OutputSpec{"TFF", "TFFilename"}, + OutputSpec{"AMD", "AODMetadataKeys"}, + OutputSpec{"AMD", "AODMetadataVals"}}; + /// Extra tables + if (enableTRDextra) { + outputs.push_back(OutputForTable::spec()); + dataRequest->inputs.emplace_back("trdlocalgainfactors", "TRD", "LOCALGAINFACTORS", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/LocalGainFactor")); + dataRequest->inputs.emplace_back("trdnoisemap", "TRD", "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/NoiseMapMCM")); + dataRequest->inputs.emplace_back("trdgaincalib", "TRD", "CALGAIN", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CalGain")); + } + + if (useMC) { + outputs.insert(outputs.end(), + {OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + // todo: use addTableToOuput helper? + // currently the description is MCCOLLISLABEL, so + // the name in AO2D would be O2mccollislabel + // addTableToOutput(outputs); + {OutputLabel{"McCollisionLabels"}, "AOD", "MCCOLLISIONLABEL", 0, Lifetime::Timeframe}}); + } return DataProcessorSpec{ "aod-producer-workflow", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(src, dataRequest, ggRequest, enableSV, useMC)}, + AlgorithmSpec{adaptFromTask(src, dataRequest, ggRequest, enableSV, useMC, enableFITextra, enableTRDextra)}, Options{ ConfigParamSpec{"run-number", VariantType::Int64, -1L, {"The run-number. If left default we try to get it from DPL header."}}, ConfigParamSpec{"aod-timeframe-id", VariantType::Int64, -1L, {"Set timeframe number"}}, @@ -2547,9 +3649,30 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo ConfigParamSpec{"anchor-pass", VariantType::String, "", {"AnchorPassName"}}, ConfigParamSpec{"anchor-prod", VariantType::String, "", {"AnchorProduction"}}, ConfigParamSpec{"reco-pass", VariantType::String, "", {"RecoPassName"}}, + ConfigParamSpec{"aod-parent", VariantType::String, "", {"Parent AOD file name (if any)"}}, + ConfigParamSpec{"created-by", VariantType::String, "", {"Who created this AO2D"}}, ConfigParamSpec{"nthreads", VariantType::Int, std::max(1, int(std::thread::hardware_concurrency() / 2)), {"Number of threads"}}, ConfigParamSpec{"reco-mctracks-only", VariantType::Int, 0, {"Store only reconstructed MC tracks and their mothers/daughters. 0 -- off, != 0 -- on"}}, - ConfigParamSpec{"ctpreadout-create", VariantType::Int, 0, {"Create CTP digits from detector readout and CTP inputs. !=1 -- off, 1 -- on"}}}}; + ConfigParamSpec{"ctpreadout-create", VariantType::Int, 0, {"Create CTP digits from detector readout and CTP inputs. !=1 -- off, 1 -- on"}}, + ConfigParamSpec{"emc-select-leading", VariantType::Bool, false, {"Flag to select if only the leading contributing particle for an EMCal cell should be stored"}}, + ConfigParamSpec{"propagate-tracks", VariantType::Bool, false, {"Propagate tracks (not used for secondary vertices) to IP"}}, + ConfigParamSpec{"propagate-tracks-max-xiu", VariantType::Float, 5.0f, {"Propagate tracks to IP if X_IU smaller than this value (and if propagate tracks enabled)"}}, + ConfigParamSpec{"hepmc-update", VariantType::String, "always", {"When to update HepMC Aux tables: always - force update, never - never update, all - if all keys are present, any - when any key is present (not valid yet)"}}, + ConfigParamSpec{"propagate-muons", VariantType::Bool, false, {"Propagate muons to IP"}}, + ConfigParamSpec{"store-all-mft-cov", VariantType::Bool, false, {"Store covariance matrices for all MFT tracks"}}, + ConfigParamSpec{"thin-tracks", VariantType::Bool, false, {"Produce thinned track tables"}}, + ConfigParamSpec{"trackqc-keepglobaltracks", VariantType::Bool, false, {"Always keep TrackQA for global tracks"}}, + ConfigParamSpec{"trackqc-retainonlydedx", VariantType::Bool, false, {"Keep only dEdx information, zero out everything else"}}, + ConfigParamSpec{"trackqc-fraction", VariantType::Float, float(0.1), {"Fraction of tracks to QC"}}, + ConfigParamSpec{"trackqc-NTrCut", VariantType::Int64, 4L, {"Minimal length of the track - in amount of tracklets"}}, + ConfigParamSpec{"trackqc-tpc-dca", VariantType::Float, 3.f, {"Keep TPC standalone track with this DCAxy to the PV"}}, + ConfigParamSpec{"trackqc-tpc-cls", VariantType::Int, 80, {"Keep TPC standalone track with this #clusters"}}, + ConfigParamSpec{"trackqc-tpc-pt", VariantType::Float, 0.2f, {"Keep TPC standalone track with this pt"}}, + ConfigParamSpec{"with-streamers", VariantType::String, "", {"Bit-mask to steer writing of intermediate streamer files"}}, + ConfigParamSpec{"seed", VariantType::Int, 0, {"Set seed for random generator used for sampling (0 (default) means using a random_device)"}}, + ConfigParamSpec{"mc-signal-filt", VariantType::Bool, false, {"Enable usage of signal filtering (only for MC with embedding)"}}, + ConfigParamSpec{"collect-config-files", VariantType::Bool, false, {"Collect ConfigParams json files written by upsteam processors"}}, + }}; } } // namespace o2::aodproducer diff --git a/Detectors/AOD/src/StandaloneAODProducer.cxx b/Detectors/AOD/src/StandaloneAODProducer.cxx index 151038465c1d1..7ac59a556ac08 100644 --- a/Detectors/AOD/src/StandaloneAODProducer.cxx +++ b/Detectors/AOD/src/StandaloneAODProducer.cxx @@ -86,7 +86,7 @@ void fillMCollisionTable(o2::steer::MCKinematicsReader const& mcreader) // mccollision::PosX, mccollision::PosY, mccollision::PosZ, mccollision::T, mccollision::Weight, // mccollision::ImpactParameter); - mcCollCursor(0, 0 /*bcID*/, 0 /*genID*/, header.GetX(), header.GetY(), header.GetZ(), time, 1. /*weight*/, header.GetB()); + mcCollCursor(0, 0 /*bcID*/, 0 /*genID*/, header.GetX(), header.GetY(), header.GetZ(), time, 1. /*weight*/, header.GetB(), 0.0); index++; } @@ -94,7 +94,7 @@ void fillMCollisionTable(o2::steer::MCKinematicsReader const& mcreader) TFile outfile("aod.root", "UPDATE"); { - TableToTree t2t(mccoltable, &outfile, aod::MetadataTrait::metadata::tableLabel()); + TableToTree t2t(mccoltable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } @@ -200,12 +200,12 @@ void fillCollisionAndTrackTable() f.Close(); TFile outfile("aod.root", "RECREATE"); { - TableToTree t2t(colltable, &outfile, aod::MetadataTrait::metadata::tableLabel()); + TableToTree t2t(colltable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } { - TableToTree t2t(tracktable, &outfile, "Tracks" /* aod::MetadataTrait::metadata::tableLabel() */); + TableToTree t2t(tracktable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } diff --git a/Detectors/AOD/src/aod-mc-producer-workflow.cxx b/Detectors/AOD/src/aod-mc-producer-workflow.cxx index 0bfd1ff021272..4f32bacac6fef 100644 --- a/Detectors/AOD/src/aod-mc-producer-workflow.cxx +++ b/Detectors/AOD/src/aod-mc-producer-workflow.cxx @@ -41,3 +41,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) wf.emplace_back(o2::aodmcproducer::getAODMcProducerWorkflowSpec()); return std::move(wf); } +// +// EOF +// diff --git a/Detectors/AOD/src/aod-producer-workflow.cxx b/Detectors/AOD/src/aod-producer-workflow.cxx index ecc68491fd4c3..d75694f3bd512 100644 --- a/Detectors/AOD/src/aod-producer-workflow.cxx +++ b/Detectors/AOD/src/aod-producer-workflow.cxx @@ -18,6 +18,7 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" #include "DetectorsBase/DPLWorkflowUtils.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -36,10 +37,14 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, {"disable-secondary-vertices", o2::framework::VariantType::Bool, false, {"disable filling secondary vertices"}}, + {"disable-strangeness-tracker", o2::framework::VariantType::Bool, false, {"disable filling strangeness tracking"}}, + {"enable-FIT-extra", o2::framework::VariantType::Bool, false, {"enable FIT extra output"}}, + {"enable-TRD-extra", o2::framework::VariantType::Bool, false, {"enable TRD extra output"}}, {"info-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}, - {"ctpconfig-per-run", o2::framework::VariantType::Bool, false, {"Use CTP config per run. 1 -- on (Data), 0 -- off (MC)"}}}; + {"ctpconfig-run-independent", o2::framework::VariantType::Bool, false, {"Use CTP config w/o runNumber tag"}}}; + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -51,9 +56,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); auto useMC = !configcontext.options().get("disable-mc"); bool enableSV = !configcontext.options().get("disable-secondary-vertices"); - bool ctpcfgperrun = configcontext.options().get("ctpconfig-per-run"); + bool enableST = !configcontext.options().get("disable-strangeness-tracker"); + bool ctpcfgperrun = !configcontext.options().get("ctpconfig-run-independent"); + bool enableFITextra = configcontext.options().get("enable-FIT-extra"); + bool enableTRDextra = configcontext.options().get("enable-TRD-extra"); - GID::mask_t allowedSrc = GID::getSourcesMask("ITS,MFT,MCH,MID,MCH-MID,TPC,TRD,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,FT0,FV0,FDD,ZDC,EMC,CTP,PHS,CPV"); + GID::mask_t allowedSrc = GID::getSourcesMask("ITS,MFT,MCH,MID,MCH-MID,TPC,TRD,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,FT0,FV0,FDD,ZDC,EMC,CTP,PHS,CPV,HMP"); GID::mask_t src = allowedSrc & GID::getSourcesMask(configcontext.options().get("info-sources")); // manually add TOF to MC mask for addInputSpecs() @@ -62,7 +70,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } WorkflowSpec specs; - specs.emplace_back(o2::aodproducer::getAODProducerWorkflowSpec(src, enableSV, useMC, ctpcfgperrun)); + specs.emplace_back(o2::aodproducer::getAODProducerWorkflowSpec(src, enableSV, enableST, useMC, ctpcfgperrun, enableFITextra, enableTRDextra)); auto srcCls = src & ~(GID::getSourceMask(GID::MCH) | GID::getSourceMask(GID::MID)); // Don't read global MID and MCH clusters (those attached to tracks are always read) auto srcMtc = src; @@ -73,6 +81,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (enableSV) { o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, inputspecs); } + if (enableST) { + o2::globaltracking::InputHelper::addInputSpecsStrangeTrack(configcontext, inputspecs, useMC); + } if (configcontext.options().get("combine-source-devices")) { std::vector unmerged; specs.push_back(specCombiner("AOD-input-reader", inputspecs, unmerged)); diff --git a/Detectors/Align/CMakeLists.txt b/Detectors/Align/CMakeLists.txt index c04b09dce44eb..d99e1a803612f 100644 --- a/Detectors/Align/CMakeLists.txt +++ b/Detectors/Align/CMakeLists.txt @@ -18,7 +18,7 @@ o2_add_library(Align src/AlignableDetectorITS.cxx #src/AlignableDetectorHMPID.cxx src/AlignableDetectorTOF.cxx - #src/AlignableDetectorTPC.cxx + src/AlignableDetectorTPC.cxx src/AlignableDetectorTRD.cxx src/Millepede2Record.cxx src/AlignmentPoint.cxx @@ -28,7 +28,7 @@ o2_add_library(Align #src/AlignableSensorHMPID.cxx src/AlignableSensorITS.cxx src/AlignableSensorTOF.cxx - #src/AlignableSensorTPC.cxx + src/AlignableSensorTPC.cxx src/AlignableSensorTRD.cxx src/Controller.cxx src/AlignmentTrack.cxx @@ -36,6 +36,8 @@ o2_add_library(Align src/EventVertex.cxx src/AlignConfig.cxx src/Mille.cxx + src/AlgPntDbg.cxx + src/AlgTrcDbg.cxx PUBLIC_LINK_LIBRARIES O2::FrameworkLogger O2::Steer O2::ReconstructionDataFormats @@ -46,6 +48,8 @@ o2_add_library(Align O2::TRDBase O2::TOFBase O2::DataFormatsGlobalTracking + O2::TPCCalibration + O2::GPUTracking ROOT::Core ROOT::Geom ROOT::MathCore @@ -60,7 +64,7 @@ o2_target_root_dictionary( include/Align/AlignableDetector.h include/Align/AlignableDetectorITS.h include/Align/AlignableDetectorTOF.h - #include/Align/AlignableDetectorTPC.h + include/Align/AlignableDetectorTPC.h include/Align/AlignableDetectorTRD.h #include/Align/AlignableDetectorHMPID.h include/Align/Millepede2Record.h @@ -68,7 +72,7 @@ o2_target_root_dictionary( include/Align/AlignableSensor.h include/Align/AlignableSensorITS.h include/Align/AlignableSensorTOF.h - #include/Align/AlignableSensorTPC.h + include/Align/AlignableSensorTPC.h include/Align/AlignableSensorTRD.h #include/Align/AlignableSensorHMPID.h include/Align/Controller.h @@ -80,6 +84,8 @@ o2_target_root_dictionary( include/Align/GeometricalConstraint.h include/Align/utils.h include/Align/AlignConfig.h + include/Align/AlgPntDbg.h + include/Align/AlgTrcDbg.h ) add_subdirectory(Workflow) diff --git a/Detectors/Align/Workflow/CMakeLists.txt b/Detectors/Align/Workflow/CMakeLists.txt index 1198064bc8984..8ceb50f71ca29 100644 --- a/Detectors/Align/Workflow/CMakeLists.txt +++ b/Detectors/Align/Workflow/CMakeLists.txt @@ -22,6 +22,8 @@ o2_add_library(AlignmentWorkflow O2::DataFormatsTPC O2::DataFormatsTRD O2::DataFormatsTOF + O2::GPUTracking + O2::TPCCalibration ROOT::Core ROOT::Geom ROOT::MathCore diff --git a/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h b/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h index fdfb62cebad5e..9793b8609874f 100644 --- a/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h +++ b/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h @@ -21,6 +21,7 @@ using namespace o2::framework; namespace o2 { + namespace align { diff --git a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx index 1192377c64f47..3e42c19f535f8 100644 --- a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx +++ b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx @@ -35,7 +35,11 @@ #include "DataFormatsITSMFT/TopologyDictionary.h" #include "TRDBase/TrackletTransformer.h" #include "CommonUtils/TreeStreamRedirector.h" - +#include "TPCCalibration/VDriftHelper.h" +#include "TPCFastTransformPOD.h" +#include "GPUO2ExternalUser.h" +#include "GPUO2InterfaceUtils.h" +#include "GPUParam.h" #include "Headers/DataHeader.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/Task.h" @@ -60,7 +64,7 @@ #include "DataFormatsTPC/ClusterNative.h" #include "DataFormatsTPC/WorkflowHelper.h" #include "ITSBase/GeometryTGeo.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" */ using namespace o2::framework; @@ -83,8 +87,8 @@ class BarrelAlignmentSpec : public Task GenPedeFiles = 0x1 << 2, LabelPedeResults = 0x1 << 3 }; BarrelAlignmentSpec(GTrackID::mask_t srcMP, std::shared_ptr dr, std::shared_ptr ggrec, - DetID::mask_t detmask, bool cosmic, int postprocess, bool useMC) - : mDataRequest(dr), mGRPGeomRequest(ggrec), mMPsrc{srcMP}, mDetMask{detmask}, mCosmic(cosmic), mPostProcessing(postprocess), mUseMC(useMC) {} + DetID::mask_t detmask, bool cosmic, int postprocess, bool useMC, bool loadTPCCalib) + : mDataRequest(dr), mGRPGeomRequest(ggrec), mMPsrc{srcMP}, mDetMask{detmask}, mCosmic(cosmic), mPostProcessing(postprocess), mUseMC(useMC), mLoadTPCCalib(loadTPCCalib) {} ~BarrelAlignmentSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -98,6 +102,8 @@ class BarrelAlignmentSpec : public Task bool mUseMC = false; bool mIgnoreCCDBAlignment = false; bool mCosmic = false; + bool mLoadTPCCalib = false; + int mLane = 0; int mPostProcessing = 0; // special mode of extracting alignment or constraints check GTrackID::mask_t mMPsrc{}; DetID::mask_t mDetMask{}; @@ -108,6 +114,12 @@ class BarrelAlignmentSpec : public Task std::unique_ptr mUsrConfMethod; std::unique_ptr mTRDTransformer; std::unique_ptr mDBGOut; + std::unique_ptr mTPCParam; + + o2::tpc::VDriftHelper mTPCVDriftHelper{}; + + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps{}; + // TStopwatch mTimer; }; @@ -117,12 +129,12 @@ void BarrelAlignmentSpec::init(InitContext& ic) mTimer.Stop(); mTimer.Reset(); o2::base::GRPGeomHelper::instance().setRequest(mGRPGeomRequest); - int dbg = ic.options().get("debug-output"), inst = ic.services().get().inputTimesliceId; - mController = std::make_unique(mDetMask, mMPsrc, mCosmic, mUseMC, inst); + + int dbg = ic.options().get("debug-output"); + mLane = ic.services().get().inputTimesliceId; + mController = std::make_unique(mDetMask, mMPsrc, mCosmic, mUseMC, mLane); if (dbg) { - mDBGOut = std::make_unique(fmt::format("mpDebug_{}.root", inst).c_str(), "recreate"); mController->setDebugOutputLevel(dbg); - mController->setDebugStream(mDBGOut.get()); } mConfMacro = ic.options().get("config-macro"); @@ -132,8 +144,7 @@ void BarrelAlignmentSpec::init(InitContext& ic) } std::string tmpmacro = mConfMacro + "+"; TString cmd = gSystem->GetMakeSharedLib(); - cmd.ReplaceAll("$Opt", "-O0 -g -ggdb"); - + cmd += " -O0 -g -ggdb"; // protect macro compilation by semaphore (to avoid problems in the pipelined code) { boost::interprocess::named_semaphore* sem = nullptr; @@ -174,10 +185,17 @@ void BarrelAlignmentSpec::init(InitContext& ic) if (ic.options().get("apply-xor")) { mTRDTransformer->setApplyXOR(); } + auto prevShift = mTRDTransformer->isShiftApplied(); + if (getenv("ALIEN_JDL_LPMPRODUCTIONTYPE") && std::strcmp(getenv("ALIEN_JDL_LPMPRODUCTIONTYPE"), "MC") == 0) { + // apply artificial pad shift in case non-ideal alignment is used to compensate for shift in current alignment from real data + mTRDTransformer->setApplyShift(false); + } + LOGP(info, "Old TRD shift : {} new : {}", prevShift, mTRDTransformer->isShiftApplied()); mController->setTRDTransformer(mTRDTransformer.get()); } mController->setAllowAfterburnerTracks(ic.options().get("allow-afterburner-tracks")); } + mIniParFile = ic.options().get("initial-params-file"); mUseIniParErrors = !ic.options().get("ignore-initial-params-errors"); if (mPostProcessing && !(mPostProcessing != GenPedeFiles) && (mIniParFile.empty() || mIniParFile == "none")) { @@ -187,10 +205,13 @@ void BarrelAlignmentSpec::init(InitContext& ic) void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) { - static bool initOnceDone = false; - if (!initOnceDone) { - initOnceDone = true; - o2::base::GRPGeomHelper::instance().checkUpdates(pc); + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + auto tinfo = pc.services().get(); + if (pc.services().get().globalRunNumberChanged) { + if (mController->getDebugOutputLevel()) { + mDBGOut = std::make_unique(fmt::format("mpDebug_{}_{:08d}_{:010d}.root", mLane, tinfo.runNumber, tinfo.tfCounter).c_str(), "recreate"); + mController->setDebugStream(mDBGOut.get()); + } if (!mIgnoreCCDBAlignment) { for (auto id = DetID::First; id <= DetID::Last; id++) { const auto* alg = o2::base::GRPGeomHelper::instance().getAlignment(id); @@ -216,7 +237,7 @@ void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) // call this in the very end if (mUsrConfMethod) { - int dummyPar = 0, ret = 0; + int dummyPar = 0, ret = -1; Controller* tmpPtr = mController.get(); const void* args[2] = {&tmpPtr, &dummyPar}; mUsrConfMethod->Execute(nullptr, args, 2, &ret); @@ -230,6 +251,33 @@ void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) if (GTrackID::includesDet(DetID::TRD, mMPsrc) && mTRDTransformer) { pc.inputs().get("calvdexb"); // just to trigger the finaliseCCDB } + if (mLoadTPCCalib) { + + static float prevField = 1e-6; + float newField = o2::base::Propagator::Instance()->getNominalBz(); + if (prevField != newField) { + prevField = newField; + if (mDetMask[DetID::TPC]) { + mTPCParam.reset(new o2::gpu::GPUParam); + mTPCParam->SetDefaults(o2::base::Propagator::Instance()->getNominalBz(), false); + mController->setTPCParam(mTPCParam.get()); + } + } + + mTPCVDriftHelper.extractCCDBInputs(pc); + + auto const& raw = pc.inputs().get("corrMap"); + mTPCCorrMaps = &o2::gpu::TPCFastTransformPOD::get(raw); + mController->setTPCCorrMaps(mTPCCorrMaps); + if (mTPCVDriftHelper.isUpdated()) { + LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, + mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, + mTPCVDriftHelper.getSourceName()); + mController->setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); + mTPCVDriftHelper.acknowledgeUpdate(); + } + } } void BarrelAlignmentSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -248,13 +296,17 @@ void BarrelAlignmentSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& match mTRDTransformer->setCalVdriftExB((const o2::trd::CalVdriftExB*)obj); return; } + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + + return; + } } void BarrelAlignmentSpec::run(ProcessingContext& pc) { mTimer.Start(false); - updateTimeDependentParams(pc); if (mPostProcessing) { // special mode, no data processing + updateTimeDependentParams(pc); if (mController->getInstanceID() == 0) { if (mPostProcessing & PostProc::CheckConstaints) { mController->addAutoConstraints(); @@ -268,6 +320,7 @@ void BarrelAlignmentSpec::run(ProcessingContext& pc) } else { RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); // call after collectData !!! mController->setRecoContainer(&recoData); mController->setTimingInfo(pc.services().get()); if (mCosmic) { @@ -308,16 +361,30 @@ DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_ { std::vector outputs; auto dataRequest = std::make_shared(); + bool loadTPCCalib = false; + Options opts{ + ConfigParamSpec{"apply-xor", o2::framework::VariantType::Bool, false, {"flip the 8-th bit of slope and position (for processing TRD CTFs from 2021 pilot beam)"}}, + ConfigParamSpec{"allow-afterburner-tracks", VariantType::Bool, false, {"allow using ITS-TPC afterburner tracks"}}, + ConfigParamSpec{"ignore-ccdb-alignment", VariantType::Bool, false, {"do not aplly CCDB alignment to ideal geometry"}}, + ConfigParamSpec{"initial-params-file", VariantType::String, "", {"initial parameters file"}}, + ConfigParamSpec{"config-macro", VariantType::String, "", {"configuration macro with signature (o2::align::Controller*, int) to execute from init"}}, + ConfigParamSpec{"ignore-initial-params-errors", VariantType::Bool, false, {"ignore initial params (if any) errors for precondition"}}, + ConfigParamSpec{"debug-output", VariantType::Int, 0, {"produce debugging output root files"}}}; if (!postprocess) { dataRequest->requestTracks(src, useMC); dataRequest->requestClusters(src, false, skipDetClusters); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); if (GTrackID::includesDet(DetID::TRD, srcMP)) { dataRequest->inputs.emplace_back("calvdexb", "TRD", "CALVDRIFTEXB", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CalVdriftExB")); } if (enableCosmic) { dataRequest->requestCoscmicTracks(useMC); } + if (src[DetID::TPC] && !skipDetClusters[DetID::TPC]) { + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + dataRequest->inputs.emplace_back("corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe); + loadTPCCalib = true; + } } auto ccdbRequest = std::make_shared(true, // orbitResetTime true, // GRPECS=true @@ -326,21 +393,15 @@ DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_ true, // askMatLUT o2::base::GRPGeomRequest::Alignments, // geometry dataRequest->inputs, - false, // ask update once (except field) - true); // init PropagatorD + false, // ask update once (except field) + true, // init PropagatorD + "ITS,TPC,TRD,TOF"); // alignment objects to apply return DataProcessorSpec{ "barrel-alignment", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(srcMP, dataRequest, ccdbRequest, dets, enableCosmic, postprocess, useMC)}, - Options{ - ConfigParamSpec{"apply-xor", o2::framework::VariantType::Bool, false, {"flip the 8-th bit of slope and position (for processing TRD CTFs from 2021 pilot beam)"}}, - ConfigParamSpec{"allow-afterburner-tracks", VariantType::Bool, false, {"allow using ITS-TPC afterburner tracks"}}, - ConfigParamSpec{"ignore-ccdb-alignment", VariantType::Bool, false, {"do not aplly CCDB alignment to ideal geometry"}}, - ConfigParamSpec{"initial-params-file", VariantType::String, "", {"initial parameters file"}}, - ConfigParamSpec{"config-macro", VariantType::String, "", {"configuration macro with signature (o2::align::Controller*, int) to execute from init"}}, - ConfigParamSpec{"ignore-initial-params-errors", VariantType::Bool, false, {"ignore initial params (if any) errors for precondition"}}, - ConfigParamSpec{"debug-output", VariantType::Int, 0, {"produce debugging output root files"}}}}; + AlgorithmSpec{adaptFromTask(srcMP, dataRequest, ccdbRequest, dets, enableCosmic, postprocess, useMC, loadTPCCalib)}, + opts}; } } // namespace align diff --git a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx index 0fdd1d28ee327..07224702b1be1 100644 --- a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx +++ b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx @@ -19,12 +19,15 @@ #include "TPCReaderWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/ClusterReaderSpec.h" #include "TPCWorkflow/ClusterSharingMapSpec.h" +#include "TPCWorkflow/TPCScalerSpec.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" #include "TOFWorkflowIO/TOFMatchedReaderSpec.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DetectorsCommonDataFormats/DetID.h" #include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" +#include "TPCCalibration/CorrectionMapsOptions.h" #include "Algorithm/RangeTokenizer.h" #include "DetectorsRaw/HBFUtilsInitializer.h" @@ -51,12 +54,14 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"enable-mc", o2::framework::VariantType::Bool, false, {"enable MC-info checks"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, - {"detectors", VariantType::String, std::string{"ITS,TRD,TOF"}, {"comma-separated list of detectors"}}, + {"detectors", VariantType::String, std::string{"ITS,TPC,TRD,TOF"}, {"comma-separated list of detectors"}}, {"enable-tpc-tracks", VariantType::Bool, false, {"allow reading TPC tracks"}}, {"enable-tpc-clusters", VariantType::Bool, false, {"allow reading TPC clusters (will trigger TPC tracks reading)"}}, {"enable-cosmic", VariantType::Bool, false, {"enable cosmic tracks)"}}, {"postprocessing", VariantType::Int, 0, {"postprocessing bits: 1 - extract alignment objects, 2 - check constraints, 4 - print mpParams/Constraints, 8 - relabel pede results"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); + o2::tpc::CorrectionMapsOptions::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -79,7 +84,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { WorkflowSpec specs; GID::mask_t alowedSources = GID::getSourcesMask("ITS,TPC,TRD,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); - DetID::mask_t allowedDets = DetID::getMask("ITS,TRD,TOF"); + DetID::mask_t allowedDets = DetID::getMask("ITS,TPC,TRD,TOF"); // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); @@ -96,6 +101,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) GID::mask_t src = alowedSources & GID::getSourcesMask(configcontext.options().get("track-sources")); GID::mask_t srcCl{}, srcMP = src; // we may need to load more track types than requested to satisfy dependencies, but only those will be fed to millipede + if (dets[DetID::TPC]) { + loadTPCClusters = loadTPCTracks = true; + } + auto sclOpt = o2::tpc::CorrectionMapsOptions::parseGlobalOptions(configcontext.options()); if (!postprocess) { // this part is needed only if the data should be read if (GID::includesDet(DetID::ITS, src)) { src |= GID::getSourceMask(GID::ITS); @@ -134,10 +143,17 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } LOG(info) << "adding TOF request"; } + if (sclOpt.requestCTPLumi) { + src = src | GID::getSourcesMask("CTP"); + } // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2_barrel_alignment_configuration.ini"); } + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); + } + specs.emplace_back(o2::align::getBarrelAlignmentSpec(srcMP, src, dets, skipDetClusters, enableCosmic, postprocess, useMC)); // RS FIXME: check which clusters are really needed if (!postprocess) { diff --git a/Detectors/Align/include/Align/AlgPntDbg.h b/Detectors/Align/include/Align/AlgPntDbg.h new file mode 100644 index 0000000000000..12e8205f6b614 --- /dev/null +++ b/Detectors/Align/include/Align/AlgPntDbg.h @@ -0,0 +1,91 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file AlignmentPoint.h +/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch +/// @since 2021-02-01 +/// @brief Meausered point in the sensor. + +/** + * Compact alignment point info for debugging + */ + +#ifndef ALGPNTDBG_H +#define ALGPNTDBG_H + +#include "Align/AlignmentPoint.h" + +namespace o2 +{ +namespace align +{ + +struct AlgPntDbg { + public: + using DetID = o2::detectors::DetID; + // + enum { + UpperLeg = 0 + }; + // + AlgPntDbg() = default; + AlgPntDbg(const AlgPntDbg&) = default; + ~AlgPntDbg() = default; + AlgPntDbg& operator=(const AlgPntDbg& other) = default; + AlgPntDbg(const AlignmentPoint* point) : mDetID(point->getDetID()), mSID(point->getSID()), mAlpha(point->getAlphaSens()), mX(point->getXTracking()), mY(point->getYTracking()), mZ(point->getZTracking()), mErrYY(point->getYZErrTracking()[0]), mErrZZ(point->getYZErrTracking()[2]), mErrYZ(point->getYZErrTracking()[1]) + { + mSinAlp = std::sin(mAlpha); + mCosAlp = std::cos(mAlpha); + mSnp = point->getTrParamWSA()[2]; // track Snp at the sensor + if (point->isInvDir()) { + setUpperLeg(); + } + } + + float getR() const { return std::sqrt(mX * mX + mY * mY); } + float getYTrack() const { return mY + mYRes; } + float getZTrack() const { return mZ + mZRes; } + float getXTrack() const { return mX; } + float getXLab() const { return mX * mCosAlp - mY * mSinAlp; } + float getYLab() const { return mX * mSinAlp + mY * mCosAlp; } + float getZLap() const { return mZ; } + float getXTrackLab() const { return mX * mCosAlp - getYTrack() * mSinAlp; } + float getYTrackLab() const { return mX * mSinAlp + getYTrack() * mCosAlp; } + float getZTrackLab() const { return getZTrack(); } + float getPhi() const { return std::atan2(getYLab(), getXLab()); } + void setFlag(int i) { mFlags |= 0x1 << i; } + bool getFlag(int i) const { return (mFlags & (0x1 << i)) != 0; } + + void setUpperLeg() { setFlag(int(UpperLeg)); } + bool isUpperLeg() const { return getFlag(int(UpperLeg)); } + + int mDetID{}; // DetectorID + int16_t mSID = -1; // sensor ID in the detector + uint16_t mFlags = 0; // flags + float mAlpha = 0.f; // Alpha of tracking frame + float mSinAlp = 0.f; + float mCosAlp = 0.f; + float mX = 0.f; // tracking X + float mY = 0.f; // tracking Y + float mZ = 0.f; // Z + float mYRes = 0.f; // tracking Y residual (track - point) + float mZRes = 0.f; // Z residual + float mErrYY = 0.f; + float mErrZZ = 0.f; + float mErrYZ = 0.f; + float mSnp = 0.f; + + ClassDefNV(AlgPntDbg, 1); +}; + +} // namespace align +} // namespace o2 +#endif diff --git a/Detectors/Align/include/Align/AlgTrcDbg.h b/Detectors/Align/include/Align/AlgTrcDbg.h new file mode 100644 index 0000000000000..cb179dd8b9b2f --- /dev/null +++ b/Detectors/Align/include/Align/AlgTrcDbg.h @@ -0,0 +1,90 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file AlgTrcDbg.h +/// @author ruben.shahoyan@cern.ch + +#ifndef ALGTRCDBG_H +#define ALGTRCDBG_H + +#include "Align/AlignmentTrack.h" +#include "Align/AlgPntDbg.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/Track.h" + +namespace o2 +{ +namespace align +{ + +struct AlgTrcDbg : public o2::track::TrackParCov { + AlgTrcDbg(const AlignmentTrack* trc) { setTrackParam(trc); } + AlgTrcDbg() = default; + ~AlgTrcDbg() = default; + AlgTrcDbg(const AlgTrcDbg&) = default; + AlgTrcDbg& operator=(const AlgTrcDbg&) = default; + + bool setTrackParam(const AlignmentTrack* trc) + { + if (!trc) { + return false; + } + setX(trc->getX()); + setY(trc->getAlpha()); + for (int i = 0; i < 5; i++) { + setParam(trc->getParam(i), i); + } + for (int i = 0; i < 15; i++) { + setCov(trc->getCov()[i], i); + } + mPoints.clear(); + for (int i = 0; i < trc->getNPoints(); i++) { + const auto* tpoint = trc->getPoint(i); + if (tpoint->containsMeasurement()) { + auto& pnt = mPoints.emplace_back(tpoint); + pnt.mYRes = trc->getResidual(0, i); + pnt.mZRes = trc->getResidual(1, i); + } + } + setX(trc->getX()); + setY(trc->getAlpha()); + for (int i = 0; i < 5; i++) { + setParam(trc->getParam(i), i); + } + for (int i = 0; i < 15; i++) { + setCov(trc->getCov()[i], i); + } + for (int i = 0; i < trc->getNPoints(); i++) { + const auto* tpoint = trc->getPoint(i); + if (tpoint->containsMeasurement()) { + auto& pnt = mPoints.emplace_back(tpoint); + pnt.mYRes = trc->getResidual(0, i); + pnt.mZRes = trc->getResidual(1, i); + } + } + mGID.clear(); + mGIDCosmUp.clear(); + return true; + } + + auto getNPoints() const { return mPoints.size(); } + bool isCosmic() const { return mGIDCosmUp.isSourceSet(); } + + std::vector mPoints; + o2::dataformats::GlobalTrackID mGID{}; + o2::dataformats::GlobalTrackID mGIDCosmUp{}; // GID of upper leg in case of cosmic + // + ClassDefNV(AlgTrcDbg, 1); +}; + +} // namespace align +} // namespace o2 +#endif diff --git a/Detectors/Align/include/Align/AlignConfig.h b/Detectors/Align/include/Align/AlignConfig.h index 0400f11190806..e72d436a14e3b 100644 --- a/Detectors/Align/include/Align/AlignConfig.h +++ b/Detectors/Align/include/Align/AlignConfig.h @@ -35,10 +35,12 @@ struct AlignConfig : public o2::conf::ConfigurableParamHelper { float q2PtMin[NTrackTypes] = {0.01, 0.01}; float q2PtMax[NTrackTypes] = {10., 10.}; float tglMax[NTrackTypes] = {3., 10.}; + float defPTB0Coll = 0.6; + float defPTB0Cosm = 3.0; int minPoints[NTrackTypes] = {4, 10}; int minDetAcc[NTrackTypes] = {1, 1}; - float minScatteringAngleToAccount = 0.0001; + float minScatteringAngleToAccount = 0.0003; int verbose = 0; @@ -50,6 +52,32 @@ struct AlignConfig : public o2::conf::ConfigurableParamHelper { int minDetectors = 1; // min number of detectors per track int minITSClusters = 4; // min ITS clusters to accept the track int minTRDTracklets = 3; // min TRD tracklets to accept the track + int minTPCClusters = 10; // discard tracks with less clusters + int minTOFClusters = 1; // min TOF clusters to accept track + int maxTPCRowsCombined = 1; // allow combining clusters on so many rows to a single cluster + int discardEdgePadrows = 3; // discard padrow if its distance to stack edge padrow < this + float discardSectorEdgeDepth = 2.5; // discard clusters too close to the sector edge + float ITSOverlapMargin = 0.15; // consider for overlaps only clusters within this marging from the chip edge (in cm) + float ITSOverlapMaxChi2 = 16; // max chi2 between track and overlapping cluster + int ITSOverlapEdgeRows = 1; // require clusters to not have pixels closer than this distance from the edge + float ITSOverlapMaxDZ = 0.3; // max difference in Z for clusters on overlapping ITS chips to consider as candidate for a double hit + + int minPointTotalCosm = 4; // total min number of alignment point to account cosmic track + int minDetectorsCosm = 1; // min number of detectors per cosmic track + int minITSClustersCosm = 0; // min ITS clusters to accept the cosmic track + int minITSClustersCosmLeg = 2; // min ITS clusters per leg to accept the cosmic track + int minTRDTrackletsCosm = 0; // min TRD tracklets to accept the cosmic track + int minTRDTrackletsCosmLeg = 2; // min TRD tracklets per leg to accept the cosmic track + int minTPCClustersCosm = 0; // discard cosmic tracks with less clusters + int minTPCClustersCosmLeg = 10; // discard cosmic tracks with less clusters per leg + int minTOFClustersCosm = 0; // min TOF clusters to accept track + int minTOFClustersCosmLeg = 1; // min TOF clusters per leg to accept track + + int minTPCPadRow = 6; // min TPC pad-row to account + int maxTPCPadRow = 146; // max TPC pad-row to account + + float cosmMaxDSnp = 0.025; // reject cosmic tracks with larger than this snp difference + float cosmMaxDTgl = 0.1; // reject cosmic tracks with larger than this tgl difference float maxDCAforVC[2] = {-1, -1}; // DCA cut in R,Z to allow track be subjected to vertex constraint float maxChi2forVC = -1; // track-vertex chi2 cut to allow the track be subjected to vertex constraint @@ -57,6 +85,7 @@ struct AlignConfig : public o2::conf::ConfigurableParamHelper { float controlFraction = -1.; // fraction for which control output is requested, if negative - only 1st instance of device will write them float MPRecOutFraction = -1.; // compact Millepede2Record fraction, if negative - only 1st instance of device will write them + bool useLinRef = true; // use initial track for lienarization reference point bool MilleOut = true; // Mille output bool KalmanResid = true; // Kalman residuals bool MilleOutBin = true; // text vs binary output for mille data diff --git a/Detectors/Align/include/Align/AlignableDetector.h b/Detectors/Align/include/Align/AlignableDetector.h index 5d671a2b5dbf1..97963c006aa46 100644 --- a/Detectors/Align/include/Align/AlignableDetector.h +++ b/Detectors/Align/include/Align/AlignableDetector.h @@ -115,7 +115,7 @@ class AlignableDetector : public DOFSet void Print(const Option_t* opt = "") const override; virtual void reset(); - virtual int processPoints(GIndex gid, bool inv = false); + virtual int processPoints(GIndex gid, int npntCut = 0, bool inv = false); virtual bool prepareDetectorData() { return true; } virtual void updatePointByTrackInfo(AlignmentPoint* pnt, const trackParam_t* t) const; @@ -209,8 +209,7 @@ class AlignableDetector : public DOFSet TObjArray mVolumes; // all volumes of the detector // // this is transient info - int mNPoints = 0; //! number of points from this detector - int mFirstPoint = 0; //! entry of the 1st point + int mNPoints = 0; //! number of points from this detector // ClassDefOverride(AlignableDetector, 1); // base class for detector global alignment }; diff --git a/Detectors/Align/include/Align/AlignableDetectorITS.h b/Detectors/Align/include/Align/AlignableDetectorITS.h index dcf23f4eca181..5f1816d8368b8 100644 --- a/Detectors/Align/include/Align/AlignableDetectorITS.h +++ b/Detectors/Align/include/Align/AlignableDetectorITS.h @@ -21,6 +21,7 @@ #include "Align/utils.h" #include "ReconstructionDataFormats/TrackParametrizationWithError.h" #include "ReconstructionDataFormats/BaseCluster.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" namespace o2 { @@ -39,6 +40,11 @@ class AlignableDetectorITS : public AlignableDetector public: // using ClusterD = o2::BaseCluster; + using OVL = o2::itsmft::ChipMappingITS::Overlaps; + enum EdgeFlags : int8_t { NONE = -1, + LowRow = OVL::LowRow, + HighRow = OVL::HighRow, + Biased = 2 }; AlignableDetectorITS() = default; // RS FIXME do we need default c-tor? AlignableDetectorITS(Controller* ctr); ~AlignableDetectorITS() override = default; @@ -49,7 +55,7 @@ class AlignableDetectorITS : public AlignableDetector // RSTODO // bool AcceptTrack(const AliESDtrack* trc, int trtype) const; - int processPoints(GIndex gid, bool inv) final; + int processPoints(GIndex gid, int npntCut, bool inv) final; bool prepareDetectorData() final; void SetAddErrorLr(int ilr, double sigY, double sigZ); @@ -71,6 +77,9 @@ class AlignableDetectorITS : public AlignableDetector protected: // std::vector mITSClustersArray; + std::vector mOverlapCandidateID; // pool of indices for potentially overlapping clusters + std::vector mOverlapClusRef; // 1st entry in mOverlapCandidateID for the overlapping cluster indices of each cluster + std::vector mOverlaps; const o2::itsmft::TopologyDictionary* mITSDict{nullptr}; // cluster patterns dictionary // ClassDefOverride(AlignableDetectorITS, 1); diff --git a/Detectors/Align/include/Align/AlignableDetectorTOF.h b/Detectors/Align/include/Align/AlignableDetectorTOF.h index 7c0736c639bdc..b5434bcf6ad3c 100644 --- a/Detectors/Align/include/Align/AlignableDetectorTOF.h +++ b/Detectors/Align/include/Align/AlignableDetectorTOF.h @@ -24,7 +24,7 @@ namespace o2 namespace align { -class AlignableDetectorTOF : public AlignableDetector +class AlignableDetectorTOF final : public AlignableDetector { public: AlignableDetectorTOF() = default; @@ -33,7 +33,7 @@ class AlignableDetectorTOF : public AlignableDetector // void defineVolumes() final; // - int processPoints(GIndex gid, bool inv) final; + int processPoints(GIndex gid, int npntCut, bool inv) final; // protected: // diff --git a/Detectors/Align/include/Align/AlignableDetectorTPC.h b/Detectors/Align/include/Align/AlignableDetectorTPC.h index d513f983d44e4..af85e18bf2de1 100644 --- a/Detectors/Align/include/Align/AlignableDetectorTPC.h +++ b/Detectors/Align/include/Align/AlignableDetectorTPC.h @@ -10,8 +10,7 @@ // or submit itself to any jurisdiction. /// @file AlignableDetectorTPC.h -/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch -/// @since 2021-02-01 +/// @author ruben.shahoyan@cern.ch /// @brief TPC detector wrapper #ifndef ALIGNABLEDETECTORTPC_H @@ -24,23 +23,47 @@ namespace o2 namespace align { -class AlignableDetectorTPC : public AlignableDetector +class AlignableDetectorTPC final : public AlignableDetector { public: - AlignableDetectorTPC(const char* title = ""); - virtual ~AlignableDetectorTPC(); // - virtual void defineVolumes(); - // - bool AcceptTrack(const AliESDtrack* trc, int trtype) const; + AlignableDetectorTPC() = default; + AlignableDetectorTPC(Controller* ctr); + ~AlignableDetectorTPC() final = default; + void defineVolumes() final; + void Print(const Option_t* opt = "") const final; // + int processPoints(GIndex gid, int npntCut, bool inv) final; + + void setTrackTimeStamp(float t) { mTrackTimeStamp = t; } + float getTrackTimeStamp() const { return mTrackTimeStamp; } + + int getStack(int padrow) const + { + for (int i = 0; i < 4; i++) { + if (padrow <= mStackMinMaxRow[i].second) { + return i; + } + } + return -1; + } + + int getDistanceToStackEdge(int padrow) const + { + // distance to the stack min or max padrow + auto st = getStack(padrow); + if (st < 0) { + return -999; + } + return std::min(padrow - mStackMinMaxRow[st].first, mStackMinMaxRow[st].second - padrow); + } + protected: // - // -------- dummies -------- - AlignableDetectorTPC(const AlignableDetectorTPC&); - AlignableDetectorTPC& operator=(const AlignableDetectorTPC&); - // - protected: + float mTrackTimeStamp = 0.f; // use track timestamp in \mus + static constexpr int NSTACKS = 4; + const std::array, NSTACKS> mStackMinMaxRow = {std::pair{0, 62}, std::pair{63, 96}, std::pair{97, 126}, std::pair{127, 151}}; + ClassDef(AlignableDetectorTPC, 1); }; } // namespace align diff --git a/Detectors/Align/include/Align/AlignableDetectorTRD.h b/Detectors/Align/include/Align/AlignableDetectorTRD.h index 7d7a59351c767..4e7577b11055c 100644 --- a/Detectors/Align/include/Align/AlignableDetectorTRD.h +++ b/Detectors/Align/include/Align/AlignableDetectorTRD.h @@ -18,14 +18,14 @@ #define ALIGNABLEDETECTORTRD_H #include "Align/AlignableDetector.h" -#include "TRDBase/RecoParam.h" +#include "GPUTRDRecoParam.h" namespace o2 { namespace align { -class AlignableDetectorTRD : public AlignableDetector +class AlignableDetectorTRD final : public AlignableDetector { public: // @@ -61,10 +61,10 @@ class AlignableDetectorTRD : public AlignableDetector mExtraErrRC[1] = z; } - int processPoints(GIndex gid, bool inv) final; + int processPoints(GIndex gid, int npntCut, bool inv) final; protected: - o2::trd::RecoParam mRecoParam; // parameters required for TRD reconstruction + o2::gpu::GPUTRDRecoParam mRecoParam; // parameters required for TRD reconstruction double mNonRCCorrDzDtgl = 0.; // correction in Z for non-crossing tracklets double mCorrDVT = 0.; // correction to Vdrift*t double mExtraErrRC[2] = {0., 0.}; // extra errors for RC tracklets diff --git a/Detectors/Align/include/Align/AlignableSensorHMPID.h b/Detectors/Align/include/Align/AlignableSensorHMPID.h index 6064078d1b923..867b72152c73c 100644 --- a/Detectors/Align/include/Align/AlignableSensorHMPID.h +++ b/Detectors/Align/include/Align/AlignableSensorHMPID.h @@ -29,7 +29,7 @@ namespace o2 namespace align { -class AlignableSensorHMPID : public AlignableSensor +class AlignableSensorHMPID final : public AlignableSensor { public: AlignableSensorHMPID(const char* name = 0, int vid = 0, int iid = 0, int isec = 0); diff --git a/Detectors/Align/include/Align/AlignableSensorITS.h b/Detectors/Align/include/Align/AlignableSensorITS.h index de0e6d35629ec..7769eb6aa22f3 100644 --- a/Detectors/Align/include/Align/AlignableSensorITS.h +++ b/Detectors/Align/include/Align/AlignableSensorITS.h @@ -29,13 +29,15 @@ namespace o2 namespace align { -class AlignableSensorITS : public AlignableSensor +class AlignableSensorITS final : public AlignableSensor { public: AlignableSensorITS() = default; AlignableSensorITS(const char* name, int vid, int iid, Controller* ctr); ~AlignableSensorITS() final = default; void prepareMatrixT2L() final; + void prepareMatrixL2G(bool reco = false) final; + void prepareMatrixL2GIdeal() final; // protected: // diff --git a/Detectors/Align/include/Align/AlignableSensorTOF.h b/Detectors/Align/include/Align/AlignableSensorTOF.h index 745251d7dc6f2..d9e11dce98a0c 100644 --- a/Detectors/Align/include/Align/AlignableSensorTOF.h +++ b/Detectors/Align/include/Align/AlignableSensorTOF.h @@ -29,7 +29,7 @@ namespace o2 namespace align { -class AlignableSensorTOF : public AlignableSensor +class AlignableSensorTOF final : public AlignableSensor { public: AlignableSensorTOF() = default; diff --git a/Detectors/Align/include/Align/AlignableSensorTPC.h b/Detectors/Align/include/Align/AlignableSensorTPC.h index ee1b0caf5ff30..838e8c8272400 100644 --- a/Detectors/Align/include/Align/AlignableSensorTPC.h +++ b/Detectors/Align/include/Align/AlignableSensorTPC.h @@ -10,39 +10,35 @@ // or submit itself to any jurisdiction. /// @file AlignableSensorTPC.h -/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch -/// @since 2021-02-01 -/// @brief TPC sensor (chamber) +/// @author ruben.shahoyan@cern.ch +/// @brief TPC fake sensor (sector) #ifndef ALIGNABLESENSORTPC_H #define ALIGNABLESENSORTPC_H #include "Align/AlignableSensor.h" - -class TObjArray; -//class AliTrackPointArray; -//class AliESDtrack; class AlignmentPoint; +class TObjArray; namespace o2 { namespace align { -class AlignableSensorTPC : public AlignableSensor +class AlignableSensorTPC final : public AlignableSensor { public: - AlignableSensorTPC(const char* name = 0, int vid = 0, int iid = 0, int isec = 0); - ~AlignableSensorTPC() = final; - // - int GetSector() const { return fSector; } - void SetSector(uint32_t sc) { fSector = (uint8_t)sc; } - // + AlignableSensorTPC() = default; + AlignableSensorTPC(const char* name, int vid, int iid, int isec, Controller* ctr); + ~AlignableSensorTPC() final = default; + int getSector() const { return mSector; } + void setSector(int sc) { mSector = (uint8_t)sc; } void prepareMatrixT2L() final; - // + void prepareMatrixL2G(bool reco = false) final; + void prepareMatrixL2GIdeal() final; + protected: - // - uint8_t fSector; // sector ID + uint8_t mSector = 0; // sector ID ClassDef(AlignableSensorTPC, 1) }; diff --git a/Detectors/Align/include/Align/AlignableSensorTRD.h b/Detectors/Align/include/Align/AlignableSensorTRD.h index 65792d597cb3a..eabc3905e9e13 100644 --- a/Detectors/Align/include/Align/AlignableSensorTRD.h +++ b/Detectors/Align/include/Align/AlignableSensorTRD.h @@ -26,7 +26,7 @@ namespace o2 namespace align { -class AlignableSensorTRD : public AlignableSensor +class AlignableSensorTRD final : public AlignableSensor { public: AlignableSensorTRD() = default; @@ -35,7 +35,12 @@ class AlignableSensorTRD : public AlignableSensor int getSector() const { return mSector; } void setSector(int sc) { mSector = (uint8_t)sc; } void dPosTraDParCalib(const AlignmentPoint* pnt, double* deriv, int calibID, const AlignableVolume* parent = nullptr) const final; + + void prepareMatrixL2G(bool reco = false) final; + void prepareMatrixL2GIdeal() final; void prepareMatrixT2L() final; + void prepareMatrixClAlg() final; + void prepareMatrixClAlgReco() final; protected: uint8_t mSector = 0; // sector ID diff --git a/Detectors/Align/include/Align/AlignableVolume.h b/Detectors/Align/include/Align/AlignableVolume.h index 7221f3a3cb587..15b077eb2700f 100644 --- a/Detectors/Align/include/Align/AlignableVolume.h +++ b/Detectors/Align/include/Align/AlignableVolume.h @@ -182,8 +182,8 @@ class AlignableVolume : public DOFSet bool createLocDeltaMatrix(TGeoHMatrix& deltaM) const; // return true if the matrix is not unity void createPreGloDeltaMatrix(TGeoHMatrix& deltaM) const; void createPreLocDeltaMatrix(TGeoHMatrix& deltaM) const; - void createAlignmenMatrix(TGeoHMatrix& alg) const; - void createAlignmentObjects(std::vector& arr) const; + void createAlignmenMatrix(TGeoHMatrix& alg, const TGeoHMatrix* envelopeDelta = nullptr) const; + void createAlignmentObjects(std::vector& arr, const TGeoHMatrix* envelopeDelta = nullptr) const; // void setSkip(bool v = true) { SetBit(kSkipBit, v); } bool getSkip() const { return TestBit(kSkipBit); } @@ -197,6 +197,9 @@ class AlignableVolume : public DOFSet bool ownsDOFID(int id) const; AlignableVolume* getVolOfDOFID(int id) const; // + bool isDummyEnvelope() const { return mIsDummyEnvelope; } + void setDummyEnvelope(bool v = true) { mIsDummyEnvelope = v; } + // bool isDummy() const { return mIsDummy; } void setDummy(bool v) { mIsDummy = v; } // @@ -228,6 +231,8 @@ class AlignableVolume : public DOFSet uint32_t mDOF = 0; // pattern of DOFs uint32_t mDOFAsMeas = 0; // consider DOF as measured with presigma error bool mIsDummy = false; // placeholder (e.g. inactive), used to have the numbering corresponding to position in the container + bool mIsDummyEnvelope = false; // some volumes are dummy envelopes for their children + // char mNDOFGeomFree = 0; // number of free geom degrees of freedom uint8_t mConstrChild = 0; // bitpattern for constraints on children corrections // diff --git a/Detectors/Align/include/Align/AlignmentTrack.h b/Detectors/Align/include/Align/AlignmentTrack.h index d4d5fbde5c7c1..cb69f11cbf85c 100644 --- a/Detectors/Align/include/Align/AlignmentTrack.h +++ b/Detectors/Align/include/Align/AlignmentTrack.h @@ -24,6 +24,7 @@ #define ALIGNMENTTRACK_H #include "Align/AlignmentPoint.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" #include "ReconstructionDataFormats/Track.h" #include #include @@ -38,8 +39,10 @@ class AlignmentTrack : public trackParam_t, public TObject { public: using trackParam_t = o2::track::TrackParametrizationWithError; + using trackPar_t = o2::track::TrackParametrization; using PropagatorD = o2::base::PropagatorD; using MatCorrType = PropagatorD::MatCorrType; + using GTrackID = o2::dataformats::GlobalTrackID; using trackParam_t::setParam; static constexpr double MaxDefStep = 3.0; static constexpr double MaxDefSnp = 0.95; @@ -59,6 +62,8 @@ class AlignmentTrack : public trackParam_t, public TObject }; AlignmentTrack() = default; ~AlignmentTrack() override = default; + void setCurrentTrackID(GTrackID id) { mCurrenTrackID = id; } + GTrackID getCurrentTrackID() const { return mCurrenTrackID; } void defineDOFs(); int getNPoints() const { return mPoints.size(); } int getInnerPointID() const { return mInnerPointID; } @@ -79,9 +84,9 @@ class AlignmentTrack : public trackParam_t, public TObject // template void copyFrom(const o2::track::TrackParametrizationWithError

& trc); - bool propagateToPoint(trackParam_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tLT = nullptr, int signCorr = 0); - bool propagateParamToPoint(trackParam_t& tr, const AlignmentPoint* pnt, double maxStep = 3, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, int signCorr = 0); // param only - bool propagateParamToPoint(trackParam_t* trSet, int nTr, const AlignmentPoint* pnt, double maxStep = 3, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, int signCorr = 0); // params only + bool propagateToPoint(trackParam_t& tr, trackPar_t* linRef, const AlignmentPoint* pnt, double maxStep, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tLT = nullptr, int signCorr = 0); + bool propagateParamToPoint(trackPar_t& tr, const AlignmentPoint* pnt, double maxStep = 3, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, int signCorr = 0); // param only + bool propagateParamToPoint(trackPar_t* trSet, int nTr, const AlignmentPoint* pnt, double maxStep = 3, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, int signCorr = 0); // params only // bool calcResiduals(const double* params = nullptr); bool calcResidDeriv(double* params = nullptr); @@ -115,23 +120,23 @@ class AlignmentTrack : public trackParam_t, public TObject void imposePtBOff(double pt) { setQ2Pt(1. / pt); } // propagation methods void copyFrom(const trackParam_t* etp); - bool applyMatCorr(trackParam_t& trPar, const double* corrDiag, const AlignmentPoint* pnt); - bool applyMatCorr(trackParam_t* trSet, int ntr, const double* corrDiaf, const AlignmentPoint* pnt); - bool applyMatCorr(trackParam_t& trPar, const double* corrpar); + bool applyMatCorr(trackPar_t& trPar, const double* corrDiag, const AlignmentPoint* pnt); + bool applyMatCorr(trackPar_t* trSet, int ntr, const double* corrDiaf, const AlignmentPoint* pnt); + bool applyMatCorr(trackPar_t& trPar, const double* corrpar); // double getResidual(int dim, int pntID) const { return mResid[dim][pntID]; } const double* getDResDLoc(int dim, int pntID) const { return mDResDLoc[dim].data() + (pntID * mNLocPar); } const double* getDResDGlo(int dim, int id) const { return mDResDGlo[dim].data() + id; } const int* getGloParID() const { return mGloParID.data(); } // - void setParams(trackParam_t& tr, double x, double alp, const double* par, bool add); - void setParams(trackParam_t* trSet, int ntr, double x, double alp, const double* par, bool add); - void setParam(trackParam_t& tr, int par, double val); - void setParam(trackParam_t* trSet, int ntr, int par, double val); - void modParam(trackParam_t& tr, int par, double delta); - void modParam(trackParam_t* trSet, int ntr, int par, double delta); + void setParams(trackPar_t& tr, double x, double alp, const double* par, bool add); + void setParams(trackPar_t* trSet, int ntr, double x, double alp, const double* par, bool add); + void setParam(trackPar_t& tr, int par, double val); + void setParam(trackPar_t* trSet, int ntr, int par, double val); + void modParam(trackPar_t& tr, int par, double delta); + void modParam(trackPar_t* trSet, int ntr, int par, double delta); // - void richardsonDeriv(const trackParam_t* trSet, const double* delta, + void richardsonDeriv(const trackPar_t* trSet, const double* delta, const AlignmentPoint* pnt, double& derY, double& derZ); // const double* getLocPars() const { return mLocPar.data(); } @@ -166,6 +171,7 @@ class AlignmentTrack : public trackParam_t, public TObject double mChi2CosmUp = 0; // chi2 for cosmic upper leg double mChi2CosmDn = 0; // chi2 for cosmic down leg double mChi2Ini = 0; // chi2 with current residuals + GTrackID mCurrenTrackID = {}; // currently processed track ID std::vector mPoints{}; // alignment points pointers sorted in X std::vector mDetPoints{}; // alignment points added by detectors std::vector mResid[2]; // residuals array @@ -174,13 +180,14 @@ class AlignmentTrack : public trackParam_t, public TObject std::vector mLocPar; // local parameters array std::vector mGloParID; // IDs of relevant global params private: - bool propagate(trackParam_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr = 0); + bool propagate(trackParam_t& tr, trackPar_t* linRef, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr = 0); + bool propagate(trackPar_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr = 0); // ClassDefOverride(AlignmentTrack, 2) }; //____________________________________________________________________________________________ -inline void AlignmentTrack::setParams(trackParam_t& tr, double x, double alp, const double* par, bool add) +inline void AlignmentTrack::setParams(trackPar_t& tr, double x, double alp, const double* par, bool add) { // set track params const double kDefQ2PtCosm = 1; @@ -200,7 +207,7 @@ inline void AlignmentTrack::setParams(trackParam_t& tr, double x, double alp, co } //____________________________________________________________________________________________ -inline void AlignmentTrack::setParams(trackParam_t* trSet, int ntr, double x, double alp, const double* par, bool add) +inline void AlignmentTrack::setParams(trackPar_t* trSet, int ntr, double x, double alp, const double* par, bool add) { // set parames for multiple tracks (VECTORIZE THIS) if (!add) { // full parameter supplied @@ -219,14 +226,14 @@ inline void AlignmentTrack::setParams(trackParam_t* trSet, int ntr, double x, do } //____________________________________________________________________________________________ -inline void AlignmentTrack::setParam(trackParam_t& tr, int par, double val) +inline void AlignmentTrack::setParam(trackPar_t& tr, int par, double val) { // set track parameter tr.setParam(val, par); } //____________________________________________________________________________________________ -inline void AlignmentTrack::setParam(trackParam_t* trSet, int ntr, int par, double val) +inline void AlignmentTrack::setParam(trackPar_t* trSet, int ntr, int par, double val) { // set parames for multiple tracks (VECTORIZE THIS) for (int i = 0; i < ntr; ++i) { @@ -235,7 +242,7 @@ inline void AlignmentTrack::setParam(trackParam_t* trSet, int ntr, int par, doub } //____________________________________________________________________________________________ -inline void AlignmentTrack::modParam(trackParam_t& tr, int par, double delta) +inline void AlignmentTrack::modParam(trackPar_t& tr, int par, double delta) { // modify track parameter const auto val = tr.getParam(par) + delta; @@ -243,7 +250,7 @@ inline void AlignmentTrack::modParam(trackParam_t& tr, int par, double delta) } //____________________________________________________________________________________________ -inline void AlignmentTrack::modParam(trackParam_t* trSet, int ntr, int par, double delta) +inline void AlignmentTrack::modParam(trackPar_t* trSet, int ntr, int par, double delta) { // modify track parameter (VECTORIZE THOS) for (int i = 0; i < ntr; ++i) { diff --git a/Detectors/Align/include/Align/Controller.h b/Detectors/Align/include/Align/Controller.h index e7fc38329a62c..a9c4eac734535 100644 --- a/Detectors/Align/include/Align/Controller.h +++ b/Detectors/Align/include/Align/Controller.h @@ -26,8 +26,11 @@ #include "DetectorsBase/GeometryManager.h" #include "DetectorsBase/Propagator.h" #include "Align/AlignmentTrack.h" +#include "Align/AlgTrcDbg.h" #include "ReconstructionDataFormats/PrimaryVertex.h" #include "ReconstructionDataFormats/TrackCosmics.h" +#include "DataFormatsTPC/VDriftCorrFact.h" +#include "TPCFastTransformPOD.h" #include "Align/Millepede2Record.h" #include "Align/ResidualsController.h" @@ -51,6 +54,8 @@ #include #include #include "Align/Mille.h" +// #include "GPUO2ExternalUser.h" +// #include "DataFormatsTPC/WorkflowHelper.h" namespace o2 { @@ -67,6 +72,11 @@ namespace utils class TreeStreamRedirector; } +namespace gpu +{ +class GPUParam; +} + namespace align { @@ -77,7 +87,7 @@ class AlignableVolume; class AlignmentPoint; class ResidualsControllerFast; -class Controller : public TObject +class Controller final : public TObject { public: struct ProcStat { @@ -214,7 +224,8 @@ class Controller : public TObject void initMPRecOutput(); void initMIlleOutput(); void initResidOutput(); - bool storeProcessedTrack(o2::dataformats::GlobalTrackID tid); + bool storeProcessedTrack(o2::dataformats::GlobalTrackID tid = {}); + void extractDbgTrack(); void printStatistics() const; // void genPedeSteerFile(const Option_t* opt = "") const; @@ -260,12 +271,21 @@ class Controller : public TObject bool getTRDTrigRecFilterActive() const { return mTRDTrigRecFilterActive; } bool getAllowAfterburnerTracks() const { return mAllowAfterburnerTracks; } + void setTPCVDrift(const o2::tpc::VDriftCorrFact& v); + void setTPCCorrMaps(const o2::gpu::TPCFastTransformPOD* maph); + const o2::gpu::TPCFastTransformPOD* getTPCCorrMaps() { return mTPCCorrMaps; } + const o2::tpc::VDriftCorrFact& getTPCVDrift() const { return mTPCDrift; } + int getInstanceID() const { return mInstanceID; } void setInstanceID(int i) { mInstanceID = i; } int getDebugOutputLevel() const { return mDebugOutputLevel; } void setDebugOutputLevel(int i) { mDebugOutputLevel = i; } void setDebugStream(o2::utils::TreeStreamRedirector* d) { mDBGOut = d; } + o2::utils::TreeStreamRedirector* getDebugStream() { return mDBGOut; } + + void setTPCParam(const o2::gpu::GPUParam* par) { mTPCParam = par; } + const o2::gpu::GPUParam* getTPCParam() const { return mTPCParam; } protected: // @@ -289,10 +309,14 @@ class Controller : public TObject float mMPRecOutFraction = 0.; float mControlFraction = 0.; std::unique_ptr mAlgTrack; // current alignment track + AlgTrcDbg mAlgTrackDbg; // current alignment track debug version const o2::globaltracking::RecoContainer* mRecoData = nullptr; // externally set RecoContainer const o2::trd::TrackletTransformer* mTRDTransformer = nullptr; // TRD tracket transformer bool mTRDTrigRecFilterActive = false; // select TRD triggers processed with ITS bool mAllowAfterburnerTracks = false; // allow using ITS-TPC afterburner tracks + + const o2::gpu::GPUParam* mTPCParam = nullptr; + std::array, DetID::nDetectors> mDetectors{}; // detectors participating in the alignment std::unique_ptr mVtxSens; // fake sensor for the vertex @@ -333,6 +357,10 @@ class Controller : public TObject int mRefRunNumber = 0; // optional run number used for reference int mRefOCDBLoaded = 0; // flag/counter for ref.OCDB loading bool mUseRecoOCDB = true; // flag to preload reco-time calib objects + + o2::tpc::VDriftCorrFact mTPCDrift{}; + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps = nullptr; + // static const int sSkipLayers[kNLrSkip]; // detector layers for which we don't need module matrices static const Char_t* sDetectorName[kNDetectors]; // names of detectors //RSREM diff --git a/Detectors/Align/include/Align/ResidualsControllerFast.h b/Detectors/Align/include/Align/ResidualsControllerFast.h index 171bfb45757d2..5a2f947db9cca 100644 --- a/Detectors/Align/include/Align/ResidualsControllerFast.h +++ b/Detectors/Align/include/Align/ResidualsControllerFast.h @@ -24,7 +24,7 @@ namespace o2 namespace align { -class ResidualsControllerFast : public TObject +class ResidualsControllerFast final : public TObject { public: enum { kCosmicBit = BIT(14), diff --git a/Detectors/Align/macro/CMakeLists.txt b/Detectors/Align/macro/CMakeLists.txt index 49106e647c412..54c8715b074b2 100644 --- a/Detectors/Align/macro/CMakeLists.txt +++ b/Detectors/Align/macro/CMakeLists.txt @@ -10,6 +10,7 @@ # or submit itself to any jurisdiction. install(FILES algconf.C + algDump.C MPRec2Mille.C DESTINATION share/Detectors/Align/macro) @@ -17,6 +18,10 @@ o2_add_test_root_macro(algconf.C PUBLIC_LINK_LIBRARIES O2::Align LABELS align COMPILE_ONLY) +o2_add_test_root_macro(algDump.C + PUBLIC_LINK_LIBRARIES O2::Align + LABELS align COMPILE_ONLY) + o2_add_test_root_macro(MPRec2Mille.C PUBLIC_LINK_LIBRARIES O2::Align LABELS align COMPILE_ONLY) diff --git a/Detectors/Align/macro/algDump.C b/Detectors/Align/macro/algDump.C new file mode 100644 index 0000000000000..2d1dfcbaaa0de --- /dev/null +++ b/Detectors/Align/macro/algDump.C @@ -0,0 +1,93 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "DetectorsBase/GeometryManager.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsTRD/Constants.h" +#include "TRDBase/Geometry.h" +#include "TOFBase/Geo.h" +#include +#endif + +using DetID = o2::detectors::DetID; + +void algDump(const std::string& geom = "", const std::string& outname = "algdump.root") +{ + o2::base::GeometryManager::loadGeometry(geom.c_str()); + o2::utils::TreeStreamRedirector outstream(outname.c_str(), "recreate"); + TGeoHMatrix* matAlg = nullptr; + TGeoHMatrix matOrig; + TVector3 pos0, pos; + DetID det; + + auto procSens = [](TGeoHMatrix& mat) { + double loc[3] = {0., 0., 0.}, glo[3]; + mat.LocalToMaster(loc, glo); + return TVector3(glo[0], glo[1], glo[2]); + }; + + auto store = [&outstream, &det, &pos, &pos0](int lr, int sid, int sidLr) { + outstream << "gm" + << "det=" << det.getID() << "lr=" << lr << "sid=" << sid << "sidlr=" << sidLr << "pos0=" << pos0 << "pos=" << pos << "\n"; + printf("xx %d %d %d %f %f %f\n", det.getID(), lr, sid, pos0[0], pos0[1], pos0[1]); + }; + + det = DetID("ITS"); + o2::itsmft::ChipMappingITS mpits; + for (int ic = 0; ic < mpits.getNChips(); ic++) { + int lr = mpits.getLayer(ic); + int ic0 = ic - mpits.getFirstChipsOnLayer(lr); + matAlg = o2::base::GeometryManager::getMatrix(det, ic); + o2::base::GeometryManager::getOriginalMatrix(det, ic, matOrig); + pos0 = procSens(matOrig); + pos = procSens(*matAlg); + store(lr, ic, ic0); + } + + det = DetID("TRD"); + for (int ilr = 0; ilr < o2::trd::constants::NLAYER; ilr++) { // layer + for (int ich = 0; ich < o2::trd::constants::NSTACK * o2::trd::constants::NSECTOR; ich++) { // chamber + int isector = ich / o2::trd::constants::NSTACK; + int istack = ich % o2::trd::constants::NSTACK; + uint16_t sid = o2::trd::Geometry::getDetector(ilr, istack, isector); + const char* symname = Form("TRD/sm%02d/st%d/pl%d", isector, istack, ilr); + if (!gGeoManager->GetAlignableEntry(symname)) { + continue; + } + matAlg = o2::base::GeometryManager::getMatrix(det, sid); + o2::base::GeometryManager::getOriginalMatrix(det, sid, matOrig); + pos0 = procSens(matOrig); + pos = procSens(*matAlg); + store(ilr, sid, ich); + } + } + + det = DetID("TOF"); + int cnt = -1; + for (int isc = 0; isc < 18; isc++) { + for (int istr = 1; istr <= o2::tof::Geo::NSTRIPXSECTOR; istr++) { // strip + const char* symname = Form("TOF/sm%02d/strip%02d", isc, istr); + cnt++; + if (!gGeoManager->GetAlignableEntry(symname)) { + continue; + } + matAlg = o2::base::GeometryManager::getMatrix(det, cnt); + o2::base::GeometryManager::getOriginalMatrix(det, cnt, matOrig); + pos0 = procSens(matOrig); + pos = procSens(*matAlg); + store(0, cnt, cnt); + } + } + outstream.Close(); +} diff --git a/Detectors/Align/macro/algconf.C b/Detectors/Align/macro/algconf.C index 2e87a6c07edb3..190c8739eaa34 100644 --- a/Detectors/Align/macro/algconf.C +++ b/Detectors/Align/macro/algconf.C @@ -13,6 +13,7 @@ #include "Align/Controller.h" #include "Align/AlignableVolume.h" #include "Align/AlignableDetectorITS.h" +#include "Align/AlignableDetectorTPC.h" #include "Align/AlignableDetectorTRD.h" #include "Align/AlignableDetectorTOF.h" #endif @@ -21,6 +22,7 @@ using namespace o2::align; void configITS(Controller* c, int par); +void configTPC(Controller* c, int par); void configTRD(Controller* c, int par); void configTOF(Controller* c, int par); @@ -31,6 +33,9 @@ int algconf(Controller* c, int par) if (c->getDetector(o2::detectors::DetID::ITS)) { configITS(c, par); } + if (c->getDetector(o2::detectors::DetID::TPC)) { + configTPC(c, par); + } if (c->getDetector(o2::detectors::DetID::TRD)) { configTRD(c, par); } @@ -102,6 +107,25 @@ void configITS(Controller* c, int par) */ } +void configTPC(Controller* c, int par) +{ + const double kCondSig[AlignableVolume::kNDOFGeom] = {1., 1., 1., 1., 1., 1.}; // precondition sigmas + AlignableDetectorTPC* tpc = (AlignableDetectorTPC*)c->getDetector(o2::detectors::DetID::TPC); + if (!tpc) { + LOG(info) << "No TPC"; + return; + } + + tpc->setFreeDOFPattern(0, -1, "TPC/sec[0-9]+$"); // Fix sectors + auto tpcvol = tpc->getVolume("TPC_envelope"); // envelope + if (!tpcvol || !tpcvol->isDummyEnvelope()) { + LOG(fatal) << "Could not find TPC_envelope volume"; + } + for (int idf = AlignableVolume::kNDOFGeom; idf--;) { + tpcvol->setParErr(idf, kCondSig[idf]); // set condition + } +} + void configTRD(Controller* c, int par) { const double kCondSig[AlignableVolume::kNDOFGeom] = {0.2, 0.2, 0.3, 1., 1., 1.}; // precondition sigmas diff --git a/Detectors/Align/src/AlgPntDbg.cxx b/Detectors/Align/src/AlgPntDbg.cxx new file mode 100644 index 0000000000000..b52492b3a5cc4 --- /dev/null +++ b/Detectors/Align/src/AlgPntDbg.cxx @@ -0,0 +1,22 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file AlgPntDbg.h + +#include "Align/AlgPntDbg.h" + +namespace o2 +{ +namespace align +{ + +} // namespace align +} // namespace o2 diff --git a/Detectors/Align/src/AlgTrcDbg.cxx b/Detectors/Align/src/AlgTrcDbg.cxx new file mode 100644 index 0000000000000..157ca941449b7 --- /dev/null +++ b/Detectors/Align/src/AlgTrcDbg.cxx @@ -0,0 +1,23 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file AlgTrcDbg.h + +#include "Align/AlgTrcDbg.h" +#include "Framework/Logger.h" + +namespace o2 +{ +namespace align +{ + +} // namespace align +} // namespace o2 diff --git a/Detectors/Align/src/AlignLinkDef.h b/Detectors/Align/src/AlignLinkDef.h index 05c73c14520ca..66f3220d720ca 100644 --- a/Detectors/Align/src/AlignLinkDef.h +++ b/Detectors/Align/src/AlignLinkDef.h @@ -8,7 +8,6 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. - #ifdef __CLING__ #pragma link off all globals; @@ -19,17 +18,17 @@ #pragma link C++ class o2::align::AlignableDetector + ; #pragma link C++ class o2::align::AlignableDetectorITS + ; #pragma link C++ class o2::align::AlignableDetectorTOF + ; -//#pragma link C++ class o2::align::AlignableDetectorTPC + ; +#pragma link C++ class o2::align::AlignableDetectorTPC + ; #pragma link C++ class o2::align::AlignableDetectorTRD + ; -//#pragma link C++ class o2::align::AlignableDetectorHMPID + ; +// #pragma link C++ class o2::align::AlignableDetectorHMPID + ; #pragma link C++ class o2::align::Millepede2Record + ; #pragma link C++ class o2::align::AlignmentPoint + ; #pragma link C++ class o2::align::AlignableSensor + ; #pragma link C++ class o2::align::AlignableSensorITS + ; #pragma link C++ class o2::align::AlignableSensorTOF + ; -//#pragma link C++ class o2::align::AlignableSensorTPC + ; +#pragma link C++ class o2::align::AlignableSensorTPC + ; #pragma link C++ class o2::align::AlignableSensorTRD + ; -//#pragma link C++ class o2::align::AlignableSensorHMPID + ; +// #pragma link C++ class o2::align::AlignableSensorHMPID + ; #pragma link C++ class o2::align::Controller + ; #pragma link C++ class o2::align::AlignmentTrack + ; #pragma link C++ class o2::align::AlignableVolume + ; @@ -39,9 +38,16 @@ #pragma link C++ class o2::align::GeometricalConstraint + ; #pragma link C++ class o2::align::utils; +#pragma link C++ class o2::align::AlgPntDbg + ; +#pragma link C++ class o2::align::AlgTrcDbg + ; +#pragma link C++ class std::vector < o2::align::AlgPntDbg> + ; + #pragma link C++ class o2::align::AlignConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::align::AlignConfig> + ; #pragma link C++ class std::vector < o2::track::TrackParametrization < double>> + ; +#include "TVector3.h" +#pragma link C++ class std::vector < TVector3> + ; + #endif diff --git a/Detectors/Align/src/AlignableDetector.cxx b/Detectors/Align/src/AlignableDetector.cxx index ae1458db5a521..68e602c85251d 100644 --- a/Detectors/Align/src/AlignableDetector.cxx +++ b/Detectors/Align/src/AlignableDetector.cxx @@ -55,7 +55,7 @@ AlignableDetector::~AlignableDetector() } //____________________________________________ -int AlignableDetector::processPoints(GIndex gid, bool inv) +int AlignableDetector::processPoints(GIndex gid, int npntCut, bool inv) { // Create alignment points corresponding to this detector, recalibrate/realign them to the // level of the "starting point" for the alignment/calibration session. @@ -116,7 +116,7 @@ void AlignableDetector::updateL2GRecoMatrices() void AlignableDetector::reset() { // prepare for the next track processing - mNPoints = mFirstPoint = 0; + mNPoints = 0; } //_________________________________________________________ @@ -177,7 +177,7 @@ void AlignableDetector::defineMatrices() TIter next(&mVolumes); AlignableVolume* vol(nullptr); while ((vol = (AlignableVolume*)next())) { - if (vol->isDummy()) { + if (vol->isDummy() || vol->isDummyEnvelope()) { continue; } vol->prepareMatrixL2G(); // modified global-local matrix diff --git a/Detectors/Align/src/AlignableDetectorITS.cxx b/Detectors/Align/src/AlignableDetectorITS.cxx index 9d56d87e7da1e..7387ae4620bf3 100644 --- a/Detectors/Align/src/AlignableDetectorITS.cxx +++ b/Detectors/Align/src/AlignableDetectorITS.cxx @@ -40,6 +40,8 @@ namespace align AlignableDetectorITS::AlignableDetectorITS(Controller* ctr) : AlignableDetector(DetID::ITS, ctr) { // default c-tor + o2::itsmft::ChipMappingITS mp; + mOverlaps = mp.getOverlapsInfo(); } /* @@ -110,19 +112,18 @@ void AlignableDetectorITS::defineVolumes() } //____________________________________________ -int AlignableDetectorITS::processPoints(GIndex gid, bool inv) +int AlignableDetectorITS::processPoints(GIndex gid, int npntCut, bool inv) { // Extract the points corresponding to this detector, recalibrate/realign them to the // level of the "starting point" for the alignment/calibration session. // If inv==true, the track propagates in direction of decreasing tracking X // (i.e. upper leg of cosmic track) // - mNPoints = 0; auto algTrack = mController->getAlgTrack(); auto recoData = mController->getRecoContainer(); const auto& algConf = AlignConfig::Instance(); - - auto procClus = [this, &algTrack](const ClusterD& clus) { + int npoints = 0; + auto procClus = [this, inv, &npoints, &algTrack](const ClusterD& clus) { auto* sensor = this->getSensor(clus.getSensorID()); auto& pnt = algTrack->addDetectorPoint(); const auto* sysE = sensor->getAddError(); // additional syst error @@ -137,24 +138,45 @@ int AlignableDetectorITS::processPoints(GIndex gid, bool inv) pnt.setDetID(this->mDetID); pnt.setSID(sensor->getSID()); pnt.setContainsMeasurement(); + pnt.setInvDir(inv); pnt.init(); - mNPoints++; + npoints++; }; + std::array clusIDs{}; + int nOverlaps = 0; if (gid.getSource() == GIndex::ITS) { const auto tracks = recoData->getITSTracks(); if (tracks.empty()) { return -1; // source not loaded? } const auto& track = tracks[gid.getIndex()]; - if (track.getNClusters() < algConf.minITSClusters) { + if (track.getNClusters() < npntCut) { return -1; } const auto& clusIdx = recoData->getITSTracksClusterRefs(); // do we want to apply some cuts? int clEntry = track.getFirstClusterEntry(); - mFirstPoint = algTrack->getNPoints(); - for (int icl = track.getNumberOfClusters(); icl--;) { - const auto& clus = mITSClustersArray[clusIdx[clEntry++]]; + int preevSensID = -1; + bool errReported = false; + for (int icl = track.getNumberOfClusters(); icl--;) { // clusters refs are stored from outer to inner layers, we loop in inner -> outer direction + const auto& clus = mITSClustersArray[(clusIDs[npoints] = clusIdx[clEntry + icl])]; + if (clus.getSensorID() < preevSensID && !errReported) { // clusters are ordered from outer to inner layer, hence decreasing sensorID + std::string errstr{}; + for (int ie = track.getNumberOfClusters(); ie--;) { + errstr += fmt::format(" {}", mITSClustersArray[clusIdx[clEntry + ie]].getSensorID()); + } + LOGP(error, "wrong ITS clusters order? : chips {}", errstr); + errReported = true; + } + preevSensID = clus.getSensorID(); + if (clus.getBits()) { // overlapping clusters will have bit set + if (clus.isBitSet(EdgeFlags::Biased)) { + continue; + } + if (clus.getCount()) { + nOverlaps++; + } + } procClus(clus); } } else { // ITSAB @@ -163,42 +185,196 @@ int AlignableDetectorITS::processPoints(GIndex gid, bool inv) int nCl = trkITSABref.getNClusters(); int clEntry = trkITSABref.getFirstEntry(); for (int icl = 0; icl < nCl; icl++) { // clusters are stored from inner to outer layers - const auto& clus = mITSClustersArray[ABTrackClusIdx[clEntry + icl]]; + const auto& clus = mITSClustersArray[(clusIDs[npoints] = ABTrackClusIdx[clEntry + icl])]; + if (clus.getBits()) { // overlapping clusters will have bit set + if (clus.isBitSet(EdgeFlags::Biased)) { + continue; + } + if (clus.getCount()) { + nOverlaps++; + } + } procClus(clus); } } - return mNPoints; + if (npoints < npntCut) { // reset points to original start + algTrack->suppressLastPoints(npoints); + npoints = 0; + return 0; + } + + // do we need to process overlaps? + if (nOverlaps) { + auto trcProp = mController->getRecoContainer()->getTrackParam(mController->getAlgTrack()->getCurrentTrackID()); + trcProp.resetCovariance(10); + auto propagator = o2::base::Propagator::Instance(); + for (int icl = 0; icl < npoints; icl++) { + const auto& clus = mITSClustersArray[clusIDs[icl]]; + float alp = getSensor(clus.getSensorID())->getAlpTracking(); + if (!trcProp.rotate(alp) || + !propagator->propagateToX(trcProp, clus.getX(), propagator->getNominalBz(), algConf.maxSnp, algConf.maxStep, base::Propagator::MatCorrType(algConf.matCorType)) || + !trcProp.update(clus)) { + break; + } + + if (clus.getCount()) { // there is an overlap, find best matching cluster + nOverlaps--; + int bestClusID = -1, clusIDtoCheck = mOverlapClusRef[clusIDs[icl]]; + float bestChi2 = algConf.ITSOverlapMaxChi2; + auto trPropOvl = trcProp; + for (int iov = 0; iov < clus.getCount(); iov++) { + int clusOvlID = mOverlapCandidateID[clusIDtoCheck]; + const auto& clusOvl = mITSClustersArray[clusOvlID]; + if (iov == 0) { + if (!trPropOvl.rotate(getSensor(clusOvl.getSensorID())->getAlpTracking()) || + !propagator->propagateToX(trPropOvl, clusOvl.getX(), propagator->getNominalBz(), algConf.maxSnp, algConf.maxStep, base::Propagator::MatCorrType::USEMatCorrNONE)) { + break; + } + } + auto chi2 = trPropOvl.getPredictedChi2(clusOvl); + if (chi2 < bestChi2) { + bestChi2 = chi2; + bestClusID = clusOvlID; + } + } + if (bestClusID != -1) { // account overlapping cluster + procClus(mITSClustersArray[bestClusID]); + } + } + if (!nOverlaps) { + break; + } + } + } + mNPoints += npoints; + return npoints; } //____________________________________________ bool AlignableDetectorITS::prepareDetectorData() { // prepare TF data for processing: convert clusters + const auto& algConf = AlignConfig::Instance(); auto recoData = mController->getRecoContainer(); const auto clusITS = recoData->getITSClusters(); + const auto clusITSROF = recoData->getITSClustersROFRecords(); const auto patterns = recoData->getITSClustersPatterns(); auto pattIt = patterns.begin(); mITSClustersArray.clear(); mITSClustersArray.reserve(clusITS.size()); + if (algConf.ITSOverlapMargin > 0) { + mOverlapClusRef.clear(); + mOverlapClusRef.resize(clusITS.size(), -1); - for (auto& c : clusITS) { - auto* sensor = getSensor(c.getSensorID()); - double sigmaY2, sigmaZ2, sigmaYZ = 0, locXYZC[3], traXYZ[3]; - auto locXYZ = o2::its::ioutils::extractClusterDataA(c, pattIt, mITSDict, sigmaY2, sigmaZ2); // local ideal coordinates - const auto& matAlg = sensor->getMatrixClAlg(); // local alignment matrix !!! RS FIXME - matAlg.LocalToMaster(locXYZ.data(), locXYZC); // aligned point in the local frame - const auto& mat = sensor->getMatrixT2L(); // RS FIXME check if correct - mat.MasterToLocal(locXYZC, traXYZ); - /* - if (applyMisalignment) { - auto lrID = chmap.getLayer(c.getSensorID()); - sigmaY2 += conf.sysErrY2[lrID]; - sigmaZ2 += conf.sysErrZ2[lrID]; - } - */ - auto& cl3d = mITSClustersArray.emplace_back(c.getSensorID(), traXYZ[0], traXYZ[1], traXYZ[2], sigmaY2, sigmaZ2, sigmaYZ); // local --> tracking + mOverlapCandidateID.clear(); + mOverlapCandidateID.reserve(clusITS.size()); } + static std::vector edgeClusters; + int ROFCount = 0; + int16_t curSensID = -1; + struct ROFChipEntry { + int16_t rofCount = -1; + int chipFirstEntry = -1; + }; + std::array chipROFStart{}; // fill only for clusters with overlaps + + for (const auto& rof : clusITSROF) { + int maxic = rof.getFirstEntry() + rof.getNEntries(); + edgeClusters.clear(); + for (int ic = rof.getFirstEntry(); ic < maxic; ic++) { + const auto& c = clusITS[ic]; + int16_t sensID = c.getSensorID(); + auto* sensor = getSensor(sensID); + double sigmaY2, sigmaZ2, sigmaYZ = 0, locXYZC[3], traXYZ[3]; + auto pattItCopy = pattIt; + auto locXYZ = o2::its::ioutils::extractClusterDataA(c, pattIt, mITSDict, sigmaY2, sigmaZ2); // local ideal coordinates + const auto& matAlg = sensor->getMatrixClAlg(); // local alignment matrix !!! RS FIXME + matAlg.LocalToMaster(locXYZ.data(), locXYZC); // aligned point in the local frame + const auto& mat = sensor->getMatrixT2L(); // RS FIXME check if correct + mat.MasterToLocal(locXYZC, traXYZ); + auto& cl3d = mITSClustersArray.emplace_back(sensID, traXYZ[0], traXYZ[1], traXYZ[2], sigmaY2, sigmaZ2, sigmaYZ); // local --> tracking + + if (algConf.ITSOverlapMargin > 0) { + // fill chips overlaps info for clusters whose center is within of the algConf.ITSOverlapMargin distance from the chip min or max row edge + // but the pixel closest to this edge has distance of at least algConf.ITSOverlapEdgeRows from the edge + int row = 0, col = 0; + o2::itsmft::SegmentationAlpide::localToDetectorUnchecked(locXYZ[0], locXYZ[2], row, col); + int drow = row < o2::itsmft::SegmentationAlpide::NRows / 2 ? row : o2::itsmft::SegmentationAlpide::NRows - row - 1; // distance to the edge + if (drow * o2::itsmft::SegmentationAlpide::PitchRow < algConf.ITSOverlapMargin) { // rough check is passed, check if the edge cluster is indeed good + cl3d.setBit(row < o2::itsmft::SegmentationAlpide::NRows / 2 ? EdgeFlags::LowRow : EdgeFlags::HighRow); // flag that this is an edge cluster and indicate the low/high row side + // check if it is not too close to the edge (to be biased) + if (algConf.ITSOverlapEdgeRows > 0) { // is there a restriction? + auto pattID = c.getPatternID(); + drow = c.getRow(); + if (pattID != itsmft::CompCluster::InvalidPatternID) { + if (!mITSDict->isGroup(pattID)) { + const auto& patt = mITSDict->getPattern(pattID); // reference pixel is min row/col corner + if (row > o2::itsmft::SegmentationAlpide::NRows / 2) { + drow = o2::itsmft::SegmentationAlpide::NRows - 1 - (drow + patt.getRowSpan() - 1); + } + } else { // group: reference pixel is the one containing the COG + o2::itsmft::ClusterPattern patt(pattItCopy); + drow = row < o2::itsmft::SegmentationAlpide::NRows / 2 ? drow - patt.getRowSpan() / 2 : o2::itsmft::SegmentationAlpide::NRows - 1 - (drow + patt.getRowSpan() / 2 - 1); + } + } else { + o2::itsmft::ClusterPattern patt(pattItCopy); // reference pixel is min row/col corner + if (row > o2::itsmft::SegmentationAlpide::NRows / 2) { + drow = o2::itsmft::SegmentationAlpide::NRows - 1 - (drow + patt.getRowSpan() - 1); + } + } + if (drow < algConf.ITSOverlapEdgeRows) { // too close to the edge, flag this + cl3d.setBit(EdgeFlags::Biased); + } + } + if (!cl3d.isBitSet(EdgeFlags::Biased)) { + if (chipROFStart[sensID].rofCount != ROFCount) { // remember 1st entry + chipROFStart[sensID].rofCount = ROFCount; + chipROFStart[sensID].chipFirstEntry = edgeClusters.size(); // remember 1st entry of edge cluster for this chip + } + edgeClusters.push_back(ic); + } + } + } + } // clusters of ROF + // relate edge clusters of ROF to each other + int prevSensID = -1; + for (auto ic : edgeClusters) { + auto& cl = mITSClustersArray[ic]; + int sensID = cl.getSensorID(); + auto ovl = mOverlaps[sensID]; + int ovlCount = 0; + for (int ir = 0; ir < OVL::NSides; ir++) { + if (ovl.rowSide[ir] == OVL::NONE) { // no overlap from this row side + continue; + } + int chipOvl = ovl.rowSide[ir]; // look for overlaps with this chip + // are there clusters with overlaps on chipOvl? + if (chipROFStart[chipOvl].rofCount == ROFCount) { + auto oClusID = edgeClusters[chipROFStart[chipOvl].chipFirstEntry]; + while (oClusID < int(mITSClustersArray.size())) { + auto oClus = mITSClustersArray[oClusID]; + if (oClus.getSensorID() != sensID) { + break; // no more clusters on the overlapping chip + } + if (oClus.isBitSet(ovl.rowSideOverlap[ir]) && // make sure that the edge cluster is on the right side of the row + !oClus.isBitSet(EdgeFlags::Biased) && // not too close to the edge + std::abs(oClus.getZ() - cl.getZ()) < algConf.ITSOverlapMaxDZ) { // apply fiducial cut on Z distance of 2 clusters + // register overlaping cluster + if (!ovlCount) { // 1st overlap + mOverlapClusRef[ic] = mOverlapCandidateID.size(); + } + mOverlapCandidateID.push_back(oClusID); + ovlCount++; + } + oClusID++; + } + } + } + cl.setCount(std::min(127, ovlCount)); + } + ROFCount++; + } // loop over ROFs return true; } diff --git a/Detectors/Align/src/AlignableDetectorTOF.cxx b/Detectors/Align/src/AlignableDetectorTOF.cxx index a0ca5212594d4..9fa78e133a25d 100644 --- a/Detectors/Align/src/AlignableDetectorTOF.cxx +++ b/Detectors/Align/src/AlignableDetectorTOF.cxx @@ -47,6 +47,9 @@ void AlignableDetectorTOF::defineVolumes() // // AddVolume( volTOF = new AlignableVolume("TOF") ); // no main volume, why? AlignableVolume* sect[NSect] = {}; + AlignableVolume* volTOF = nullptr; // fictious envelope + addVolume(volTOF = new AlignableVolume("TOF_envelope", getDetLabel(), mController)); + volTOF->setDummyEnvelope(); // int cnt = 0; for (int isc = 0; isc < NSect; isc++) { @@ -60,6 +63,7 @@ void AlignableDetectorTOF::defineVolumes() } if (!sect[isc]) { sect[isc] = new AlignableVolume(Form("TOF/sm%02d", isc), getNonSensLabel(isc), mController); + sect[isc]->setParent(volTOF); } strip->setParent(sect[isc]); } // strip @@ -73,7 +77,7 @@ void AlignableDetectorTOF::defineVolumes() } //____________________________________________ -int AlignableDetectorTOF::processPoints(GIndex gid, bool inv) +int AlignableDetectorTOF::processPoints(GIndex gid, int npntCut, bool inv) { // Extract the points corresponding to this detector, recalibrate/realign them to the // level of the "starting point" for the alignment/calibration session. @@ -81,7 +85,7 @@ int AlignableDetectorTOF::processPoints(GIndex gid, bool inv) // (i.e. upper leg of cosmic track) // - mNPoints = 0; + int npoints = 0; auto algTrack = mController->getAlgTrack(); auto recoData = mController->getRecoContainer(); auto TOFClusters = recoData->getTOFClusters(); @@ -104,7 +108,6 @@ int AlignableDetectorTOF::processPoints(GIndex gid, bool inv) const auto& matT2L = sensor->getMatrixT2L(); matT2L.MasterToLocal(locCorr, traCorr); // - mFirstPoint = algTrack->getNPoints(); auto& pnt = algTrack->addDetectorPoint(); const auto* sysE = sensor->getAddError(); // additional syst error @@ -118,11 +121,12 @@ int AlignableDetectorTOF::processPoints(GIndex gid, bool inv) pnt.setXSens(sensor->getXTracking()); pnt.setDetID(mDetID); pnt.setSID(sensor->getSID()); - // pnt.setContainsMeasurement(); + pnt.setInvDir(inv); pnt.init(); - mNPoints++; - return mNPoints; + npoints++; + mNPoints += npoints; + return npoints; } } // namespace align diff --git a/Detectors/Align/src/AlignableDetectorTPC.cxx b/Detectors/Align/src/AlignableDetectorTPC.cxx index 63fac7dd9cac6..46473ce091c59 100644 --- a/Detectors/Align/src/AlignableDetectorTPC.cxx +++ b/Detectors/Align/src/AlignableDetectorTPC.cxx @@ -10,84 +10,255 @@ // or submit itself to any jurisdiction. /// @file AlignableDetectorTPC.h -/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch -/// @since 2021-02-01 +/// @author ruben.shahoyan@cern.ch /// @brief TPC detector wrapper #include "Align/AlignableDetectorTPC.h" #include "Align/AlignableVolume.h" #include "Align/AlignableSensorTPC.h" #include "Align/Controller.h" -//#include "AliGeomManager.h" -//#include "AliESDtrack.h" -#include "Framework/Logger.h" +#include "Align/AlignConfig.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsTPC/Constants.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsTPC/WorkflowHelper.h" +#include #include - -ClassImp(o2::align::AlignableDetectorTPC); +#include "GPUO2ExternalUser.h" +#include "DataFormatsTPC/WorkflowHelper.h" +#include "GPUParam.inc" namespace o2 { namespace align { +using namespace TMath; //____________________________________________ -AlignableDetectorTPC::AlignableDetectorTPC(const char* title) +AlignableDetectorTPC::AlignableDetectorTPC(Controller* ctr) : AlignableDetector(DetID::TPC, ctr) { // default c-tor - SetNameTitle(Controller::getDetNameByDetID(Controller::kTPC), title); - setDetID(Controller::kTPC); } //____________________________________________ -AlignableDetectorTPC::~AlignableDetectorTPC() +void AlignableDetectorTPC::defineVolumes() { - // d-tor + // define fictious TPC envelope and sector volumes + AlignableVolume* volTPC = nullptr; + int labDet = getDetLabel(); + const int NSectors = o2::tpc::constants::MAXSECTOR / 2; + + addVolume(volTPC = new AlignableVolume("TPC_envelope", getDetLabel(), mController)); + volTPC->setDummyEnvelope(); + + for (int isec = 0; isec < o2::tpc::constants::MAXSECTOR; isec++) { + int isecSide = isec % NSectors; + const char* symname = Form("TPC/sec%02d", isec); + AlignableSensorTPC* sector = new AlignableSensorTPC(symname, o2::base::GeometryManager::getSensID(mDetID, isec), getSensLabel(isec), isec, mController); + sector->setParent(volTPC); + addVolume(sector); + } } //____________________________________________ -void AlignableDetectorTPC::defineVolumes() +void AlignableDetectorTPC::Print(const Option_t* opt) const { - // define TPC volumes - // - const int kNSect = 18, kAC = 2, kIOROC = 2; - const char* kSide[kAC] = {"A", "C"}; - const char* kROC[kIOROC] = {"Inner", "Outer"}; - // AlignableSensorTPC *chamb=0; - // - int labDet = getDetLabel(); - AlignableVolume* volTPC = new AlignableVolume("ALIC_1/TPC_M_1", labDet); - addVolume(volTPC); + // print info + AlignableDetector::Print(opt); +} + +//____________________________________________ +int AlignableDetectorTPC::processPoints(GIndex gid, int npntCut, bool inv) +{ + // Extract the points corresponding to this detector, recalibrate/realign them to the + // level of the "starting point" for the alignment/calibration session. + // If inv==true, the track propagates in direction of decreasing tracking X + // (i.e. upper leg of cosmic track) // + const auto& algConf = AlignConfig::Instance(); + const auto recoData = mController->getRecoContainer(); + const auto& trk = recoData->getTrack(gid); + gsl::span TPCShMap = recoData->clusterShMapTPC; + + int nClus = trk.getNClusters(); + if (nClus < npntCut) { + return -1; + } + int npointsIni = mNPoints; + auto prop = o2::base::Propagator::Instance(); // float version! + constexpr float TAN10 = 0.17632698; + const auto clusterIdxStruct = recoData->getTPCTracksClusterRefs(); + const auto clusterNativeAccess = recoData->inputsTPCclusters->clusterIndex; + bool fail = false; + auto algTrack = mController->getAlgTrack(); + + o2::track::TrackParCov trkParam = inv ? trk : trk.getOuterParam(); // we refit outer param inward + trkParam.resetCovariance(); + float bzkG = prop->getNominalBz(), qptB5Scale = std::abs(bzkG) > 0.1 ? std::abs(bzkG) / 5.006680f : 1.f; + float q2pt2 = trkParam.getQ2Pt() * trkParam.getQ2Pt(), q2pt2Wgh = q2pt2 * qptB5Scale * qptB5Scale; + float err2 = (100.f + q2pt2Wgh) / (1.f + q2pt2Wgh) * q2pt2; // -> 100 for high pTs, -> 1 for low pTs. + trkParam.setCov(err2, 14); // 100% error + + int direction = inv ? -1 : 1; + int start = inv ? nClus - 1 : 0; + int stop = inv ? -1 : nClus; + const o2::tpc::ClusterNative* cl = nullptr; + uint8_t sector, row = 0, currentSector = 0, currentRow = 0; + short clusterState = 0, nextState; + float tOffset = mTrackTimeStamp / (o2::constants::lhc::LHCBunchSpacingMUS * 8); + bool stopLoop = false; + int npoints = 0; + for (int i = start; i != stop; i += cl ? 0 : direction) { + float x, y, z, xTmp, yTmp, zTmp, charge = 0.f; + int clusters = 0; + double combRow = 0; - for (int roc = 0; roc < kIOROC; roc++) { // inner/outer - for (int side = 0; side < kAC; side++) { // A/C - for (int isc = 0; isc < kNSect; isc++) { // sector ID - const char* symname = Form("TPC/Endcap%s/Sector%d/%sChamber", kSide[side], isc + 1, kROC[roc]); - if (!gGeoManager->GetAlignableEntry(symname)) { - AliErrorF("Did not find alignable %s", symname); + while (true) { + if (!cl) { + auto clTmp = &trk.getCluster(clusterIdxStruct, i, clusterNativeAccess, sector, row); + if (row < algConf.minTPCPadRow) { + if (!inv) { // inward refit: all other clusters padrow will be <= minumum (with outward refit the following clusters have a chance to be accepted) + stopLoop = true; + } + break; + } else if (row > algConf.maxTPCPadRow) { + if (inv) { // outward refit: all other clusters padrow will be >= maximum (with inward refit the following clusters have a chance to be accepted) + stopLoop = true; + } + break; + } + if (algConf.discardEdgePadrows > 0 && getDistanceToStackEdge(row) < algConf.discardEdgePadrows) { + if (i + direction != stop) { + i += direction; + continue; + } else { + stopLoop = true; + break; + } + } + mController->getTPCCorrMaps()->Transform(sector, row, clTmp->getPad(), clTmp->getTime(), xTmp, yTmp, zTmp, tOffset); + if (algConf.discardSectorEdgeDepth > 0) { + if (std::abs(yTmp) + algConf.discardSectorEdgeDepth > xTmp * TAN10) { + if (i + direction != stop) { + i += direction; + continue; + } else { + stopLoop = true; + break; + } + } + } + + cl = clTmp; + nextState = TPCShMap[cl - clusterNativeAccess.clustersLinear]; + } + if (clusters == 0 || (sector == currentSector && std::abs(row - currentRow) < algConf.maxTPCRowsCombined)) { + if (clusters == 1) { + x *= charge; + y *= charge; + z *= charge; + combRow *= charge; + } + if (clusters == 0) { + x = xTmp; + y = yTmp; + z = zTmp; + // mController->getTPCCorrMaps()->Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, tOffset); + currentRow = row; + currentSector = sector; + charge = cl->getQtot(); + clusterState = nextState; + combRow = row; + LOGP(debug, "starting a supercluster at row {} of sector {} -> {},{},{}", currentRow, currentSector, x, y, z); + } else { + // float xx, yy, zz; + // mController->getTPCCorrMaps()->Transform(sector, row, cl->getPad(), cl->getTime(), xx, yy, zz, tOffset); + x += xTmp * cl->getQtot(); + y += yTmp * cl->getQtot(); + z += zTmp * cl->getQtot(); + combRow += row * cl->getQtot(); + charge += cl->getQtot(); + clusterState |= nextState; + npntCut--; + LOGP(debug, "merging cluster #{} at row {} to a supercluster starting at row {} ", clusters + 1, row, currentRow); + } + cl = nullptr; + clusters++; + if (i + direction != stop) { + i += direction; continue; } - int iid = side * kNSect + isc; - uint16_t vid = AliGeomManager::LayerToVolUID(AliGeomManager::kTPC1 + roc, iid); - iid = labDet + (1 + side) * 10000 + (1 + isc) * 100 + (1 + roc); - AlignableSensorTPC* sens = new AlignableSensorTPC(symname, vid, iid, isc); - sens->setParent(volTPC); - addVolume(sens); - } // sector ID - } // A/C - } // inner/outer - // -} + } + break; + } + if (stopLoop) { + break; + } + if (clusters == 0) { + continue; + } else if (clusters > 1) { + x /= charge; + y /= charge; + z /= charge; + currentRow = combRow / charge; + LOGP(debug, "Combined cluster of {} subclusters: row {} , {},{},{}", clusters, currentRow, x, y, z); + } -//____________________________________________ -bool AlignableDetectorTPC::AcceptTrack(const AliESDtrack* trc, int trtype) const -{ - // test if detector had seed this track - if (!CheckFlags(trc, trtype)) - return false; - if (trc->GetNcls(1) < mNPointsSel[trtype]) - return false; - return true; + if (!trkParam.rotate(math_utils::detail::sector2Angle(currentSector)) || !prop->PropagateToXBxByBz(trkParam, x, algConf.maxSnp)) { + break; + } + if (!npoints) { + trkParam.setZ(z); + } + + auto* sectSensor = (AlignableSensorTPC*)getSensor(currentSector); + const auto* sysE = sectSensor->getAddError(); // additional syst error + + std::array p = {y, z}; + std::array c = {0, 0, 0}; + mController->getTPCParam()->GetClusterErrors2(sector, currentRow, z, trkParam.getSnp(), trkParam.getTgl(), -1.f, 0.f, 0.f, c[0], c[2]); // TODO: Note this disables occupancy / charge components of the error estimation + mController->getTPCParam()->UpdateClusterError2ByState(clusterState, c[0], c[2]); + int nrComb = std::abs(row - currentRow) + 1; + if (nrComb > 1) { + float fact = 1. / std::sqrt(nrComb); + c[0] *= fact; + c[2] *= fact; + } + if (sysE[0] > 0.f) { + c[0] += sysE[0] * sysE[0]; + } + if (sysE[1] > 0.f) { + c[2] += sysE[1] * sysE[1]; + } + + if (!trkParam.update(p, c)) { + break; + } + + auto& pnt = algTrack->addDetectorPoint(); + pnt.setYZErrTracking(c[0], c[1], c[2]); + if (getUseErrorParam()) { // errors will be calculated just before using the point in the fit, using track info + pnt.setNeedUpdateFromTrack(); + } + pnt.setXYZTracking(x, y, z); + pnt.setSensor(sectSensor); + pnt.setAlphaSens(sectSensor->getAlpTracking()); + pnt.setXSens(sectSensor->getXTracking()); + pnt.setDetID(mDetID); + pnt.setSID(sectSensor->getSID()); + pnt.setContainsMeasurement(); + pnt.setInvDir(inv); + pnt.init(); + npoints++; + } + if (npoints < npntCut) { + algTrack->suppressLastPoints(npoints); + mNPoints = npointsIni; + npoints = -1; + } + mNPoints += npoints; + + return npoints; } } // namespace align diff --git a/Detectors/Align/src/AlignableDetectorTRD.cxx b/Detectors/Align/src/AlignableDetectorTRD.cxx index bdf5b8b06a1b5..6fe8a60ef90f6 100644 --- a/Detectors/Align/src/AlignableDetectorTRD.cxx +++ b/Detectors/Align/src/AlignableDetectorTRD.cxx @@ -26,6 +26,7 @@ #include "DataFormatsTRD/TrackTRD.h" #include "DataFormatsTRD/Tracklet64.h" #include "DataFormatsTRD/CalibratedTracklet.h" +#include "GPUO2InterfaceConfiguration.h" #include #include @@ -51,8 +52,11 @@ void AlignableDetectorTRD::defineVolumes() geo->createClusterMatrixArray(); // ideal T2L matrices AlignableSensorTRD* chamb = nullptr; - int labDet = getDetLabel(); AlignableVolume* sect[o2::trd::constants::NSECTOR]{}; + AlignableVolume* volTRD = nullptr; // fictious envelope + + addVolume(volTRD = new AlignableVolume("TRD_envelope", getDetLabel(), mController)); + volTRD->setDummyEnvelope(); for (int ilr = 0; ilr < o2::trd::constants::NLAYER; ilr++) { // layer for (int ich = 0; ich < o2::trd::constants::NSTACK * o2::trd::constants::NSECTOR; ich++) { // chamber @@ -67,6 +71,7 @@ void AlignableDetectorTRD::defineVolumes() } if (!sect[isector]) { sect[isector] = new AlignableVolume(Form("TRD/sm%02d", isector), getNonSensLabel(isector), mController); + sect[isector]->setParent(volTRD); } chamb->setParent(sect[isector]); } // chamber @@ -157,7 +162,7 @@ double AlignableDetectorTRD::getCalibDOFValWithCal(int id) const } //____________________________________________ -int AlignableDetectorTRD::processPoints(GIndex gid, bool inv) +int AlignableDetectorTRD::processPoints(GIndex gid, int npntCut, bool inv) { // Extract the points corresponding to this detector, recalibrate/realign them to the // level of the "starting point" for the alignment/calibration session. @@ -167,22 +172,20 @@ int AlignableDetectorTRD::processPoints(GIndex gid, bool inv) const auto& algConf = AlignConfig::Instance(); const auto recoData = mController->getRecoContainer(); const auto& trk = recoData->getTrack(gid); - if (trk.getNtracklets() < algConf.minTRDTracklets) { + if (trk.getNtracklets() < npntCut) { return -1; } auto propagator = o2::base::Propagator::Instance(); // float version! - static float prevBz = -99999.; - if (prevBz != propagator->getNominalBz()) { - prevBz = propagator->getNominalBz(); - mRecoParam.setBfield(prevBz); + static bool firstCall = true; + if (firstCall) { + mRecoParam.init(propagator->getNominalBz()); + firstCall = false; } - mNPoints = 0; const auto* transformer = mController->getTRDTransformer(); auto algTrack = mController->getAlgTrack(); - mFirstPoint = algTrack->getNPoints(); const auto trackletsRaw = recoData->getTRDTracklets(); bool fail = false; - int nPntIni = algTrack->getNPoints(); + int nPntIni = algTrack->getNPoints(), npoints = 0; o2::track::TrackPar trkParam = trk.getOuterParam(); // we refit outer param inward to get tracklet coordinates accounting for tilt for (int il = o2::trd::constants::NLAYER; il--;) { if (trk.getTrackletIndex(il) == -1) { @@ -199,18 +202,10 @@ int AlignableDetectorTRD::processPoints(GIndex gid, bool inv) continue; } double locXYZ[3] = {trackletCalibLoc.getX(), trackletCalibLoc.getY(), trackletCalibLoc.getZ()}, locXYZC[3], traXYZ[3]; - ; const auto& matAlg = sensor->getMatrixClAlg(); // local alignment matrix matAlg.LocalToMaster(locXYZ, locXYZC); // aligned point in the local frame const auto& mat = sensor->getMatrixT2L(); // RS FIXME check if correct mat.MasterToLocal(locXYZC, traXYZ); - - // This is a hack until TRD T2L matrix problem will be solved - const auto trackletCalib = recoData->getTRDCalibratedTracklets()[trkltId]; - traXYZ[0] = trackletCalib.getX(); - traXYZ[1] = trackletCalib.getY(); - traXYZ[2] = trackletCalib.getZ(); - int trkltSec = sensor->getSector(); // trkltDet / (o2::trd::constants::NLAYER * o2::trd::constants::NSTACK); float alpSens = sensor->getAlpTracking(); // o2::math_utils::sector2Angle(trkltSec); if (trkltSec != o2::math_utils::angle2Sector(trkParam.getAlpha()) || @@ -252,14 +247,17 @@ int AlignableDetectorTRD::processPoints(GIndex gid, bool inv) pnt.setDetID(mDetID); pnt.setSID(sensor->getSID()); pnt.setContainsMeasurement(); + pnt.setInvDir(inv); pnt.init(); - mNPoints++; + npoints++; } if (fail) { // reset points to original start - algTrack->suppressLastPoints(mNPoints); - mNPoints = 0; + algTrack->suppressLastPoints(npoints); + npoints = 0; } - return mNPoints; + mNPoints += npoints; + + return npoints; } } // namespace align diff --git a/Detectors/Align/src/AlignableSensor.cxx b/Detectors/Align/src/AlignableSensor.cxx index 00b637efdd164..91658eba0a196 100644 --- a/Detectors/Align/src/AlignableSensor.cxx +++ b/Detectors/Align/src/AlignableSensor.cxx @@ -47,7 +47,7 @@ void AlignableSensor::dPosTraDParGeomLOC(const AlignmentPoint* pnt, double* deri // Jacobian of position in sensor tracking frame (tra) vs sensor LOCAL frame // parameters in TGeoHMatrix convention. // Result is stored in array deriv as linearized matrix 6x3 - const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5, 0.5, 0.5}; + const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5 * DegToRad(), 0.5 * DegToRad(), 0.5 * DegToRad()}; // changed angles to radians double delta[kNDOFGeom], pos0[3], pos1[3], pos2[3], pos3[3]; TGeoHMatrix matMod; // @@ -94,7 +94,7 @@ void AlignableSensor::dPosTraDParGeomLOC(const AlignmentPoint* pnt, double* deri // Jacobian of position in sensor tracking frame (tra) vs parent volume LOCAL frame parameters. // NO check of parentship is done! // Result is stored in array deriv as linearized matrix 6x3 - const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5, 0.5, 0.5}; + const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5 * DegToRad(), 0.5 * DegToRad(), 0.5 * DegToRad()}; // changed angles to radians double delta[kNDOFGeom], pos0[3], pos1[3], pos2[3], pos3[3]; TGeoHMatrix matMod; // this is the matrix for transition from sensor to parent volume local frames: LOC=matRel*loc @@ -193,7 +193,7 @@ void AlignableSensor::dPosTraDParGeomTRA(const AlignmentPoint* pnt, double* deri // tra' = tau*tra // // Result is stored in array deriv as linearized matrix 6x3 - const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5, 0.5, 0.5}; + const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5 * DegToRad(), 0.5 * DegToRad(), 0.5 * DegToRad()}; // changed angles to radians double delta[kNDOFGeom], pos0[3], pos1[3], pos2[3], pos3[3]; TGeoHMatrix matMod; // diff --git a/Detectors/Align/src/AlignableSensorITS.cxx b/Detectors/Align/src/AlignableSensorITS.cxx index d3fe5476ecbbc..b4480a25bc740 100644 --- a/Detectors/Align/src/AlignableSensorITS.cxx +++ b/Detectors/Align/src/AlignableSensorITS.cxx @@ -66,5 +66,40 @@ void AlignableSensorITS::prepareMatrixT2L() setMatrixT2L(t2l); } +void AlignableSensorITS::prepareMatrixL2G(bool reco) +{ + // Note that for ITS2 it is NOT the same as GeometryManager::getMatrix() (i.e. that of the alignable volule) + // since we need the matrix of epitaxial layer and not the whole chip + auto geom = o2::its::GeometryTGeo::Instance(); + const auto* m = geom->extractMatrixSensor(getVolID()); + if (!m) { + LOGP(fatal, "Failed on :GeometryTGeo::extractMatrixSensor({})", getVolID()); + } + reco ? setMatrixL2GReco(*m) : setMatrixL2G(*m); +} + +void AlignableSensorITS::prepareMatrixL2GIdeal() +{ + // Note that for ITS2 it is NOT the same as GeometryManager::getOriginalMatrix (i.e. that of the alignable volule) + // since we need the matrix of epitaxial layer and not the whole chip + auto geom = o2::its::GeometryTGeo::Instance(); + TGeoHMatrix mtmp; + if (!base::GeometryManager::getOriginalMatrix(getSymName(), mtmp)) { // this is chip ideal matrix, not that of the epitaxial layer + LOG(fatal) << "Failed to find ideal L2G matrix for " << getSymName(); + } + // we have to apply to it the difference between the aligner epitaxial layer matrix and that of the chip + const auto* malgSens = geom->extractMatrixSensor(getVolID()); + if (!malgSens) { + LOGP(fatal, "Failed on :GeometryTGeo::extractMatrixSensor({})", getVolID()); + } + const auto* malgChip = geom->getMatrix(getVolID()); + // correct chip original matrix by the difference between aligneg chip and sensor matrices + // Sens_ideal = Chip_ideal * Chip_aligned^-1 * Sens_aligned + auto chAlignInv = malgChip->Inverse(); + chAlignInv.Multiply(*malgSens); + mtmp.Multiply(chAlignInv); + setMatrixL2GIdeal(mtmp); +} + } // namespace align } // namespace o2 diff --git a/Detectors/Align/src/AlignableSensorTPC.cxx b/Detectors/Align/src/AlignableSensorTPC.cxx index 7180a44d2df30..87b3923633f77 100644 --- a/Detectors/Align/src/AlignableSensorTPC.cxx +++ b/Detectors/Align/src/AlignableSensorTPC.cxx @@ -10,62 +10,54 @@ // or submit itself to any jurisdiction. /// @file AlignableSensorTPC.h -/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch -/// @since 2021-02-01 -/// @brief TPC sensor (chamber) +/// @author ruben.shahoyan@cern.ch +/// @brief TPC sensor #include "Align/AlignableSensorTPC.h" +#include "Align/AlignableDetectorTPC.h" #include "Align/utils.h" #include "Framework/Logger.h" -//#include "AliTrackPointArray.h" -//#include "AliESDtrack.h" #include "Align/AlignmentPoint.h" -#include "Align/AlignableDetector.h" - -ClassImp(o2::align::AlignableSensorTPC) - - using namespace o2::align::utils; -using namespace TMath; namespace o2 { namespace align { +using namespace o2::align::utils; +using namespace TMath; //_________________________________________________________ -AlignableSensorTPC::AlignableSensorTPC(const char* name, int vid, int iid, int isec) - : AlignableSensor(name, vid, iid), fSector(isec) +AlignableSensorTPC::AlignableSensorTPC(const char* name, int vid, int iid, int isec, Controller* ctr) : AlignableSensor(name, vid, iid, ctr), mSector(isec) { // def c-tor } -//_________________________________________________________ -AlignableSensorTPC::~AlignableSensorTPC() +//____________________________________________ +void AlignableSensorTPC::prepareMatrixL2G(bool reco) +{ + double alp = math_utils::detail::sector2Angle(mSector % 18); + TGeoHMatrix m; + m.RotateZ(alp * RadToDeg()); + reco ? setMatrixL2GReco(m) : setMatrixL2G(m); +} + +//____________________________________________ +void AlignableSensorTPC::prepareMatrixL2GIdeal() { - // d-tor + double alp = math_utils::detail::sector2Angle(mSector % 18); + TGeoHMatrix m; + m.RotateZ(alp * RadToDeg()); + setMatrixL2GIdeal(m); } //____________________________________________ void AlignableSensorTPC::prepareMatrixT2L() { - // extract from geometry T2L matrix - double alp = math_utils::detail::sector2Angle(fSector); - double loc[3] = {0, 0, 0}, glo[3]; - getMatrixL2GIdeal().LocalToMaster(loc, glo); - double x = Sqrt(glo[0] * glo[0] + glo[1] * glo[1]); - TGeoHMatrix t2l; - t2l.SetDx(x); - t2l.RotateZ(alp * RadToDeg()); - const TGeoHMatrix& l2gi = getMatrixL2GIdeal().Inverse(); - t2l.MultiplyLeft(&l2gi); - /* - const TGeoHMatrix* t2l = AliGeomManager::GetTracking2LocalMatrix(getVolID()); - if (!t2l) { - Print("long"); - AliFatalF("Failed to find T2L matrix for VID:%d %s",getVolID(),getSymName()); - } - */ - setMatrixT2L(t2l); + // local and tracking matrices are the same + double alp = math_utils::detail::sector2Angle(mSector % 18); + mAlp = alp; + TGeoHMatrix m; + setMatrixT2L(m); // } diff --git a/Detectors/Align/src/AlignableSensorTRD.cxx b/Detectors/Align/src/AlignableSensorTRD.cxx index 2dc7d25ca5932..aa52567a36fdf 100644 --- a/Detectors/Align/src/AlignableSensorTRD.cxx +++ b/Detectors/Align/src/AlignableSensorTRD.cxx @@ -19,6 +19,7 @@ #include "Align/utils.h" #include "Framework/Logger.h" #include "Align/AlignmentPoint.h" +#include "DetectorsBase/GeometryManager.h" namespace o2 { @@ -33,22 +34,71 @@ AlignableSensorTRD::AlignableSensorTRD(const char* name, int vid, int iid, int i // def c-tor } +//____________________________________________ +void AlignableSensorTRD::prepareMatrixClAlg() +{ + // prepare alignment matrix in the pseudo-LOCAL frame of TRD (account that the chamber has extra X,Y rotations + TGeoHMatrix ma = getMatrixL2GIdeal().Inverse(); + ma *= getMatrixL2G(); + setMatrixClAlg(ma); + // +} + +//____________________________________________ +void AlignableSensorTRD::prepareMatrixClAlgReco() +{ + // prepare alignment matrix in the pseudo-LOCAL frame of TRD (account that the chamber has extra X,Y rotations + TGeoHMatrix ma = getMatrixL2GIdeal().Inverse(); + ma *= getMatrixL2G(); + setMatrixClAlgReco(ma); + // +} + +//____________________________________________ +void AlignableSensorTRD::prepareMatrixL2GIdeal() +{ + TGeoHMatrix Rxy; + Rxy.RotateX(-90); + Rxy.RotateY(-90); + TGeoHMatrix mtmp; + if (!base::GeometryManager::getOriginalMatrix(getSymName(), mtmp)) { + LOG(fatal) << "Failed to find ideal L2G matrix for " << getSymName(); + } + mtmp *= Rxy; + setMatrixL2GIdeal(mtmp); +} + +//____________________________________________ +void AlignableSensorTRD::prepareMatrixL2G(bool reco) +{ + TGeoHMatrix Rxy; + Rxy.RotateX(-90); + Rxy.RotateY(-90); + const char* path = getSymName(); + const TGeoHMatrix* l2g = nullptr; + if (!gGeoManager->GetAlignableEntry(path) || !(l2g = base::GeometryManager::getMatrix(path))) { + LOGP(fatal, "Failed to find L2G matrix for {}alignable {} -> {}", gGeoManager->GetAlignableEntry(path) ? "" : "non-", path, (void*)l2g); + } + TGeoHMatrix mtmp = *l2g; + mtmp *= Rxy; + reco ? setMatrixL2GReco(mtmp) : setMatrixL2G(mtmp); +} + //____________________________________________ void AlignableSensorTRD::prepareMatrixT2L() { // extract from geometry T2L matrix double alp = math_utils::detail::sector2Angle(mSector); mAlp = alp; - double loc[3] = {0, 0, 0}, glo[3]; - getMatrixL2GIdeal().LocalToMaster(loc, glo); - mX = Sqrt(glo[0] * glo[0] + glo[1] * glo[1]); - TGeoHMatrix t2l; - // t2l.SetDx(mX); // to remove when T2L will be clarified - t2l.RotateZ(alp * RadToDeg()); - const TGeoHMatrix l2gi = getMatrixL2GIdeal().Inverse(); - t2l.MultiplyLeft(&l2gi); - + TGeoHMatrix Rs; + Rs.RotateZ(-alp * TMath::RadToDeg()); + TGeoHMatrix m0 = getMatrixL2GIdeal(); + m0.MultiplyLeft(Rs); + TGeoHMatrix t2l = m0.Inverse(); setMatrixT2L(t2l); + double loc[3] = {0, 0, 0}, glo[3]; + t2l.MasterToLocal(loc, glo); + mX = glo[0]; // } diff --git a/Detectors/Align/src/AlignableVolume.cxx b/Detectors/Align/src/AlignableVolume.cxx index 00c3d97ee682a..73d888dc1559b 100644 --- a/Detectors/Align/src/AlignableVolume.cxx +++ b/Detectors/Align/src/AlignableVolume.cxx @@ -42,10 +42,6 @@ G_0*delta_0 ... G^-1_{j-2}*G_{j-1}*delta_{j-1}*G^-1_{j-1}*G_j*delta_j = Delta_0 * Delta_{1} ... Delta_{j-1}*Delta_{j}... * G_j - -> Delta_j = G'_{j-1} * G^-1_{j-1} * G_j * G'^-1_j - where G and G' are modified and original L2G matrices - - From this by induction one gets relation between local and global deltas: Delta_j = Z_j * delta_j * Z^-1_j @@ -295,6 +291,9 @@ void AlignableVolume::prepareMatrixL2G(bool reco) { // extract from geometry L2G matrix, depending on reco flag, set it as a reco-time // or current alignment matrix + if (isDummyEnvelope() || isDummy()) { + return; // unit matrix + } const char* path = getSymName(); if (gGeoManager->GetAlignableEntry(path)) { const TGeoHMatrix* l2g = base::GeometryManager::getMatrix(path); @@ -710,7 +709,7 @@ bool AlignableVolume::createLocDeltaMatrix(TGeoHMatrix& deltaM) const } //_________________________________________________________________ -void AlignableVolume::createAlignmenMatrix(TGeoHMatrix& alg) const +void AlignableVolume::createAlignmenMatrix(TGeoHMatrix& alg, const TGeoHMatrix* envelopeDelta) const { // create final alignment matrix, accounting for eventual prealignment // @@ -724,8 +723,22 @@ void AlignableVolume::createAlignmenMatrix(TGeoHMatrix& alg) const // but this creates precision problem. // Therefore we use explicitly cached Deltas from prealignment object. // + // If (parent) envelopeDelta is provided, it is simply added on top of its proper global delta matrix + const AlignableVolume* par = getParent(); - if (createGloDeltaMatrix(alg) && par) { // account parent matrices only if the alg matrix is non-trivial + bool nonTrivial = createGloDeltaMatrix(alg); + if (envelopeDelta) { + if (nonTrivial) { + alg.MultiplyLeft(envelopeDelta); + } else { + alg = *envelopeDelta; + } + nonTrivial = true; + } + // Account parent matrices only if the alg matrix is non-trivial. + // Also, if parent is dummy envelope, it is already accounted via envelopeDelta + if (nonTrivial && (par && !par->isDummyEnvelope())) { + // if envelopeDelta is provided, then there should be TGeoHMatrix dchain; while (par) { dchain.MultiplyLeft(&par->getGlobalDeltaRef()); @@ -777,7 +790,7 @@ void AlignableVolume::createAlignmenMatrix(TGeoHMatrix& alg) const */ //_________________________________________________________________ -void AlignableVolume::createAlignmentObjects(std::vector& arr) const +void AlignableVolume::createAlignmentObjects(std::vector& arr, const TGeoHMatrix* envelopeDelta) const { // add to supplied array alignment object for itself and children if (isDummy()) { @@ -785,11 +798,27 @@ void AlignableVolume::createAlignmentObjects(std::vectorcreateAlignmentObjects(arr); + getChild(ich)->createAlignmentObjects(arr, nonTrivial ? envelopeDelta : nullptr); } } diff --git a/Detectors/Align/src/AlignmentTrack.cxx b/Detectors/Align/src/AlignmentTrack.cxx index b22b23b4881f2..644ee07c64984 100644 --- a/Detectors/Align/src/AlignmentTrack.cxx +++ b/Detectors/Align/src/AlignmentTrack.cxx @@ -168,7 +168,7 @@ bool AlignmentTrack::calcResidDeriv(double* extendedParams, bool invert, int pFr // (like http://root.cern.ch/root/html/ROOT__Math__RichardsonDerivator.html) // const auto& algConf = AlignConfig::Instance(); - trackParam_t probD[kNRDClones]; // use this to vary supplied param for derivative calculation + trackPar_t probD[kNRDClones]; // use this to vary supplied param for derivative calculation double varDelta[kRichardsonN]; const int kInvElem[kNKinParBON] = {-1, 1, 1, -1, -1}; // @@ -511,7 +511,7 @@ bool AlignmentTrack::calcResiduals(const double* extendedParams, bool invert, in } //______________________________________________________ -bool AlignmentTrack::propagateParamToPoint(trackParam_t* tr, int nTr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, int signCorr) +bool AlignmentTrack::propagateParamToPoint(trackPar_t* tr, int nTr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, int signCorr) { // Propagate set of tracks to the point (only parameters, no error matrix) // VECTORIZE this @@ -521,7 +521,7 @@ bool AlignmentTrack::propagateParamToPoint(trackParam_t* tr, int nTr, const Alig if (!propagateParamToPoint(tr[itr], pnt, maxStep, maxSnp, mt, signCorr)) { if (algConf.verbose > 2) { LOG(error) << "Failed on clone " << itr << " propagation "; - tr[itr].print(); + tr[itr].printParam(); pnt->print(AlignmentPoint::kMeasurementBit | AlignmentPoint::kMaterialBit); } return false; @@ -531,21 +531,33 @@ bool AlignmentTrack::propagateParamToPoint(trackParam_t* tr, int nTr, const Alig } //______________________________________________________ -bool AlignmentTrack::propagateParamToPoint(trackParam_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, int signCorr) +bool AlignmentTrack::propagateParamToPoint(trackPar_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, int signCorr) { // propagate tracks to the point (only parameters, no error matrix) return propagate(tr, pnt, maxStep, maxSnp, mt, nullptr, signCorr); } //______________________________________________________ -bool AlignmentTrack::propagateToPoint(trackParam_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr) +bool AlignmentTrack::propagateToPoint(trackParam_t& tr, trackPar_t* linRef, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr) { // propagate tracks to the point. If matCor is true, then material corrections will be applied. // if matPar pointer is provided, it will be filled by total x2x0 and signed xrho - return propagate(tr, pnt, maxStep, maxSnp, mt, tLT, signCorr); + return propagate(tr, linRef, pnt, maxStep, maxSnp, mt, tLT, signCorr); } -bool AlignmentTrack::propagate(trackParam_t& track, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr) +bool AlignmentTrack::propagate(trackParam_t& track, trackPar_t* linRef, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr) +{ + if (signCorr == 0) { // auto + // calculate the sign of the energy loss correction and ensure the upper leg of cosmics is calculated correctly. + double dx = pnt->getXTracking() - track.getX(); + int dir = dx > 0.f ? 1 : -1; + signCorr = pnt->isInvDir() ? dir : -dir; // propagation along the track direction should have signCorr=-1 + } + // do propagation in at least 2 step to reveal eventual effect of MS on the position + return PropagatorD::Instance()->propagateToAlphaX(track, linRef, pnt->getAlphaSens(), pnt->getXTracking(), pnt->getUseBzOnly(), maxSnp, maxStep, 2, mt, tLT, signCorr); +} + +bool AlignmentTrack::propagate(trackPar_t& track, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr) { if (signCorr == 0) { // auto // calculate the sign of the energy loss correction and ensure the upper leg of cosmics is calculated correctly. @@ -603,7 +615,7 @@ bool AlignmentTrack::ApplyMS(trackParam_t& trPar, double tms,double pms) */ //______________________________________________________ -bool AlignmentTrack::applyMatCorr(trackParam_t& trPar, const double* corrPar, const AlignmentPoint* pnt) +bool AlignmentTrack::applyMatCorr(trackPar_t& trPar, const double* corrPar, const AlignmentPoint* pnt) { // Modify track param (e.g. trackParam_t) in the tracking frame // by delta accounting for material effects @@ -630,7 +642,7 @@ bool AlignmentTrack::applyMatCorr(trackParam_t& trPar, const double* corrPar, co } //______________________________________________________ -bool AlignmentTrack::applyMatCorr(trackParam_t& trPar, const double* corr) +bool AlignmentTrack::applyMatCorr(trackPar_t& trPar, const double* corr) { // Modify track param (e.g. trackParam_t) in the tracking frame // by delta accounting for material effects @@ -645,7 +657,7 @@ bool AlignmentTrack::applyMatCorr(trackParam_t& trPar, const double* corr) printf("%+.3e ", corr[i]); } printf("\n"); - trPar.print(); + trPar.printParam(); } return false; } @@ -656,7 +668,7 @@ bool AlignmentTrack::applyMatCorr(trackParam_t& trPar, const double* corr) } //______________________________________________________ -bool AlignmentTrack::applyMatCorr(trackParam_t* trSet, int ntr, const double* corrDiag, const AlignmentPoint* pnt) +bool AlignmentTrack::applyMatCorr(trackPar_t* trSet, int ntr, const double* corrDiag, const AlignmentPoint* pnt) { // Modify set of track params (e.g. trackParam_t) in the tracking frame // by delta accounting for material effects @@ -683,7 +695,7 @@ bool AlignmentTrack::applyMatCorr(trackParam_t* trSet, int ntr, const double* co if (!applyMatCorr(trSet[itr], corr)) { if (algConf.verbose > 2) { LOGP(error, "Failed on clone {} materials", itr); - trSet[itr].print(); + trSet[itr].printParam(); } return false; } @@ -732,7 +744,7 @@ double AlignmentTrack::richardsonExtrap(const double* val, int ord) } //______________________________________________ -void AlignmentTrack::richardsonDeriv(const trackParam_t* trSet, const double* delta, const AlignmentPoint* pnt, double& derY, double& derZ) +void AlignmentTrack::richardsonDeriv(const trackPar_t* trSet, const double* delta, const AlignmentPoint* pnt, double& derY, double& derZ) { // Calculate Richardson derivatives for diagonalized Y and Z from a set of kRichardsonN pairs // of tracks with same parameter of i-th pair varied by +-delta[i] @@ -787,7 +799,7 @@ void AlignmentTrack::Print(Option_t* opt) const for (int ip = 0; ip < getNPoints(); ip++) { printf("#%3d ", ip); auto* pnt = getPoint(ip); - pnt->print(0); // RS FIXME OPT + pnt->print(AlignmentPoint::kMeasurementBit); // RS FIXME OPT // if (res && pnt->containsMeasurement()) { printf(" Residuals : %+.3e %+.3e -> Pulls: %+7.2f %+7.2f\n", @@ -848,17 +860,17 @@ bool AlignmentTrack::iniFit() { // perform initial fit of the track // - trackParam_t trc(*(trackParam_t*)this); - // + const auto& algConf = AlignConfig::Instance(); if (!getFieldON()) { // for field-off data impose nominal momentum + setQ2Pt(isCosmic() ? 1. / algConf.defPTB0Cosm : 1. / algConf.defPTB0Coll); } + trackParam_t trc(*(trackParam_t*)this); mChi2 = mChi2CosmUp = mChi2CosmDn = 0; // // the points are ranged from outer to inner for collision tracks, // and from outer point of lower leg to outer point of upper leg for the cosmic track // // the fit will always start from the outgoing track in inward direction - const auto& algConf = AlignConfig::Instance(); if (!fitLeg(trc, 0, getInnerPointID(), mNeedInv[0])) { if (algConf.verbose > 2) { LOG(warn) << "Failed fitLeg 0"; @@ -874,7 +886,7 @@ bool AlignmentTrack::iniFit() trackParam_t trcU = trc; if (!fitLeg(trcU, getNPoints() - 1, getInnerPointID() + 1, mNeedInv[1])) { //fit upper leg of cosmic track if (algConf.verbose > 2) { - LOG(warn) << "Failed fitLeg 0"; + LOG(warn) << "Failed fitLeg 1"; trc.print(); } return false; // collision track or cosmic lower leg @@ -882,7 +894,7 @@ bool AlignmentTrack::iniFit() // // propagate to reference point, which is the inner point of lower leg const AlignmentPoint* refP = getPoint(getInnerPointID()); - if (!propagateToPoint(trcU, refP, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, -1)) { // moving along the track: energy is lost + if (!propagateToPoint(trcU, nullptr, refP, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, -1)) { // moving along the track: energy is lost return false; } // @@ -922,6 +934,17 @@ bool AlignmentTrack::combineTracks(trackParam_t& trcL, const trackParam_t& trcU) return false; } // + LOGP(debug, "CosmDn: {}", trcL.asString()); + LOGP(debug, "CosmUp: {}", trcU.asString()); + float dsnp = trcL.getSnp() - trcU.getSnp(), dTgl = trcL.getTgl() - trcU.getTgl(); + if (std::abs(dsnp) > algConf.cosmMaxDSnp || std::abs(dTgl) > algConf.cosmMaxDTgl) { + if (algConf.verbose > 2) { + LOGP(error, "Rejecting track with dSnp={} dTgl={}", dsnp, dTgl); + LOGP(error, "CosmDn: {}", trcL.asString()); + LOGP(error, "CosmUp: {}", trcU.asString()); + } + return false; + } // const covMat_t& covU = trcU.getCov(); // const covMat_t& covL = trcL.getCov(); // @@ -968,7 +991,8 @@ bool AlignmentTrack::combineTracks(trackParam_t& trcL, const trackParam_t& trcU) } mChi2 += chi2; // - // printf("Combined: Chi2Tot:%.2f ChiUp:%.2f ChiDn:%.2f ChiCmb:%.2f\n",mChi2,mChi2CosmUp,mChi2CosmDn, chi2); + LOGP(debug, "CosmCB: {}", trcL.asString()); + LOGP(debug, "Combined: Chi2Tot:{} ChiUp:{} ChiDn:{} ChiCmb:{} DSnp:{} DTgl:{} Alp:{}", mChi2, mChi2CosmUp, mChi2CosmDn, chi2, dsnp, dTgl, trcL.getAlpha()); return true; } @@ -1004,7 +1028,7 @@ bool AlignmentTrack::fitLeg(trackParam_t& trc, int pFrom, int pTo, bool& inv) inv = true; trc.invert(); } - // Fit is done from outward to inward: normally against the track direction (hence e.loss is compensated) inless inversion is requested (cosmic upper leg) + // Fit is done from outward to inward: normally against the track direction (hence e.loss is compensated) unless inversion is requested (cosmic upper leg) if (!propagateParamToPoint(trc, p0, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), -1)) { if (algConf.verbose > 2) { LOGF(warn, "Failed on PropagateParamOnlyTo to %f", p0->getXTracking()); @@ -1012,6 +1036,7 @@ bool AlignmentTrack::fitLeg(trackParam_t& trc, int pFrom, int pTo, bool& inv) } return false; } + trackPar_t linRef(trc), *linRefP = algConf.useLinRef ? &linRef : nullptr; trc.setCov(kIniErr); trc.setCov(16 * trc.getQ2Pt() * trc.getQ2Pt(), 4, 4); // lowest diagonal element (Q2Pt2) // @@ -1030,7 +1055,7 @@ bool AlignmentTrack::fitLeg(trackParam_t& trc, int pFrom, int pTo, bool& inv) int pntCnt = 0; for (int ip = pFrom; ip != pTo; ip += pinc) { // inward fit from outer point AlignmentPoint* pnt = getPoint(ip); - if (!propagateToPoint(trc, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { // against track direction : e.loss is compensated + if (!propagateToPoint(trc, linRefP, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { // against track direction : e.loss is compensated if (algConf.verbose > 2) { LOGF(warn, "Failed on propagateToPoint %d (%d : %d) %f", ip, pFrom, pTo, pnt->getXTracking()); trc.print(); @@ -1127,7 +1152,7 @@ bool AlignmentTrack::residKalman() trc.invert(); inv = !inv; } - if (!propagateToPoint(trc, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { + if (!propagateToPoint(trc, nullptr, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { return false; } if (!pnt->containsMeasurement()) { @@ -1166,7 +1191,7 @@ bool AlignmentTrack::residKalman() trc.invert(); inv = !inv; } - if (!propagateToPoint(trc, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { // we are going along track direction, e.loss is applied + if (!propagateToPoint(trc, nullptr, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { // we are going along track direction, e.loss is applied return false; } if (!pnt->containsMeasurement()) { @@ -1323,7 +1348,7 @@ bool AlignmentTrack::processMaterials(trackParam_t& trc, int pFrom, int pTo) // matTL.clearFast(); // printf("-> ProcMat %d (%d->%d)\n",ip,pFrom,pTo); - if (!propagateToPoint(trc, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), &matTL, signELoss)) { // with material corrections + if (!propagateToPoint(trc, nullptr, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), &matTL, signELoss)) { // with material corrections if (algConf.verbose > 2) { LOG(error) << "Failed to take track to point" << ip << " (dir: " << pFrom << "->" << pTo << ") with mat.corr."; trc.print(); @@ -1334,7 +1359,7 @@ bool AlignmentTrack::processMaterials(trackParam_t& trc, int pFrom, int pTo) // // is there enough material to consider the point as a scatterer? bool hasMaterial = matTL.getX2X0() > minX2X0; - if (!propagateToPoint(tr0, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType::USEMatCorrNONE, nullptr, signELoss)) { // no material corrections + if (!propagateToPoint(tr0, nullptr, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType::USEMatCorrNONE, nullptr, signELoss)) { // no material corrections if (algConf.verbose > 2) { LOG(error) << "Failed to take track to point" << ip << " (dir: " << pFrom << "->" << pTo << ") with mat.corr."; tr0.print(); @@ -1428,7 +1453,7 @@ void AlignmentTrack::sortPoints() // sort points in order against track direction: innermost point is last // for collision tracks. // For 2-leg cosmic tracks: 1st points of outgoing (lower) leg are added from large to - // small radii, then the points of incomint (upper) leg are added in increasing R direction + // small radii, then the points of incoming (upper) leg are added in increasing R direction // // The mInnerPointID will mark the id of the innermost point, i.e. the last one for collision-like // tracks and in case of cosmics - the point of lower leg with smallest R diff --git a/Detectors/Align/src/Controller.cxx b/Detectors/Align/src/Controller.cxx index 387af1460a0a0..5f55d07893d33 100644 --- a/Detectors/Align/src/Controller.cxx +++ b/Detectors/Align/src/Controller.cxx @@ -24,6 +24,7 @@ #include "Align/AlignableDetectorITS.h" #include "Align/AlignableDetectorTRD.h" #include "Align/AlignableDetectorTOF.h" +#include "Align/AlignableDetectorTPC.h" #include "Align/EventVertex.h" #include "Align/ResidualsControllerFast.h" #include "Align/GeometricalConstraint.h" @@ -43,10 +44,13 @@ #include #include #include +#include "GPUO2ExternalUser.h" +#include "DataFormatsTPC/WorkflowHelper.h" #include #include "CommonUtils/NameConf.h" #include "MathUtils/SymMatrixSolver.h" #include "DataFormatsParameters/GRPObject.h" +#include "GPUParam.h" #include "SimulationDataFormat/MCUtils.h" #include "Steer/MCKinematicsReader.h" @@ -110,6 +114,9 @@ void Controller::init() if (mDetMask[DetID::TRD]) { addDetector(new AlignableDetectorTRD(this)); } + if (mDetMask[DetID::TPC]) { + addDetector(new AlignableDetectorTPC(this)); + } if (mDetMask[DetID::TOF]) { addDetector(new AlignableDetectorTOF(this)); } @@ -160,6 +167,10 @@ void Controller::process() bool useVertexConstrain = false; if (vtx) { auto nContrib = vtx->getNContributors(); + // check cov matrix since data reconstructed with < 6797a257f5ab8ffaec32d56dddb0a321939bdf1c may have negative errors + if (vtx->getSigmaX2() < 0. || vtx->getSigmaY2() < 0. || vtx->getSigmaZ2() < 0.) { + continue; + } useVertexConstrain = nContrib >= algConf.vtxMinCont && nContrib <= algConf.vtxMaxCont; mStat.data[ProcStat::kInput][ProcStat::kVertices]++; } @@ -176,6 +187,8 @@ void Controller::process() int start = trackRef.getFirstEntryOfSource(src), end = start + trackRef.getEntriesOfSource(src); for (int ti = start; ti < end; ti++) { auto trackIndex = primVerGIs[ti]; + mAlgTrack->setCurrentTrackID(trackIndex); + bool tpcIn = false; if (trackIndex.isAmbiguous()) { auto& ambSeen = ambigTable[trackIndex]; if (ambSeen) { // processed @@ -209,21 +222,33 @@ void Controller::process() int ndet = 0, npntDet = 0; if ((det = getDetector(DetID::ITS))) { - if (contributorsGID[GIndex::ITS].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::ITS], false)) >= algConf.minITSClusters) { + if (contributorsGID[GIndex::ITS].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::ITS], algConf.minITSClusters, false)) > 0) { npnt += npntDet; ndet++; - } else if (mAllowAfterburnerTracks && contributorsGID[GIndex::ITSAB].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::ITSAB], false)) > 0) { + } else if (mAllowAfterburnerTracks && contributorsGID[GIndex::ITSAB].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::ITSAB], 2, false)) > 0) { npnt += npntDet; ndet++; } else { continue; } } - if ((det = getDetector(DetID::TRD)) && contributorsGID[GIndex::TRD].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::TRD], false)) >= algConf.minTRDTracklets) { + if ((det = getDetector(DetID::TPC)) && contributorsGID[GIndex::TPC].isIndexSet()) { + float t0 = 0, t0err = 0; + mRecoData->getTrackTime(trackIndex, t0, t0err); + ((AlignableDetectorTPC*)det)->setTrackTimeStamp(t0); + npntDet = det->processPoints(contributorsGID[GIndex::TPC], algConf.minTPCClusters, false); + if (npntDet > 0) { + npnt += npntDet; + ndet++; + tpcIn = true; + } + } + + if ((det = getDetector(DetID::TRD)) && contributorsGID[GIndex::TRD].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::TRD], algConf.minTRDTracklets, false)) > 0) { npnt += npntDet; ndet++; } - if ((det = getDetector(DetID::TOF)) && contributorsGID[GIndex::TOF].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::TOF], false)) > 0) { + if ((det = getDetector(DetID::TOF)) && contributorsGID[GIndex::TOF].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::TOF], algConf.minTOFClusters, false)) > 0) { npnt += npntDet; ndet++; } @@ -231,7 +256,7 @@ void Controller::process() if (algConf.verbose > 1) { LOGP(info, "processing track {} {} of vtref {}, Ndets:{}, Npoints: {}, use vertex: {} | Kin: {} Kout: {}", ti, trackIndex.asString(), ivref, ndet, npnt, useVertexConstrain && trackIndex.isPVContributor(), trcIn.asString(), trcOut.asString()); } - if (ndet < algConf.minDetectors) { + if (ndet < algConf.minDetectors || (tpcIn && ndet == 1)) { // we don't want TPC only track continue; } if (npnt < algConf.minPointTotal) { @@ -307,8 +332,16 @@ void Controller::process() } continue; } - - if (mUseMC && mDebugOutputLevel) { + if (mDebugOutputLevel && mAlgTrackDbg.setTrackParam(mAlgTrack.get())) { + mAlgTrackDbg.mGID = trackIndex; + (*mDBGOut) << "algtrack" + << "runNumber=" << mTimingInfo.runNumber + << "tfID=" << mTimingInfo.tfCounter + << "orbit=" << mTimingInfo.firstTForbit + << "bz=" << PropagatorD::Instance()->getNominalBz() + << "t=" << mAlgTrackDbg << "\n"; + } + if (mUseMC && mDebugOutputLevel > 1) { auto lbl = mRecoData->getTrackMCLabel(trackIndex); if (lbl.isValid()) { std::vector pntX, pntY, pntZ, trcX, trcY, trcZ, prpX, prpY, prpZ, alpha, xsens, pntXTF, pntYTF, pntZTF, resY, resZ; @@ -379,6 +412,12 @@ void Controller::process() void Controller::processCosmic() { auto timerStart = std::chrono::system_clock::now(); + const auto tracks = mRecoData->getCosmicTracks(); + if (!tracks.size()) { + LOGP(info, "Skipping TF {}: No cosmic tracks", mNTF); + mNTF++; + return; + } int nTrc = 0, nTrcAcc = 0; for (auto id = DetID::First; id <= DetID::Last; id++) { auto* det = getDetector(id); @@ -386,18 +425,164 @@ void Controller::processCosmic() det->prepareDetectorData(); // in case the detector needs to preprocess the RecoContainer data } } - const auto& algConf = AlignConfig::Instance(); - // process vertices with contributor tracks bool fieldON = std::abs(PropagatorD::Instance()->getNominalBz()) > 0.1; - const auto tracks = mRecoData->getCosmicTracks(); for (const auto& track : tracks) { + resetForNextTrack(); nTrc++; mStat.data[ProcStat::kInput][ProcStat::kCosmic]++; + std::array contributorsGID[2] = {mRecoData->getSingleDetectorRefs(track.getRefBottom()), mRecoData->getSingleDetectorRefs(track.getRefTop())}; + bool hasTRD = false, hasITS = false, hasTPC = false, hasTOF = false; + if (contributorsGID[0][GTrackID::TRD].isIndexSet() || contributorsGID[1][GTrackID::TRD].isIndexSet()) { + hasTRD = true; + } + if (contributorsGID[0][GTrackID::TOF].isIndexSet() || contributorsGID[1][GTrackID::TOF].isIndexSet()) { + hasTOF = true; + } + if (contributorsGID[0][GTrackID::TPC].isIndexSet() || contributorsGID[1][GTrackID::TPC].isIndexSet()) { + hasTPC = true; + } + if (contributorsGID[0][GTrackID::ITS].isIndexSet() || contributorsGID[1][GTrackID::ITS].isIndexSet()) { + hasITS = true; + } + // check detectors contributions + AlignableDetector* det = nullptr; + int ndet = 0, npnt = 0; + bool accTrack = true; + bool tpcIn = false; + if ((det = getDetector(DetID::ITS))) { + int npntDet = 0; + for (int ibt = 0; ibt < 2; ibt++) { + int npntDetBT = 0; + mAlgTrack->setCurrentTrackID(ibt ? track.getRefBottom() : track.getRefTop()); + if (contributorsGID[ibt][GIndex::ITS].isIndexSet() && (npntDetBT = det->processPoints(contributorsGID[ibt][GIndex::ITS], algConf.minITSClustersCosmLeg, ibt)) < 0) { + accTrack = false; + break; + } + npntDet += npntDetBT; + } + if (!accTrack || npntDet < algConf.minITSClustersCosm) { + continue; + } + if (npntDet) { + ndet++; + npnt += npntDet; + } + } + + if ((det = getDetector(DetID::TPC))) { + int npntDet = 0; + ((AlignableDetectorTPC*)det)->setTrackTimeStamp(track.getTimeMUS().getTimeStamp()); + for (int ibt = 0; ibt < 2; ibt++) { + int npntDetBT = 0; + mAlgTrack->setCurrentTrackID(ibt ? track.getRefBottom() : track.getRefTop()); + if (contributorsGID[ibt][GIndex::TPC].isIndexSet() && (npntDetBT = det->processPoints(contributorsGID[ibt][GIndex::TPC], algConf.minTPCClustersCosmLeg, ibt)) < 0) { + accTrack = false; + break; + } + npntDet += npntDetBT; + } + if (!accTrack || npntDet < algConf.minTPCClustersCosm) { + continue; + } + if (npntDet) { + ndet++; + npnt += npntDet; + tpcIn = true; + } + } + + if ((det = getDetector(DetID::TRD))) { + int npntDet = 0; + for (int ibt = 0; ibt < 2; ibt++) { + int npntDetBT = 0; + mAlgTrack->setCurrentTrackID(ibt ? track.getRefBottom() : track.getRefTop()); + if (contributorsGID[ibt][GIndex::TRD].isIndexSet() && (npntDetBT = det->processPoints(contributorsGID[ibt][GIndex::TRD], algConf.minTRDTrackletsCosmLeg, ibt)) < 0) { + accTrack = false; + break; + } + npntDet += npntDetBT; + } + if (!accTrack || npntDet < algConf.minTRDTrackletsCosm) { + continue; + } + if (npntDet) { + ndet++; + npnt += npntDet; + } + } + + if ((det = getDetector(DetID::TOF))) { + int npntDet = 0; + for (int ibt = 0; ibt < 2; ibt++) { + int npntDetBT = 0; + mAlgTrack->setCurrentTrackID(ibt ? track.getRefBottom() : track.getRefTop()); + if (contributorsGID[ibt][GIndex::TOF].isIndexSet() && (npntDetBT = det->processPoints(contributorsGID[ibt][GIndex::TOF], algConf.minTOFClustersCosmLeg, ibt)) < 0) { + accTrack = false; + break; + } + npntDet += npntDetBT; + } + if (!accTrack || npntDet < algConf.minTOFClustersCosm) { + continue; + } + if (npntDet) { + ndet++; + npnt += npntDet; + } + } + if (algConf.verbose > 1) { + LOGP(info, "processing cosmic track B-Leg:{} T-Leg:{}, Ndets:{}, Npoints: {}", track.getRefBottom().asString(), track.getRefTop().asString(), ndet, npnt); + } + if (ndet < algConf.minDetectorsCosm /* || (tpcIn && ndet == 1)*/) { + continue; + } + if (npnt < algConf.minPointTotalCosm) { + if (algConf.verbose > 0) { + LOGP(info, "too few points {} < {}", npnt, algConf.minPointTotalCosm); + } + continue; + } + mAlgTrack->setCosmic(true); + mAlgTrack->copyFrom(mRecoData->getTrackParamOut(track.getRefBottom())); // copy kinematices of outer track as the refit will be done inward + mAlgTrack->setFieldON(fieldON); + mAlgTrack->sortPoints(); + if (!mAlgTrack->iniFit()) { + if (algConf.verbose > 0) { + LOGP(warn, "iniFit failed"); + } + continue; + } + if (!mAlgTrack->processMaterials()) { + if (algConf.verbose > 0) { + LOGP(warn, "processMaterials failed"); + } + continue; + } + mAlgTrack->defineDOFs(); + if (!mAlgTrack->calcResidDeriv()) { + if (algConf.verbose > 0) { + LOGP(warn, "calcResidDeriv failed"); + } + continue; + } + if (mDebugOutputLevel && mAlgTrackDbg.setTrackParam(mAlgTrack.get())) { + mAlgTrackDbg.mGID = track.getRefBottom(); + mAlgTrackDbg.mGIDCosmUp = track.getRefTop(); + (*mDBGOut) << "algtrack" + << "runNumber=" << mTimingInfo.runNumber + << "tfID=" << mTimingInfo.tfCounter + << "orbit=" << mTimingInfo.firstTForbit + << "bz=" << PropagatorD::Instance()->getNominalBz() + << "t=" << mAlgTrackDbg << "\n"; + } + storeProcessedTrack(); + mStat.data[ProcStat::kAccepted][ProcStat::kCosmic]++; + nTrcAcc++; } auto timerEnd = std::chrono::system_clock::now(); std::chrono::duration duration = timerEnd - timerStart; - LOGP(info, "Processed TF {}: {} vertices ({} used), {} tracks ({} used) in {} ms", mNTF, nTrc, nTrcAcc, duration.count()); + LOGP(info, "Processed cosmic TF {}: {} tracks ({} used) in {} ms", mNTF, nTrc, nTrcAcc, duration.count()); mNTF++; } @@ -1690,5 +1875,17 @@ void Controller::expandGlobalsBy(int n) mNDOFs += n; } +//______________________________________________ +void Controller::setTPCVDrift(const o2::tpc::VDriftCorrFact& v) +{ + mTPCDrift = v; +} + +//______________________________________________ +void Controller::setTPCCorrMaps(const o2::gpu::TPCFastTransformPOD* maph) +{ + mTPCCorrMaps = maph; +} + } // namespace align } // namespace o2 diff --git a/Detectors/Align/src/ResidualsController.cxx b/Detectors/Align/src/ResidualsController.cxx index 94092597e5e40..b8d64ecea202f 100644 --- a/Detectors/Align/src/ResidualsController.cxx +++ b/Detectors/Align/src/ResidualsController.cxx @@ -206,7 +206,7 @@ bool ResidualsController::fillTrack(AlignmentTrack& trc, bool doKalman) int nfill = 0; for (int i = 0; i < np; i++) { auto pnt = trc.getPoint(i); - int inv = pnt->isInvDir() ? -1 : 1; // Flag invertion for cosmic upper leg + int inv = pnt->isInvDir() ? -1 : 1; // Flag inversion for cosmic upper leg if (!pnt->containsMeasurement()) { continue; } @@ -216,7 +216,7 @@ bool ResidualsController::fillTrack(AlignmentTrack& trc, bool doKalman) mVolID[nfill] = pnt->getVolID(); mLabel[nfill] = pnt->getSensor()->getInternalID(); mAlpha[nfill] = pnt->getAlphaSens(); - mX[nfill] = pnt->getXTracking() * inv; + mX[nfill] = pnt->getXTracking(); mY[nfill] = pnt->getYTracking(); mZ[nfill] = pnt->getZTracking(); mDY[nfill] = pnt->getResidY(); diff --git a/Detectors/Base/CMakeLists.txt b/Detectors/Base/CMakeLists.txt index 1bde116e23c23..e2d8114bc30fa 100644 --- a/Detectors/Base/CMakeLists.txt +++ b/Detectors/Base/CMakeLists.txt @@ -8,12 +8,20 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +#add_compile_options(-O0 -g -fPIC) + +# Optional VecGeom backend for material-budget LUT filling, off by default when TGeo2VecGeom +# isn't installed (guarded by O2_WITH_VECGEOM). Linked PRIVATE: VecGeom types never appear in +# DetectorsBase's public headers, so consumers need neither VecGeom headers nor VecGeom itself. +find_package(TGeo2VecGeom CONFIG QUIET) o2_add_library(DetectorsBase + TARGETVARNAME targetDetectorsBase SOURCES src/Detector.cxx src/GeometryManager.cxx src/MaterialManager.cxx src/MaterialManagerParam.cxx + src/GeometryManagerParam.cxx src/Propagator.cxx src/MatLayerCyl.cxx src/MatLayerCylSet.cxx @@ -25,6 +33,11 @@ o2_add_library(DetectorsBase src/TFIDInfoHelper.cxx src/GRPGeomHelper.cxx src/Stack.cxx + src/VMCSeederService.cxx + src/GlobalParams.cxx + src/O2Tessellated.cxx + src/TGeoGeometryUtils.cxx + src/CADGeometryUtils.cxx PUBLIC_LINK_LIBRARIES FairRoot::Base O2::CommonUtils O2::DetectorsCommonDataFormats @@ -39,16 +52,25 @@ o2_add_library(DetectorsBase O2::SimulationDataFormat O2::SimConfig O2::CCDB + O2::GPUDataTypes MC::VMC TBB::tbb - PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Merger # Must not link to avoid cyclic dependency + ROOT::Gdml ) +if(TGeo2VecGeom_FOUND) + target_compile_definitions(${targetDetectorsBase} PRIVATE O2_WITH_VECGEOM) + target_link_libraries(${targetDetectorsBase} PRIVATE TGeo2VecGeom::TGeo2VecGeom) +else() + message(STATUS "TGeo2VecGeom not found: DetectorsBase built without the optional VecGeom material-budget backend") +endif() + o2_target_root_dictionary(DetectorsBase HEADERS include/DetectorsBase/Detector.h include/DetectorsBase/GeometryManager.h include/DetectorsBase/MaterialManager.h include/DetectorsBase/MaterialManagerParam.h + include/DetectorsBase/GeometryManagerParam.h include/DetectorsBase/Propagator.h include/DetectorsBase/Ray.h include/DetectorsBase/MatCell.h @@ -56,17 +78,22 @@ o2_target_root_dictionary(DetectorsBase include/DetectorsBase/MatLayerCylSet.h include/DetectorsBase/Aligner.h include/DetectorsBase/Stack.h - include/DetectorsBase/SimFieldUtils.h) + include/DetectorsBase/SimFieldUtils.h + include/DetectorsBase/GlobalParams.h + include/DetectorsBase/O2Tessellated.h + ) if(BUILD_SIMULATION) - o2_add_test( - MatBudLUT - SOURCES test/testMatBudLUT.cxx - COMPONENT_NAME DetectorsBase - PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::ITSMFTReconstruction - LABELS detectorsbase - ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage + if (NOT APPLE) + o2_add_test( + MatBudLUT + SOURCES test/testMatBudLUT.cxx + COMPONENT_NAME DetectorsBase + PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::ITSMFTReconstruction + LABELS detectorsbase + ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/${CMAKE_INSTALL_DATADIR}) + endif() o2_add_test( MCStack @@ -78,6 +105,24 @@ if(BUILD_SIMULATION) VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/${CMAKE_INSTALL_DATADIR}) endif() +install(FILES test/buildMatBudLUT.C + test/compareMatBudLUT.C + test/extractLUTLayers.C + test/rescaleLUT.C + DESTINATION share/macro/) + o2_add_test_root_macro(test/buildMatBudLUT.C PUBLIC_LINK_LIBRARIES O2::DetectorsBase LABELS detectorsbase) + +o2_add_test_root_macro(test/compareMatBudLUT.C + PUBLIC_LINK_LIBRARIES O2::DetectorsBase + LABELS detectorsbase) + +o2_add_test_root_macro(test/extractLUTLayers.C + PUBLIC_LINK_LIBRARIES O2::DetectorsBase + LABELS detectorsbase) + +o2_add_test_root_macro(test/rescaleLUT.C + PUBLIC_LINK_LIBRARIES O2::DetectorsBase + LABELS detectorsbase) diff --git a/Detectors/Base/include/DetectorsBase/CADGeometryUtils.h b/Detectors/Base/include/DetectorsBase/CADGeometryUtils.h new file mode 100644 index 0000000000000..12d626d0eb572 --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/CADGeometryUtils.h @@ -0,0 +1,50 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CADGeometryUtils.h +/// \brief Helpers to inject CAD-derived (TGeo) geometry into O2 simulation +/// +/// These utilities are shared between purely passive external modules +/// (o2::passive::ExternalModule) and sensitive external detectors +/// (o2::ext::ExternalDetector). They deal with the geometry produced by +/// scripts/geometry/O2_CADtoTGeo.py, which is emitted as a ROOT macro. + +#ifndef ALICEO2_BASE_CADGEOMETRYUTILS_H +#define ALICEO2_BASE_CADGEOMETRYUTILS_H + +#include + +class TGeoVolume; + +namespace o2::base +{ + +/// JIT-compile a CAD-derived ROOT geometry macro (as produced by O2_CADtoTGeo.py) +/// and execute it to obtain the top TGeoVolume of the described module. +/// +/// The macro body is wrapped into a unique namespace (derived from \a instanceTag) +/// so that several such macros — which all export identically named symbols +/// (build(), get_builder_hook_unchecked(), ...) — can coexist in the same Cling +/// session without colliding. Returns nullptr on failure. +/// +/// \param macroFile path to the geometry macro (shell variables are expanded) +/// \param instanceTag a short tag used to build a unique, human-readable namespace +TGeoVolume* buildCADVolumeFromMacro(const std::string& macroFile, const std::string& instanceTag); + +/// Re-register the TGeo media used in the volume tree rooted at \a top into the O2 +/// MaterialManager under ownership of \a modulename, rewiring the volumes to the +/// newly created media. This brings the CAD-imported media under O2's media/cut +/// handling (so that e.g. tracking cuts apply consistently). +void remapCADMedia(TGeoVolume* top, const char* modulename); + +} // namespace o2::base + +#endif diff --git a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h index d7ef66635ff9a..e94123bb2b7ff 100644 --- a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h +++ b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h @@ -26,11 +26,15 @@ #include "DetectorsCommonDataFormats/CTFHeader.h" #include "DetectorsCommonDataFormats/CTFIOSize.h" #include "DataFormatsCTP/TriggerOffsetsParam.h" -#include "rANS/rans.h" +#include "DetectorsCommonDataFormats/ANSHeader.h" +#include "rANS/factory.h" +#include "rANS/compat.h" +#include "rANS/histogram.h" #include #include "Framework/InitContext.h" #include "Framework/ConcreteDataMatcher.h" #include "Framework/ConfigParamRegistry.h" +#include namespace o2 { @@ -54,8 +58,8 @@ class CTFCoderBase Decoder }; CTFCoderBase() = delete; - CTFCoderBase(int n, DetID det, float memFactor = 1.f) : mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f) {} - CTFCoderBase(OpType op, int n, DetID det, float memFactor = 1.f) : mOpType(op), mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f) {} + CTFCoderBase(int n, DetID det, float memFactor = 1.f, const std::string& ctfdictOpt = "none") : mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f), mDictOpt{ctfdictOpt} {} + CTFCoderBase(OpType op, int n, DetID det, float memFactor = 1.f, const std::string& ctfdictOpt = "none") : mOpType(op), mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f), mDictOpt{ctfdictOpt} {} virtual ~CTFCoderBase() = default; virtual void createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) = 0; @@ -66,7 +70,8 @@ class CTFCoderBase template void setSelectedIRFrames(const SPAN& sp) { - mIRFrameSelector.setSelectedIRFrames(sp, mIRFrameSelMarginBwd, mIRFrameSelMarginFwd, true); + reportIRFrames(); + mIRFrameSelector.setSelectedIRFrames(sp, mIRFrameSelMarginBwd, mIRFrameSelMarginFwd, mIRFrameSelShift, true); } template @@ -76,19 +81,30 @@ class CTFCoderBase void createCodersFromFile(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op, bool mayFail = false); template - void createCoder(OpType op, const o2::rans::RenormedFrequencyTable& renormedFrequencyTable, int slot) + void createCoder(OpType op, const o2::rans::RenormedDenseHistogram& renormedHistogram, int slot) { - if (renormedFrequencyTable.empty()) { - LOG(warning) << "Empty dictionary provided for slot " << slot << ", " << (op == OpType::Encoder ? "encoding" : "decoding") << " will assume literal symbols only"; - } - - switch (op) { - case OpType::Encoder: - mCoders[slot].reset(new o2::rans::LiteralEncoder64(renormedFrequencyTable)); - break; - case OpType::Decoder: - mCoders[slot].reset(new o2::rans::LiteralDecoder64(renormedFrequencyTable)); - break; + LOG_IF(warning, renormedHistogram.empty()) << fmt::format("Empty dictionary provided for slot {}, {} will assume literal symbols only", slot, (op == OpType::Encoder ? "encoding" : "decoding")); + + if (mANSVersion == ANSVersionCompat) { + switch (op) { + case OpType::Encoder: + mCoders[slot] = std::make_any>(rans::compat::makeEncoder::fromRenormed(renormedHistogram)); + break; + case OpType::Decoder: + mCoders[slot] = std::make_any>(rans::compat::makeDecoder::fromRenormed(renormedHistogram)); + break; + } + } else if (mANSVersion == ANSVersion1) { + switch (op) { + case OpType::Encoder: + mCoders[slot] = std::make_any>(rans::makeDenseEncoder<>::fromRenormed(renormedHistogram)); + break; + case OpType::Decoder: + mCoders[slot] = std::make_any>(rans::makeDecoder<>::fromRenormed(renormedHistogram)); + break; + } + } else { + throw std::runtime_error("unsupported ANS version"); } } @@ -125,7 +141,11 @@ class CTFCoderBase o2::utils::IRFrameSelector& getIRFramesSelector() { return mIRFrameSelector; } size_t getIRFrameSelMarginBwd() const { return mIRFrameSelMarginBwd; } size_t getIRFrameSelMarginFwd() const { return mIRFrameSelMarginFwd; } + long getIRFrameSelShift() const { return mIRFrameSelShift; } + inline const ctf::ANSHeader& getANSVersion() const noexcept { return mANSVersion; }; + inline ctf::ANSHeader& getANSVersion() { return const_cast(const_cast(*this).getANSVersion()); }; + inline void setANSVersion(const ctf::ANSHeader& ansVersion) noexcept { mANSVersion = ansVersion; }; void setBCShift(int64_t n) { mBCShift = n; } void setFirstTFOrbit(uint32_t n) { mFirstTFOrbit = n; } auto getBCShift() const { return mBCShift; } @@ -133,7 +153,16 @@ class CTFCoderBase void setSupportBCShifts(bool v = true) { mSupportBCShifts = v; } bool getSupportBCShifts() const { return mSupportBCShifts; } + void setDictBinding(const std::string& s) { mDictBinding = s; } + const std::string& getDictBinding() const { return mDictBinding; } + + void setTrigOffsBinding(const std::string& s) { mTrigOffsBinding = s; } + const std::string& getTrigOffsBinding() const { return mTrigOffsBinding; } + + const DetID getDet() const { return mDet; } + protected: + void reportIRFrames(); std::string getPrefix() const { return o2::utils::Str::concat_string(mDet.getName(), "_CTF: "); } void checkDictVersion(const CTFDictHeader& h) const; bool isTreeDictionary(const void* buff) const; @@ -144,20 +173,37 @@ class CTFCoderBase } bool canApplyBCShift(const o2::InteractionRecord& ir) const { return canApplyBCShift(ir, mBCShift); } + template + [[nodiscard]] size_t estimateBufferSize(size_t slot, source_IT samplesBegin, source_IT samplesEnd); + + template + size_t estimateBufferSize(size_t slot, size_t nSamples); + + template + [[nodiscard]] size_t estimateBufferSize(size_t slot, const std::vector& samples) + { + return estimateBufferSize(slot, samples.begin(), samples.end()); + } + template std::vector loadDictionaryFromTree(TTree* tree); - std::vector> mCoders; // encoders/decoders + std::vector mCoders; // encoders/decoders DetID mDet; - CTFDictHeader mExtHeader; // external dictionary header + std::string mDictOpt{}; + std::string mDictBinding{"ctfdict"}; + std::string mTrigOffsBinding{"trigoffset"}; + CTFDictHeader mExtHeader; // external dictionary header o2::utils::IRFrameSelector mIRFrameSelector; // optional IR frames selector - float mMemMarginFactor = 1.0f; // factor for memory allocation in EncodedBlocks + float mMemMarginFactor = 1.0f; // factor for memory allocation in EncodedBlocks bool mLoadDictFromCCDB{true}; bool mSupportBCShifts{false}; - OpType mOpType; // Encoder or Decoder - int64_t mBCShift = 0; // shift to apply to decoded IR (i.e. CTP offset if was not corrected on raw data decoding level) + OpType mOpType; // Encoder or Decoder + ctf::ANSHeader mANSVersion{ctf::ANSVersionCompat}; // Version of the ANSEncoder/Decoder + int64_t mBCShift = 0; // shift to apply to decoded IR (i.e. CTP offset if was not corrected on raw data decoding level) uint32_t mFirstTFOrbit = 0; size_t mIRFrameSelMarginBwd = 0; // margin in BC to add to the IRFrame lower boundary when selection is requested size_t mIRFrameSelMarginFwd = 0; // margin in BC to add to the IRFrame upper boundary when selection is requested + long mIRFrameSelShift = 0; // Global shift of the IRFrames, to account for e.g. detector latency int mVerbosity = 0; }; @@ -265,15 +311,34 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) if (ic.options().hasOption("irframe-margin-fwd")) { mIRFrameSelMarginFwd = ic.options().get("irframe-margin-fwd"); } - auto dict = ic.options().get("ctf-dict"); - if (dict.empty() || dict == "ccdb") { // load from CCDB + if (ic.options().hasOption("irframe-shift")) { + mIRFrameSelShift = (long)ic.options().get("irframe-shift"); + } + bool ansVersionProvided = false; + if (ic.options().hasOption("ans-version")) { + if (ic.options().isSet("ans-version")) { + const std::string ansVersionString = ic.options().get("ans-version"); + if (!ansVersionString.empty()) { + mANSVersion = ansVersionFromString(ansVersionString); + LOGP(info, "parsing ansVersionString {} into {}", ansVersionString, static_cast(mANSVersion)); + if (mANSVersion == ANSVersionUnspecified) { + throw std::invalid_argument(fmt::format("Invalid ANS Version {}", ansVersionString)); + } + ansVersionProvided = true; + } + } + } + if (mDictOpt.empty() || mDictOpt == "ccdb") { // load from CCDB mLoadDictFromCCDB = true; } else { - if (dict != "none") { // none means per-CTF dictionary will created on the fly - createCodersFromFile(dict, mOpType); - LOGP(info, "Loaded {} from {}", mExtHeader.asString(), dict); + if (mDictOpt != "none") { // none means per-CTF dictionary will created on the fly + createCodersFromFile(mDictOpt, mOpType); + LOGP(info, "Loaded {} from {}, ANS Version {}", mExtHeader.asString(), mDictOpt, std::string(mANSVersion)); } else { - LOGP(info, "Internal per-TF CTF Dict will be created"); + if (!ansVersionProvided) { + mANSVersion = ANSVersion1; + } + LOGP(info, "Internal per-TF CTF Dict will be created, ANS Version {}", std::string(mANSVersion)); } mLoadDictFromCCDB = false; // don't try to load from CCDB } @@ -308,7 +373,7 @@ bool CTFCoderBase::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, voi } createCoders(*dict, mOpType); mExtHeader = static_cast(CTF::get(dict->data())->getHeader()); - LOGP(info, "Loaded {} from CCDB", mExtHeader.asString()); + LOGP(info, "Loaded {} from CCDB, ANS Version {}", mExtHeader.asString(), std::string(mANSVersion)); } mLoadDictFromCCDB = false; // we read the dictionary at most once! } else if ((match = (matcher == o2::framework::ConcreteDataMatcher("CTP", "Trig_Offset", 0)))) { @@ -326,6 +391,39 @@ bool CTFCoderBase::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, voi return match; } +template +[[nodiscard]] inline size_t CTFCoderBase::estimateBufferSize(size_t slot, IT samplesBegin, IT samplesEnd) +{ + using source_type = typename std::iterator_traits::value_type; + const size_t nSamples = std::distance(samplesBegin, samplesEnd); + return estimateBufferSize(slot, nSamples); +}; + +template +[[nodiscard]] inline size_t CTFCoderBase::estimateBufferSize(size_t slot, size_t nSamples) +{ + + std::any& coder = mCoders[slot]; + if (coder.has_value()) { + const size_t alphabetRangeBits = [this, &coder]() { + if (mANSVersion == ANSVersionCompat) { + const auto& encoder = std::any_cast&>(coder); + auto view = rans::trim(rans::makeHistogramView(encoder.getSymbolTable())); + return rans::utils::getRangeBits(view.getMin(), view.getMax()); + } else if (mANSVersion == ANSVersion1) { + const auto& encoder = std::any_cast&>(coder); + auto view = rans::trim(rans::makeHistogramView(encoder.getSymbolTable())); + return rans::utils::getRangeBits(view.getMin(), view.getMax()); + } else { + throw std::runtime_error("unsupported ANS version"); + } + }(); + return rans::compat::calculateMaxBufferSizeB(nSamples, alphabetRangeBits); + } else { + return nSamples * sizeof(source_T); + } +}; + } // namespace ctf } // namespace o2 diff --git a/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h b/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h index ba87b6ecd7ddb..ba3dd821fa118 100644 --- a/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h +++ b/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h @@ -25,9 +25,15 @@ #include "Framework/CompletionPolicy.h" #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DeviceSpec.h" +#include "Framework/Task.h" #include "Framework/DataSpecUtils.h" #include #include +#include +#ifdef WITH_OPENMP +#include +#include "TROOT.h" +#endif namespace o2 { @@ -160,7 +166,12 @@ o2::framework::DataProcessorSpec specCombiner(std::string const& name, std::vect } for (auto& is : spec.inputs) { if (inputBindings.find(is.binding) != inputBindings.end()) { - LOG(error) << "Found duplicate input binding " << is.binding; + // we can accept duplicate binding if it is bound to the same spec (e.g. ccdbParamSpec) + if (std::find(combinedInputSpec.begin(), combinedInputSpec.end(), is) == combinedInputSpec.end()) { + LOG(error) << "Found duplicate input binding with different spec.:" << is; + } else { + continue; // consider as already accounted + } } combinedInputSpec.push_back(is); inputBindings[is.binding] = 1; @@ -212,6 +223,16 @@ o2::framework::DataProcessorSpec specCombiner(std::string const& name, std::vect void init(o2::framework::InitContext& ic) { std::cerr << "Init Combined\n"; + mNThreads = std::max(1, ic.options().get("combine-nthreads")); + if (mNThreads > 1) { +#ifdef WITH_OPENMP + LOGP(info, "Combined tasks will be run with {} threads", mNThreads); + ROOT::EnableThreadSafety(); +#else + LOGP(warn, "{} threads requested for combined tasks but OpenMP is not detected, link it from the workflow CMakeList", mNThreads); + mNThreads = 1; +#endif + } for (auto& t : tasks) { // the init function actually creates the onProcess function // which we have to do here (maybe some more stuff needed) @@ -236,18 +257,33 @@ o2::framework::DataProcessorSpec specCombiner(std::string const& name, std::vect void run(o2::framework::ProcessingContext& pc) { - std::cerr << "Processing Combined\n"; - for (auto& t : tasks) { - std::cerr << " Executing sub-device " << t.name << "\n"; - t.algorithm.onProcess(pc); + std::cerr << "Processing Combined with " << mNThreads << " threads\n"; + if (mNThreads > 1) { + size_t nt = tasks.size(); +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) +#endif + for (size_t i = 0; i < nt; i++) { + auto& t = tasks[i]; + std::cerr << " Executing sub-device " << t.name << "\n"; + t.algorithm.onProcess(pc); + } + } else { + for (auto& t : tasks) { + std::cerr << " Executing sub-device " << t.name << "\n"; + t.algorithm.onProcess(pc); + } } } private: std::vector tasks; std::unordered_map> optionsRemap; + int mNThreads = 1; }; + combinedOptions.emplace_back(ConfigParamSpec{"combine-nthreads", VariantType::Int, 1, {"Number of threads for combined tasks"}}); + return DataProcessorSpec{ name, combinedInputSpec, diff --git a/Detectors/Base/include/DetectorsBase/Detector.h b/Detectors/Base/include/DetectorsBase/Detector.h index 73764e8b7021b..54e264dd22666 100644 --- a/Detectors/Base/include/DetectorsBase/Detector.h +++ b/Detectors/Base/include/DetectorsBase/Detector.h @@ -29,7 +29,6 @@ #include #include #include -#include #include "CommonUtils/ShmManager.h" #include "CommonUtils/ShmAllocator.h" #include @@ -42,9 +41,7 @@ #include -namespace o2 -{ -namespace base +namespace o2::base { /// This is the basic class for any AliceO2 detector module, whether it is @@ -109,6 +106,9 @@ class Detector : public FairDetector /// declare alignable volumes of detector virtual void addAlignableVolumes() const; + /// fill parallel geometry with sensitive volumes of detector + virtual void fillParallelWorld() const; + /// Sets per wrapper volume parameters virtual void defineWrapperVolume(Int_t id, Double_t rmin, Double_t rmax, Double_t zspan); @@ -216,6 +216,10 @@ class Detector : public FairDetector // which is required for media creation static void initFieldTrackingParams(int& mode, float& maxfield); + /// set the DetID to HitBitIndex mapping. Succeeds if not already set. + static void setDetId2HitBitIndex(std::vector const& v) { Detector::sDetId2HitBitIndex = v; } + static std::vector const& getDetId2HitBitIndex() { return Detector::sDetId2HitBitIndex; } + protected: Detector(const Detector& origin); @@ -233,6 +237,7 @@ class Detector : public FairDetector static Float_t mDensityFactor; //! factor that is multiplied to all material densities (ONLY for // systematic studies) + static std::vector sDetId2HitBitIndex; //! global lookup table keeping mapping of DetID to index in hit bit field (used in MCTrack) ClassDefOverride(Detector, 1); // Base class for ALICE Modules }; @@ -255,17 +260,12 @@ T decodeShmMessage(fair::mq::Parts& dataparts, int index, bool*& busy) } // this goes into the source -void attachMessageBufferToParts(fair::mq::Parts& parts, fair::mq::Channel& channel, - void* data, size_t size, void (*func_ptr)(void* data, void* hint), void* hint); +void attachMessageBufferToParts(fair::mq::Parts& parts, fair::mq::Channel& channel, void* data, TClass* cl); template void attachTMessage(Container const& hits, fair::mq::Channel& channel, fair::mq::Parts& parts) { - TMessage* tmsg = new TMessage(); - tmsg->WriteObjectAny((void*)&hits, TClass::GetClass(typeid(hits))); - attachMessageBufferToParts( - parts, channel, tmsg->Buffer(), tmsg->BufferSize(), - [](void* data, void* hint) { delete static_cast(hint); }, tmsg); + attachMessageBufferToParts(parts, channel, (void*)&hits, TClass::GetClass(typeid(hits))); } void* decodeTMessageCore(fair::mq::Parts& dataparts, int index); @@ -530,9 +530,14 @@ class DetImpl : public o2::base::Detector { using Hit_t = typename std::remove_pointer(this)->Det::getHits(0))>::type; using Collector_t = tbb::concurrent_unordered_map>>>; - static Collector_t hitcollector; // note: we can't put this as member because - // decltype type deduction doesn't seem to work for class members; so we use a static member - // and will use some pointer member to communicate this data to other functions + // note: we can't put this as a member because decltype type deduction doesn't seem to work for + // class members; so we use a static and communicate it to other functions via a pointer member. + // The collector must be kept *per detector instance* (keyed by 'this'): for most detectors there + // is a single instance per C++ type, but several external detectors share the same type + // (o2::ext::ExternalDetector) and would otherwise clobber/double-free each other's buffers. + // tbb::concurrent_unordered_map is node-based, so the reference stays valid across insertions. + static tbb::concurrent_unordered_map hitcollectors; + auto& hitcollector = hitcollectors[this]; mHitCollectorBufferPtr = (char*)&hitcollector; int probe = 0; @@ -738,9 +743,9 @@ class DetImpl : public o2::base::Detector int mInitialized = false; char* mHitCollectorBufferPtr = nullptr; //! pointer to hit (collector) buffer location (strictly internal) + ClassDefOverride(DetImpl, 0); }; -} // namespace base -} // namespace o2 +} // namespace o2::base #endif diff --git a/Detectors/Base/include/DetectorsBase/GRPGeomHelper.h b/Detectors/Base/include/DetectorsBase/GRPGeomHelper.h index e3ec5cf02b2b5..ec1f9a91c3f20 100644 --- a/Detectors/Base/include/DetectorsBase/GRPGeomHelper.h +++ b/Detectors/Base/include/DetectorsBase/GRPGeomHelper.h @@ -22,6 +22,7 @@ #include "DataFormatsParameters/GRPLHCIFData.h" #include "DataFormatsParameters/GRPECSObject.h" #include "DataFormatsParameters/GRPMagField.h" +#include "DataFormatsParameters/AggregatedRunInfo.h" namespace o2::framework { @@ -92,6 +93,7 @@ struct GRPGeomRequest { Ideal, Alignments }; + bool askAggregateRunInfo = false; bool askGRPECS = false; bool askGRPLHCIF = false; bool askGRPMagField = false; @@ -104,7 +106,8 @@ struct GRPGeomRequest { bool needPropagatorD = false; // init also PropagatorD GRPGeomRequest() = delete; - GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, bool GRPMagField, bool askMatLUT, GeomRequest geom, std::vector& inputs, bool askOnce = false, bool needPropD = false); + GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, bool GRPMagField, bool askMatLUT, GeomRequest geom, std::vector& inputs, bool askOnce = false, bool needPropD = false, std::string detMaskString = "all"); + void requireAggregateRunInfo(std::vector& inputs); void addInput(const o2::framework::InputSpec&& isp, std::vector& inputs); }; @@ -121,14 +124,16 @@ class GRPGeomHelper } void setRequest(std::shared_ptr req); bool finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj); - void checkUpdates(o2::framework::ProcessingContext& pc) const; + void checkUpdates(o2::framework::ProcessingContext& pc); auto getAlignment(o2::detectors::DetID det) const { return mAlignments[det]; } auto getMatLUT() const { return mMatLUT; } auto getGRPECS() const { return mGRPECS; } auto getGRPLHCIF() const { return mGRPLHCIF; } auto getGRPMagField() const { return mGRPMagField; } - auto getOrbitResetTimeMS() const { return mOrbitResetTimeMS; } + auto getOrbitResetTimeMS() const { return mOrbitResetTimeMUS / 1000; } + auto getOrbitResetTimeMUS() const { return mOrbitResetTimeMUS; } + const o2::parameters::AggregatedRunInfo& getAggregatedRunInfo() const { return mAggregatedRunInfo; } static int getNHBFPerTF(); private: @@ -141,7 +146,8 @@ class GRPGeomHelper const o2::parameters::GRPECSObject* mGRPECS = nullptr; const o2::parameters::GRPLHCIFData* mGRPLHCIF = nullptr; const o2::parameters::GRPMagField* mGRPMagField = nullptr; - long mOrbitResetTimeMS = 0; // orbit reset time in milliseconds + o2::parameters::AggregatedRunInfo mAggregatedRunInfo{}; + long mOrbitResetTimeMUS = 0; // orbit reset time in microseconds }; } // namespace base diff --git a/Detectors/Base/include/DetectorsBase/GeometryManager.h b/Detectors/Base/include/DetectorsBase/GeometryManager.h index 96998187cca63..f105d137c8742 100644 --- a/Detectors/Base/include/DetectorsBase/GeometryManager.h +++ b/Detectors/Base/include/DetectorsBase/GeometryManager.h @@ -29,6 +29,7 @@ #include class TGeoHMatrix; // lines 11-11 class TGeoManager; // lines 9-9 +class TGeoNavigator; namespace o2 { @@ -39,6 +40,12 @@ class AlignParam; namespace base { +/// Backend used to compute material budget for LUT filling: ROOT/TGeo (default, always +/// available) or VecGeom (requires O2 to be built against the optional TGeo2VecGeom +/// package; see GeometryManager::isVecGeomAvailable()). +enum class MatbudGeomBackend : int { ROOT = 0, + VECGEOM = 1 }; + /// Class for interfacing to the geometry; it also builds and manages the look-up tables for fast /// access to geometry and alignment information for sensitive alignable volumes: /// 1) the look-up table mapping unique volume ids to TGeoPNEntries. This allows to access @@ -69,7 +76,7 @@ class GeometryManager : public TObject static int getSensID(o2::detectors::DetID detid, int sensid) { /// compose combined detector+sensor ID for sensitive volumes - return (detid << sDetOffset) | (sensid & sSensorMask); + return detid <= o2::detectors::DetID::FOC ? ((detid << sDetOffset) | (sensid & sSensorMask)) : ((detid << sDetOffsetLarge) | (sensid & sSensorMaskLarge)); } /// Default destructor @@ -86,8 +93,7 @@ class GeometryManager : public TObject double meanZ = 0.; // mean Z: sum(x_i*Z_i)/sum(x_i) [adimensional] double meanZ2A = 0.; // Z/A mean: sum(x_i*Z_i/A_i)/sum(x_i) [adimensional] double length = -1.; // length: sum(x_i) [cm] - int nCross = 0; - ; // number of boundary crosses + int nCross = 0; // number of boundary crosses MatBudgetExt() = default; ~MatBudgetExt() = default; @@ -97,14 +103,18 @@ class GeometryManager : public TObject ClassDefNV(MatBudgetExt, 1); }; - static o2::base::MatBudget meanMaterialBudget(float x0, float y0, float z0, float x1, float y1, float z1); - static o2::base::MatBudget meanMaterialBudget(const math_utils::Point3D& start, const math_utils::Point3D& end) + /// Mean material budget between two points. Pass a navigator owned by the calling thread to + /// run lock-free from several threads; with nav = nullptr the shared navigator is used under + /// a mutex, as before. + static o2::base::MatBudget meanMaterialBudget(float x0, float y0, float z0, float x1, float y1, float z1, + TGeoNavigator* nav = nullptr); + static o2::base::MatBudget meanMaterialBudget(const math_utils::Point3D& start, const math_utils::Point3D& end, TGeoNavigator* nav = nullptr) { - return meanMaterialBudget(start.X(), start.Y(), start.Z(), end.X(), end.Y(), end.Z()); + return meanMaterialBudget(start.X(), start.Y(), start.Z(), end.X(), end.Y(), end.Z(), nav); } - static o2::base::MatBudget meanMaterialBudget(const math_utils::Point3D& start, const math_utils::Point3D& end) + static o2::base::MatBudget meanMaterialBudget(const math_utils::Point3D& start, const math_utils::Point3D& end, TGeoNavigator* nav = nullptr) { - return meanMaterialBudget(start.X(), start.Y(), start.Z(), end.X(), end.Y(), end.Z()); + return meanMaterialBudget(start.X(), start.Y(), start.Z(), end.X(), end.Y(), end.Z(), nav); } static MatBudgetExt meanMaterialBudgetExt(float x0, float y0, float z0, float x1, float y1, float z1); @@ -117,6 +127,17 @@ class GeometryManager : public TObject return meanMaterialBudgetExt(start.X(), start.Y(), start.Z(), end.X(), end.Y(), end.Z()); } + /// Whether this build of O2 was configured with the optional VecGeom material-budget + /// backend (i.e. TGeo2VecGeom was found at CMake configure time). +#ifdef O2_WITH_VECGEOM + static constexpr bool isVecGeomAvailable() { return true; } + /// Mean material budget between two points, using the VecGeom backend. On first call, + /// lazily converts the currently loaded TGeo geometry to VecGeom (once per process). + static o2::base::MatBudget vecGeomMaterialBudget(float x0, float y0, float z0, float x1, float y1, float z1); +#else + static constexpr bool isVecGeomAvailable() { return false; } +#endif + private: /// Default constructor GeometryManager() = default; @@ -132,11 +153,13 @@ class GeometryManager : public TObject /// detector geometry. The output global matrix is stored in 'm'. /// Returns kFALSE in case TGeo has not been initialized or the volume path is not valid. static Bool_t getOriginalMatrixFromPath(const char* path, TGeoHMatrix& m); + private: /// sensitive volume identifier composed from (det_ID< { + bool useParallelWorld = false; + bool usePwGeoBVH = false; + bool usePwCaching = false; + + int printLevel = -1; // geometry level to print out (determined by the volume path) + + O2ParamDef(GeometryManagerParam, "GeometryManagerParam"); +}; + +} // namespace o2 + +#endif /* DETECTORS_BASE_INCLUDE_GEOMETRYMANAGERPARAM_H_ */ diff --git a/Detectors/Base/include/DetectorsBase/GlobalParams.h b/Detectors/Base/include/DetectorsBase/GlobalParams.h new file mode 100644 index 0000000000000..25bee925196d3 --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/GlobalParams.h @@ -0,0 +1,29 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef DETECTORS_BASE_GLOBALPARAMS_H_ +#define DETECTORS_BASE_GLOBALPARAMS_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ + +struct GlobalParams : public o2::conf::ConfigurableParamHelper { + bool withITS3 = false; // detector geometry includes ITS3 + + O2ParamDef(GlobalParams, "GlobalParams"); +}; + +} // namespace o2 + +#endif // DETECTORS_BASE_GLOBALPARAMS_H_ diff --git a/Detectors/Base/include/DetectorsBase/MatCell.h b/Detectors/Base/include/DetectorsBase/MatCell.h index b5af66439955d..40c5fd3db1f69 100644 --- a/Detectors/Base/include/DetectorsBase/MatCell.h +++ b/Detectors/Base/include/DetectorsBase/MatCell.h @@ -31,7 +31,13 @@ struct MatCell { float meanX2X0; ///< fraction of radiaton lenght GPUd() MatCell() : meanRho(0.f), meanX2X0(0.f) {} - GPUdDefault() MatCell(const MatCell& src) CON_DEFAULT; + GPUdDefault() MatCell(const MatCell& src) = default; + + GPUd() void set(const MatCell& c) + { + meanRho = c.meanRho; + meanX2X0 = c.meanX2X0; + } GPUd() void scale(float scale) { @@ -49,7 +55,7 @@ struct MatBudget : MatCell { float length; ///< length in material GPUd() MatBudget() : length(0.f) {} - GPUdDefault() MatBudget(const MatBudget& src) CON_DEFAULT; + GPUdDefault() MatBudget(const MatBudget& src) = default; GPUd() void scale(float scale) { diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCyl.h b/Detectors/Base/include/DetectorsBase/MatLayerCyl.h index 4d509fdf9e3c1..04aefed06f010 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCyl.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCyl.h @@ -20,11 +20,16 @@ #include #endif #include "GPUCommonDef.h" +#ifndef GPUCA_ALIGPUCODE +#include "DetectorsBase/GeometryManager.h" // for MatbudGeomBackend +#endif #include "FlatObject.h" #include "GPUCommonRtypes.h" #include "GPUCommonMath.h" #include "DetectorsBase/MatCell.h" +class TGeoNavigator; + namespace o2 { namespace base @@ -54,17 +59,19 @@ class MatLayerCyl : public o2::gpu::FlatObject Within = 0, Above = 1 }; +#ifndef GPUCA_GPUCODE MatLayerCyl(); - MatLayerCyl(const MatLayerCyl& src) CON_DELETE; - ~MatLayerCyl() CON_DEFAULT; + MatLayerCyl(const MatLayerCyl& src) = delete; + ~MatLayerCyl() = default; +#endif #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version MatLayerCyl(float rMin, float rMax, float zHalfSpan, float dzMin, float drphiMin); void initSegmentation(float rMin, float rMax, float zHalfSpan, int nz, int nphi); void initSegmentation(float rMin, float rMax, float zHalfSpan, float dzMin, float drphiMin); - void populateFromTGeo(int ntrPerCell = 10); - void populateFromTGeo(int ip, int iz, int ntrPerCell); + void populateFromTGeo(int ntrPerCell = 10, MatbudGeomBackend backend = MatbudGeomBackend::ROOT); + void populateFromTGeo(int ip, int iz, int ntrPerCell, TGeoNavigator* nav = nullptr, MatbudGeomBackend backend = MatbudGeomBackend::ROOT); void print(bool data = false) const; #endif // !GPUCA_ALIGPUCODE @@ -90,13 +97,22 @@ class MatLayerCyl : public o2::gpu::FlatObject GPUd() const MatCell& getCellPhiBin(int iphi, int iz) const { return mCells[getCellIDPhiBin(iphi, iz)]; } GPUd() const MatCell& getCell(int iphiSlice, int iz) const { return mCells[getCellID(iphiSlice, iz)]; } +#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version + MatCell& getCellPhiBin(int iphi, int iz) + { + return mCells[getCellIDPhiBin(iphi, iz)]; + } + + void scale(float factor, bool _x2x0 = true, bool _rho = true); +#endif + // ---------------------- Z slice manipulation // convert Z to Zslice GPUd() RangeStatus isZOutside(float z) const { return z < getZMin() ? Below : (z > getZMax() ? Above : Within); } GPUd() int getZBinID(float z) const { int idz = int((z - getZMin()) * getDZInv()); // cannot be negative since before isZOutside is applied - return idz < getNZBins() ? idz : getNZBins() - 1; + return idz < getNZBins() ? (idz > 0 ? idz : 0) : getNZBins() - 1; } // lower boundary of Z slice @@ -156,6 +172,8 @@ class MatLayerCyl : public o2::gpu::FlatObject /// Gives minimal alignment in bytes required for the flat buffer static constexpr size_t getBufferAlignmentBytes() { return 8; } #endif + // linearized cell ID from phi bin and z bin + GPUd() int getCellIDPhiBin(int iphi, int iz) const { return getCellID(phiBin2Slice(iphi), iz); } protected: GPUd() int getNCells() const { return getNZBins() * getNPhiSlices(); } @@ -165,9 +183,6 @@ class MatLayerCyl : public o2::gpu::FlatObject // linearized cell ID from phi slice and z bin GPUd() int getCellID(int iphi, int iz) const { return iphi * getNZBins() + iz; } - // linearized cell ID from phi bin and z bin - GPUd() int getCellIDPhiBin(int iphi, int iz) const { return getCellID(phiBin2Slice(iphi), iz); } - // convert Phi (in 0:2pi convention) to PhiBinID GPUd() int getPhiBinID(float phi) const { diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h index 8c25c07c0ccc1..f2e8937ae8448 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h @@ -51,9 +51,11 @@ class MatLayerCylSet : public o2::gpu::FlatObject { public: - MatLayerCylSet() CON_DEFAULT; - ~MatLayerCylSet() CON_DEFAULT; - MatLayerCylSet(const MatLayerCylSet& src) CON_DELETE; +#ifndef GPUCA_GPUCODE + MatLayerCylSet() = default; + ~MatLayerCylSet() = default; + MatLayerCylSet(const MatLayerCylSet& src) = delete; +#endif GPUd() const MatLayerCylSetLayout* get() const { return reinterpret_cast(mFlatBufferPtr); } GPUd() MatLayerCylSetLayout* get() { return reinterpret_cast(mFlatBufferPtr); } @@ -71,7 +73,12 @@ class MatLayerCylSet : public o2::gpu::FlatObject #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version void print(bool data = false) const; void addLayer(float rmin, float rmax, float zmax, float dz, float drphi); - void populateFromTGeo(int ntrPerCel = 10); + /// Populate the LUT from TGeo or VecGeom. nThreads > 1 fills cells in parallel (one + /// TGeoNavigator per thread for ROOT; VecGeom navigation is thread-safe on its own); + /// nThreads < 0 takes the count from NTHREADS_MATBUD. VECGEOM requires O2 built against + /// TGeo2VecGeom, see GeometryManager::isVecGeomAvailable(). + void populateFromTGeo(int ntrPerCel = 10, int nThreads = -1, MatbudGeomBackend backend = MatbudGeomBackend::ROOT); + static int getNThreadsFromEnv(); void optimizePhiSlices(float maxRelDiff = 0.05); void dumpToTree(const std::string& outName = "matbudTree.root") const; @@ -79,8 +86,15 @@ class MatLayerCylSet : public o2::gpu::FlatObject static MatLayerCylSet* loadFromFile(const std::string& inpFName = "matbud.root"); static MatLayerCylSet* rectifyPtrFromFile(MatLayerCylSet* ptr); + // initializes internal voxel lookup + void initLayerVoxelLU(); + void flatten(); + MatLayerCyl& getLayer(int i) { return get()->mLayers[i]; } + MatLayerCylSet* extractCopy(float rmin, float rmax, float tol = 1e-3, const MatLayerCylSet* toAdd = nullptr) const; + void finalizeStructures(); + #endif // !GPUCA_ALIGPUCODE #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version @@ -89,11 +103,18 @@ class MatLayerCylSet : public o2::gpu::FlatObject // get material budget traversed on the line between point0 and point1 return getMatBudget(point0.X(), point0.Y(), point0.Z(), point1.X(), point1.Y(), point1.Z()); } + + void scaleLayersByID(int lrFrom, int lrTo, float factor, bool _x2x0 = true, bool _rho = true); + void scaleLayersByR(float rFrom, float rTo, float factor, bool _x2x0 = true, bool _rho = true); + #endif // !GPUCA_ALIGPUCODE GPUd() MatBudget getMatBudget(float x0, float y0, float z0, float x1, float y1, float z1) const; GPUd() int searchSegment(float val, int low = -1, int high = -1) const; + /// searches a layer based on r2 input, using a lookup table + GPUd() int searchLayerFast(float r2, int low = -1, int high = -1) const; + #ifndef GPUCA_GPUCODE //----------------------------------------------------------- std::size_t estimateFlatBufferSize() const; @@ -114,6 +135,14 @@ class MatLayerCylSet : public o2::gpu::FlatObject static constexpr size_t getBufferAlignmentBytes() { return 8; } #endif // !GPUCA_GPUCODE + static constexpr float LayerRMax = 500; // maximum value of R lookup (corresponds to last layer of MatLUT) + static constexpr float VoxelRDelta = 0.05; // voxel spacing for layer lookup; seems a natural choice - corresponding ~ to smallest spacing + static constexpr float InvVoxelRDelta = 1.f / VoxelRDelta; + static constexpr int NumVoxels = int(LayerRMax / VoxelRDelta); + + uint16_t mLayerVoxelLU[2 * NumVoxels]; //! helper structure to lookup a layer based on known radius (static dimension for easy copy to GPU) + bool mInitializedLayerVoxelLU = false; //! if the voxels have been initialized + ClassDefNV(MatLayerCylSet, 1); }; diff --git a/Detectors/Base/include/DetectorsBase/MaterialManager.h b/Detectors/Base/include/DetectorsBase/MaterialManager.h index 4448998ee3d33..b0de75c2d6c84 100644 --- a/Detectors/Base/include/DetectorsBase/MaterialManager.h +++ b/Detectors/Base/include/DetectorsBase/MaterialManager.h @@ -218,7 +218,7 @@ class MaterialManager std::unordered_map mDensityMap; void initDensityMap(); - float getDensity(std::string const& modname); + float getDensity(std::string const& modname, std::string const& matname); // Hide details by providing these private methods so it cannot happen that special settings // are applied as default settings by accident using a boolean flag diff --git a/Detectors/Base/include/DetectorsBase/O2Tessellated.h b/Detectors/Base/include/DetectorsBase/O2Tessellated.h new file mode 100644 index 0000000000000..7a1a3945c80d8 --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/O2Tessellated.h @@ -0,0 +1,150 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_BASE_O2TESSELLATED_ +#define ALICEO2_BASE_O2TESSELLATED_ + +#include "TGeoShape.h" +#include "TGeoBBox.h" +#include "TGeoVector3.h" +#include "TGeoTypedefs.h" +#include "TGeoTessellated.h" + +namespace o2 +{ +namespace base +{ + +class O2Tessellated : public TGeoBBox +{ + + public: + using Vertex_t = Tessellated::Vertex_t; + + private: + int fNfacets = 0; // Number of facets + int fNvert = 0; // Number of vertices + int fNseg = 0; // Number of segments + bool fDefined = false; //! Shape fully defined + bool fClosedBody = false; // The faces are making a closed body + + // for now separate vectors but might be better to group per face + std::vector fVertices; // List of vertices + std::vector fFacets; // List of facets + std::vector fOutwardNormals; // Vector of outward-facing normals (to be streamed !) + + std::multimap fVerticesMap; //! Temporary map used to deduplicate vertices + bool fIsClosed = false; //! to know if shape still needs closure/initialization + void* fBVH = nullptr; //! BVH acceleration structure for safety and navigation + + O2Tessellated(const O2Tessellated&) = delete; + O2Tessellated& operator=(const O2Tessellated&) = delete; + + // bvh helper functions + void BuildBVH(); + void CalculateNormals(); + + public: + // constructors + O2Tessellated() {} + O2Tessellated(const char* name, int nfacets = 0); + O2Tessellated(const char* name, const std::vector& vertices); + // from a TGeoTessellated + O2Tessellated(TGeoTessellated const&, bool check = false); + + // destructor + ~O2Tessellated() override {} + + void ComputeBBox() override; + void CloseShape(bool check = true, bool fixFlipped = true, bool verbose = true); + + bool AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2); + bool AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2, const Vertex_t& pt3); + bool AddFacet(int i1, int i2, int i3); + bool AddFacet(int i1, int i2, int i3, int i4); + int AddVertex(const Vertex_t& vert); + + bool FacetCheck(int ifacet) const; + Vertex_t FacetComputeNormal(int ifacet, bool& degenerated) const; + + int GetNfacets() const { return fFacets.size(); } + int GetNsegments() const { return fNseg; } + int GetNvertices() const { return fNvert; } + bool IsClosedBody() const { return fClosedBody; } + bool IsDefined() const { return fDefined; } + + const TGeoFacet& GetFacet(int i) const { return fFacets[i]; } + const Vertex_t& GetVertex(int i) const { return fVertices[i]; } + + int DistancetoPrimitive(int, int) override { return 99999; } + const TBuffer3D& GetBuffer3D(int reqSections, Bool_t localFrame) const override; + void GetMeshNumbers(int& nvert, int& nsegs, int& npols) const override; + int GetNmeshVertices() const override { return fNvert; } + void InspectShape() const override {} + TBuffer3D* MakeBuffer3D() const override; + void Print(Option_t* option = "") const override; + void SavePrimitive(std::ostream&, Option_t*) override {} + void SetPoints(double* points) const override; + void SetPoints(Float_t* points) const override; + void SetSegsAndPols(TBuffer3D& buff) const override; + void Sizeof3D() const override {} + + /// Resize and center the shape in a box of size maxsize + void ResizeCenter(double maxsize); + + /// Flip all facets + void FlipFacets() + { + for (auto facet : fFacets) + facet.Flip(); + } + + bool CheckClosure(bool fixFlipped = true, bool verbose = true); + + /// Reader from .obj format + static O2Tessellated* ImportFromObjFormat(const char* objfile, bool check = false, bool verbose = false); + + // navigation functions used by TGeoNavigator (attention: only the iact == 3 cases implemented for now) + Double_t DistFromOutside(const Double_t* point, const Double_t* dir, Int_t iact = 1, + Double_t step = TGeoShape::Big(), Double_t* safe = nullptr) const override; + Double_t DistFromInside(const Double_t* point, const Double_t* dir, Int_t iact = 1, Double_t step = TGeoShape::Big(), + Double_t* safe = nullptr) const override; + bool Contains(const Double_t* point) const override; + Double_t Safety(const Double_t* point, Bool_t in = kTRUE) const override; + void ComputeNormal(const Double_t* point, const Double_t* dir, Double_t* norm) const override; + + // these are trivial implementations, just for debugging + Double_t DistFromInside_Loop(const Double_t* point, const Double_t* dir) const; + Double_t DistFromOutside_Loop(const Double_t* point, const Double_t* dir) const; + bool Contains_Loop(const Double_t* point) const; + + Double_t Capacity() const override; + + private: + // a safety kernel used in multiple implementations + template + Double_t SafetyKernel(const Double_t* point, bool in, int* closest_facet_id = nullptr) const; + + // cached values for safety + mutable float mLast_x; + mutable float mLast_y; + mutable float mLast_z; + mutable float mCachedSafety; + mutable size_t cached_counter = 0; + mutable size_t call_counter = 0; + + ClassDefOverride(O2Tessellated, 1) // tessellated shape class +}; + +} // namespace base +} // namespace o2 + +#endif diff --git a/Detectors/Base/include/DetectorsBase/Propagator.h b/Detectors/Base/include/DetectorsBase/Propagator.h index 84cc4b5f30732..377094cc368b8 100644 --- a/Detectors/Base/include/DetectorsBase/Propagator.h +++ b/Detectors/Base/include/DetectorsBase/Propagator.h @@ -17,7 +17,6 @@ #define ALICEO2_BASE_PROPAGATOR_ #include "GPUCommonRtypes.h" -#include "GPUCommonArray.h" #include "CommonConstants/PhysicsConstants.h" #include "ReconstructionDataFormats/Track.h" #include "ReconstructionDataFormats/DCA.h" @@ -25,6 +24,7 @@ #include "DetectorsBase/MatLayerCylSet.h" #ifndef GPUCA_GPUCODE +#include #include #endif @@ -34,7 +34,7 @@ namespace parameters { class GRPObject; class GRPMagField; -} +} // namespace parameters namespace dataformats { @@ -45,7 +45,7 @@ namespace field { class MagFieldFast; class MagneticField; -} +} // namespace field namespace gpu { @@ -76,6 +76,10 @@ class PropagatorImpl value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool PropagateToXBxByBz(TrackParCov_t& track, TrackPar_t& linRef, value_type x, + value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool PropagateToXBxByBz(TrackPar_t& track, value_type x, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; @@ -84,6 +88,10 @@ class PropagatorImpl value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToX(TrackParCov_t& track, TrackPar_t& linRef, value_type x, value_type bZ, + value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToX(TrackPar_t& track, value_type x, value_type bZ, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; @@ -92,13 +100,40 @@ class PropagatorImpl GPUd() bool propagateTo(track_T& track, value_type x, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const { - return bzOnly ? propagateToX(track, x, getNominalBz(), maxSnp, maxStep, matCorr, tofInfo, signCorr) : PropagateToXBxByBz(track, x, maxSnp, maxStep, matCorr, tofInfo, signCorr); + return bzOnly ? propagateToX(track, x, getBz(track.getXYZGlo()), maxSnp, maxStep, matCorr, tofInfo, signCorr) : PropagateToXBxByBz(track, x, maxSnp, maxStep, matCorr, tofInfo, signCorr); + } + + GPUd() bool propagateToX(TrackParCov_t& track, TrackPar_t* linRef, value_type x, value_type bZ, + value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const + { + return linRef ? propagateToX(track, *linRef, x, bZ, maxSnp, maxStep, matCorr, tofInfo, signCorr) : propagateToX(track, x, bZ, maxSnp, maxStep, matCorr, tofInfo, signCorr); + } + + GPUd() bool PropagateToXBxByBz(TrackParCov_t& track, TrackPar_t* linRef, value_type x, + value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const + { + return linRef ? PropagateToXBxByBz(track, *linRef, x, maxSnp, maxStep, matCorr, tofInfo, signCorr) : PropagateToXBxByBz(track, x, maxSnp, maxStep, matCorr, tofInfo, signCorr); + } + + GPUd() bool propagateTo(TrackParCov_t& track, TrackPar_t* linRef, value_type x, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, + MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const + { + return bzOnly ? propagateToX(track, linRef, x, getBz(track.getXYZGlo()), maxSnp, maxStep, matCorr, tofInfo, signCorr) : PropagateToXBxByBz(track, linRef, x, maxSnp, maxStep, matCorr, tofInfo, signCorr); } template GPUd() bool propagateToAlphaX(track_T& track, value_type alpha, value_type x, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, int minSteps = 1, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToAlphaX(TrackParCov_t& track, TrackPar_t* linRef, value_type alpha, value_type x, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, int minSteps = 1, + MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + + template + GPUd() bool propagateToR(track_T& track, value_type r, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, + MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToDCA(const o2::dataformats::VertexBase& vtx, o2::track::TrackParametrizationWithError& track, value_type bZ, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, o2::dataformats::DCA* dcaInfo = nullptr, track::TrackLTIntegral* tofInfo = nullptr, @@ -111,12 +146,12 @@ class PropagatorImpl GPUd() bool propagateToDCA(const o2::math_utils::Point3D& vtx, o2::track::TrackParametrization& track, value_type bZ, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, - gpu::gpustd::array* dca = nullptr, track::TrackLTIntegral* tofInfo = nullptr, + std::array* dca = nullptr, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0, value_type maxD = 999.f) const; GPUd() bool propagateToDCABxByBz(const o2::math_utils::Point3D& vtx, o2::track::TrackParametrization& track, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, - gpu::gpustd::array* dca = nullptr, track::TrackLTIntegral* tofInfo = nullptr, + std::array* dca = nullptr, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0, value_type maxD = 999.f) const; PropagatorImpl(PropagatorImpl const&) = delete; @@ -126,16 +161,18 @@ class PropagatorImpl // Bz at the origin GPUd() void updateField(); - GPUd() value_type getNominalBz() const { return mBz; } + GPUd() value_type getNominalBz() const { return mNominalBz; } GPUd() void setTGeoFallBackAllowed(bool v) { mTGeoFallBackAllowed = v; } GPUd() bool isTGeoFallBackAllowed() const { return mTGeoFallBackAllowed; } GPUd() void setMatLUT(const o2::base::MatLayerCylSet* lut) { mMatLUT = lut; } GPUd() const o2::base::MatLayerCylSet* getMatLUT() const { return mMatLUT; } GPUd() void setGPUField(const o2::gpu::GPUTPCGMPolynomialField* field) { mGPUField = field; } GPUd() const o2::gpu::GPUTPCGMPolynomialField* getGPUField() const { return mGPUField; } - GPUd() void setBz(value_type bz) { mBz = bz; } + GPUd() void setNominalBz(value_type bz) { mNominalBz = bz; } + GPUd() bool hasMagFieldSet() const { return mField != nullptr; } - GPUd() void estimateLTFast(o2::track::TrackLTIntegral& lt, const o2::track::TrackParametrization& trc) const; + GPUd() value_type estimateLTFast(o2::track::TrackLTIntegral& lt, const o2::track::TrackParametrization& trc) const; + GPUd() float estimateLTIncrement(const o2::track::TrackParametrization& trc, const o2::math_utils::Point3D& postStart, const o2::math_utils::Point3D& posEnd) const; #ifndef GPUCA_GPUCODE static PropagatorImpl* Instance(bool uninitialized = false) @@ -144,9 +181,9 @@ class PropagatorImpl return &instance; } static int initFieldFromGRP(const o2::parameters::GRPMagField* grp, bool verbose = false); - static int initFieldFromGRP(const o2::parameters::GRPObject* grp, bool verbose = false); static int initFieldFromGRP(const std::string grpFileName = "", bool verbose = false); + static int initFieldFromGRP(float currL3, float currDip, bool uniform, bool verbose = false); #endif GPUd() MatBudget getMatBudget(MatCorrType corrType, const o2::math_utils::Point3D& p0, const o2::math_utils::Point3D& p1) const; @@ -155,6 +192,10 @@ class PropagatorImpl GPUd() void getFieldXYZ(const math_utils::Point3D xyz, double* bxyz) const; + GPUd() float getBz(const math_utils::Point3D xyz) const; + + GPUd() double getBz(const math_utils::Point3D xyz) const; + private: #ifndef GPUCA_GPUCODE PropagatorImpl(bool uninitialized = false); @@ -163,10 +204,12 @@ class PropagatorImpl static constexpr value_type Epsilon = 0.00001; // precision of propagation to X template GPUd() void getFieldXYZImpl(const math_utils::Point3D xyz, T* bxyz) const; + template + GPUd() T getBzImpl(const math_utils::Point3D xyz) const; const o2::field::MagFieldFast* mFieldFast = nullptr; ///< External fast field map (barrel only for the moment) o2::field::MagneticField* mField = nullptr; ///< External nominal field map - value_type mBz = 0; ///< nominal field + value_type mNominalBz = 0; ///< nominal field bool mTGeoFallBackAllowed = true; ///< allow fall back to TGeo if requested MatLUT is not available const o2::base::MatLayerCylSet* mMatLUT = nullptr; // externally set LUT diff --git a/Detectors/Base/include/DetectorsBase/Ray.h b/Detectors/Base/include/DetectorsBase/Ray.h index 1701e65c2d638..0b0c2f2904d27 100644 --- a/Detectors/Base/include/DetectorsBase/Ray.h +++ b/Detectors/Base/include/DetectorsBase/Ray.h @@ -49,7 +49,7 @@ class Ray GPUd() Ray() : mP{0.f}, mD{0.f}, mDistXY2(0.f), mDistXY2i(0.f), mDistXYZ(0.f), mXDxPlusYDy(0.f), mXDxPlusYDyRed(0.f), mXDxPlusYDy2(0.f), mR02(0.f), mR12(0.f) { } - GPUdDefault() ~Ray() CON_DEFAULT; + GPUdDefault() ~Ray() = default; #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version Ray(const math_utils::Point3D point0, const math_utils::Point3D point1); @@ -74,12 +74,12 @@ class Ray GPUd() float getDist() const { return mDistXYZ; } GPUd() float getDist(float deltaT) const { return mDistXYZ * (deltaT > 0 ? deltaT : -deltaT); } - // for debud only - float getPos(float t, int i) const { return mP[i] + t * mD[i]; } + // for debug only + GPUd() float getPos(float t, int i) const { return mP[i] + t * mD[i]; } GPUd() float getPhi(float t) const { - float p = o2::gpu::CAMath::ATan2(mP[1] + t * mD[1], mP[0] + t * mD[0]); + float p = o2::math_utils::fastATan2(mP[1] + t * mD[1], mP[0] + t * mD[0]); // instead of float p = o2::gpu::CAMath::ATan2(mP[1] + t * mD[1], mP[0] + t * mD[0]); o2::math_utils::bringTo02Pi(p); return p; } diff --git a/Detectors/Base/include/DetectorsBase/Stack.h b/Detectors/Base/include/DetectorsBase/Stack.h index 69d221000e493..479981a65477a 100644 --- a/Detectors/Base/include/DetectorsBase/Stack.h +++ b/Detectors/Base/include/DetectorsBase/Stack.h @@ -210,6 +210,9 @@ class Stack : public FairGenericStack /// query if a track is a direct **or** indirect daughter of a parentID /// if trackid is same as parentid it returns true bool isTrackDaughterOf(int /*trackid*/, int /*parentid*/) const; + /// query if a track originates, directly or indirectly, from a radioactive decay + /// only meaningful during transport, before selectTracks() remaps mother indices + bool isFromRadDecay(int trackid) const; bool isCurrentTrackDaughterOf(int parentid) const; @@ -348,6 +351,31 @@ inline int Stack::getMotherTrackId(int trackid) const return mParticles[entryinParticles].getMotherTrackId(); } +inline bool Stack::isFromRadDecay(int trackid) const +{ + // Check whether particle originates directly or indirectly from radioactive decay. + // Walks up the mother chain until a primary is reached. Only meaningful during + // transport, since selectTracks() later rewrites the mother indices in mParticles. + // + // Note that primaries are not kept in mParticles and that mTrackIDtoParticlesEntry + // is meaningless for them, so the chain has to be terminated on the trackID itself. + for (int id = trackid; id >= mNumberOfPrimaryParticles;) { + if (id >= static_cast(mTrackIDtoParticlesEntry.size())) { + return false; + } + const auto entry = mTrackIDtoParticlesEntry[id]; + if (entry < 0 || entry >= static_cast(mParticles.size())) { + return false; + } + const auto& part = mParticles[entry]; + if (part.getProcess() == kPRadDecay) { + return true; + } + id = part.getMotherTrackId(); + } + return false; +} + inline bool Stack::isCurrentTrackDaughterOf(int parentid) const { // if parentid is current primary the answer is certainly yes diff --git a/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h new file mode 100644 index 0000000000000..5ec85f1c14702 --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h @@ -0,0 +1,38 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TGeoGeometryUtils.h +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo + +#ifndef ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ +#define ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ + +class TGeoShape; +class TGeoTessellated; + +namespace o2 +{ +namespace base +{ + +/// A few utility functions to operate on TGeo geometries (transformations, printing, ...) +class TGeoGeometryUtils +{ + public: + ///< Transform any (primitive) TGeoShape to a tessellated representation + static TGeoTessellated* TGeoShapeToTGeoTessellated(TGeoShape const*); +}; + +} // namespace base +} // namespace o2 + +#endif diff --git a/Detectors/Base/include/DetectorsBase/VMCSeederService.h b/Detectors/Base/include/DetectorsBase/VMCSeederService.h new file mode 100644 index 0000000000000..5f8f70f48840f --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/VMCSeederService.h @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_VMC_SEEDERSERVICE_H +#define ALICEO2_VMC_SEEDERSERVICE_H + +class TVirtualMC; + +#include + +namespace o2 +{ +namespace base +{ + +// A simple (singleton) service to propagate random number seeding +// to VMC engines in a loosely-coupled, just-in-time way (without needing to compile against these libraries). +class VMCSeederService +{ + + public: + static VMCSeederService const& instance() + { + static VMCSeederService inst; + return inst; + } + + void setSeed() const; // will propagate seed to the VMC engines + + /// how often a seed was propagated; lets callers detect a silent no-op + unsigned long long getSeedCount() const { return mSeedCount; } + + typedef std::function SeederFcn; + + private: + VMCSeederService(); + void initSeederFunction(TVirtualMC const*); + + SeederFcn mSeederFcn; // the just-in-time compiled function talking to the VMC engines + mutable unsigned long long mSeedCount{0}; // number of setSeed() calls +}; + +} // namespace base +} // namespace o2 + +#endif diff --git a/Detectors/Base/src/BaseDPLDigitizer.cxx b/Detectors/Base/src/BaseDPLDigitizer.cxx index 173f3490ef560..3b3c5cfd477cb 100644 --- a/Detectors/Base/src/BaseDPLDigitizer.cxx +++ b/Detectors/Base/src/BaseDPLDigitizer.cxx @@ -16,6 +16,7 @@ #include #include #include +#include using namespace o2::base; @@ -54,6 +55,10 @@ void BaseDPLDigitizer::init(o2::framework::InitContext& ic) } } + // initialize the global ROOT random number generator (needed or not) + LOG(info) << "Initializing ROOT digitizer random with seed " << o2::conf::DigiParams::Instance().seed; + gRandom->SetSeed(o2::conf::DigiParams::Instance().seed); + // finally call specific init this->initDigitizerTask(ic); } diff --git a/Detectors/Base/src/CADGeometryUtils.cxx b/Detectors/Base/src/CADGeometryUtils.cxx new file mode 100644 index 0000000000000..84c1890d844b5 --- /dev/null +++ b/Detectors/Base/src/CADGeometryUtils.cxx @@ -0,0 +1,202 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DetectorsBase/CADGeometryUtils.h" +#include "DetectorsBase/MaterialManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::base +{ + +TGeoVolume* buildCADVolumeFromMacro(const std::string& macroFile, const std::string& instanceTag) +{ + if (macroFile.empty()) { + return nullptr; + } + auto expandedHookFileName = o2::utils::expandShellVarsInFileName(macroFile); + if (!std::filesystem::exists(expandedHookFileName)) { + LOG(error) << "External geometry macro " << expandedHookFileName << " does not exist"; + return nullptr; + } + + // We JIT the macro into a *unique* namespace per call. This is essential when several + // external geometries are present at the same time: every macro produced by + // O2_CADtoTGeo.py exports identically named symbols (build(), get_builder_hook_unchecked(), + // LoadFacets(), ...). Loading them all into the single global Cling scope would collide + // (the first definition wins and subsequent macros are silently ignored). By wrapping each + // macro body in its own namespace we keep the symbols separate. The preprocessor #include + // lines must stay at global scope, so we hoist them out of the namespace. + std::ifstream macroStream(expandedHookFileName, std::ios::in); + if (!macroStream.is_open()) { + LOG(error) << "Cannot open external geometry macro " << expandedHookFileName; + return nullptr; + } + std::string preamble; // #include (and other top-level preprocessor) lines -> global scope + std::string body; // everything else -> wrapped into a unique namespace + std::string line; + while (std::getline(macroStream, line)) { + auto firstNonSpace = line.find_first_not_of(" \t"); + if (firstNonSpace != std::string::npos && line[firstNonSpace] == '#') { + preamble += line + "\n"; + } else { + body += line + "\n"; + } + } + + // build a unique, valid C++ identifier for the namespace + static std::atomic instanceCounter{0}; + std::string ns = std::string("o2_cadgeom_") + instanceTag + "_" + std::to_string(instanceCounter++); + for (auto& c : ns) { + if (!std::isalnum(static_cast(c)) && c != '_') { + c = '_'; + } + } + + const std::string wrapped = preamble + "\nnamespace " + ns + " {\n" + body + "\n}\n"; + if (!gInterpreter->Declare(wrapped.c_str())) { + LOG(error) << "Failed to JIT external geometry macro " << expandedHookFileName; + return nullptr; + } + + // retrieve the builder hook from the unique namespace + const std::string globalName = "__" + ns + "_hook__"; + gROOT->ProcessLine(Form("std::function %s = %s::get_builder_hook_unchecked();", + globalName.c_str(), ns.c_str())); + auto global = gROOT->GetGlobal(globalName.c_str()); + if (!global) { + LOG(error) << "Could not retrieve geometry builder hook from macro " << expandedHookFileName; + return nullptr; + } + auto hook = *reinterpret_cast*>(global->GetAddress()); + LOG(info) << "CAD geometry hook initialized from file " << expandedHookFileName << " (namespace " << ns << ")"; + + auto top = hook(); + if (!top) { + LOG(error) << "CAD geometry macro " << expandedHookFileName << " did not return a top volume"; + } + return top; +} + +void remapCADMedia(TGeoVolume* top, const char* modulename) +{ + std::unordered_map medium_ptr_mapping; + std::unordered_set volumes_already_treated; + int counter = 1; + + // The transformer function + auto transform_media = [&](TGeoVolume* vol_) { + if (volumes_already_treated.find(vol_) != volumes_already_treated.end()) { + // this volume was already transformed + return; + } + volumes_already_treated.insert(vol_); + + if (dynamic_cast(vol_)) { + // do nothing for assemblies (they don't have a medium) + return; + } + + auto medium = vol_->GetMedium(); + if (!medium) { + return; + } + + auto iter = medium_ptr_mapping.find(medium); + if (iter != medium_ptr_mapping.end()) { + // This medium has already been transformed, so + // we just update the volume + vol_->SetMedium(iter->second); + return; + } else { + LOG(info) << "Transforming media with name " << medium->GetName() << " for volume " << vol_->GetName(); + + // we found a medium, not yet treated + auto curr_mat = medium->GetMaterial(); + auto& matmgr = o2::base::MaterialManager::Instance(); + + matmgr.Material(modulename, counter, curr_mat->GetName(), curr_mat->GetA(), curr_mat->GetZ(), curr_mat->GetDensity(), curr_mat->GetRadLen(), curr_mat->GetIntLen()); + // TGeo medium params are stored in a flat array with the following convention + // fParams[0] = isvol; + // fParams[1] = ifield; + // fParams[2] = fieldm; + // fParams[3] = tmaxfd; + // fParams[4] = stemax; + // fParams[5] = deemax; + // fParams[6] = epsil; + // fParams[7] = stmin; + const auto isvol = medium->GetParam(0); + const auto isxfld = medium->GetParam(1); + const auto sxmgmx = medium->GetParam(2); + const auto tmaxfd = medium->GetParam(3); + const auto stemax = medium->GetParam(4); + const auto deemax = medium->GetParam(5); + const auto epsil = medium->GetParam(6); + const auto stmin = medium->GetParam(7); + + matmgr.Medium(modulename, counter, medium->GetName(), counter, isvol, isxfld, sxmgmx, tmaxfd, stemax, deemax, epsil, stmin); + + // there will be new Material and Medium objects; fetch them + auto new_med = matmgr.getTGeoMedium(modulename, counter); + + // insert into cache + medium_ptr_mapping[medium] = new_med; + vol_->SetMedium(new_med); + counter++; + } + }; // end transformer lambda + + // a generic volume walker + std::function visit_volume; + visit_volume = [&](TGeoVolume* vol) -> void { + if (!vol) { + return; + } + + // call the transformer + transform_media(vol); + + // Recurse into daughters + const int nd = vol->GetNdaughters(); + for (int i = 0; i < nd; ++i) { + TGeoNode* node = vol->GetNode(i); + if (!node) { + continue; + } + TGeoVolume* child = node->GetVolume(); + if (!child) { + continue; + } + + visit_volume(child); + } + }; + + visit_volume(top); +} + +} // namespace o2::base diff --git a/Detectors/Base/src/CTFCoderBase.cxx b/Detectors/Base/src/CTFCoderBase.cxx index 4eb94180684bd..e29ca9e6d1afe 100644 --- a/Detectors/Base/src/CTFCoderBase.cxx +++ b/Detectors/Base/src/CTFCoderBase.cxx @@ -51,13 +51,13 @@ void CTFCoderBase::updateTimeDependentParams(ProcessingContext& pc, bool askTree setFirstTFOrbit(pc.services().get().firstTForbit); if (pc.services().get().globalRunNumberChanged) { // this params need to be queried only once if (mOpType == OpType::Decoder) { - pc.inputs().get("trigoffset"); // this is a configurable param + pc.inputs().get(mTrigOffsBinding); // this is a configurable param } if (mLoadDictFromCCDB) { if (askTree) { - pc.inputs().get("ctfdict"); // just to trigger the finaliseCCDB + pc.inputs().get(mDictBinding); // just to trigger the finaliseCCDB } else { - pc.inputs().get*>("ctfdict"); // just to trigger the finaliseCCDB + pc.inputs().get*>(mDictBinding); // just to trigger the finaliseCCDB } } } @@ -86,3 +86,12 @@ bool CTFCoderBase::isTreeDictionary(const void* buff) const } return found; } + +void CTFCoderBase::reportIRFrames() +{ + static bool repDone = false; + if (!repDone) { + LOGP(info, "IRFrames will be selected with shift {}, forward {} margin and backward {} margin (in BCs)", mIRFrameSelShift, mIRFrameSelMarginBwd, mIRFrameSelMarginFwd); + repDone = true; + } +} diff --git a/Detectors/Base/src/Detector.cxx b/Detectors/Base/src/Detector.cxx index e93295f1fc3f6..d2be9237f6f13 100644 --- a/Detectors/Base/src/Detector.cxx +++ b/Detectors/Base/src/Detector.cxx @@ -17,6 +17,7 @@ #include "DetectorsBase/MaterialManager.h" #include "DetectorsCommonDataFormats/DetID.h" #include "Field/MagneticField.h" +#include "Framework/TMessageSerializer.h" #include "TString.h" // for TString #include "TGeoManager.h" @@ -30,6 +31,7 @@ using namespace o2::base; using namespace o2::detectors; Float_t Detector::mDensityFactor = 1.0; +std::vector o2::base::Detector::sDetId2HitBitIndex{}; // initialize empty vector Detector::Detector() : FairDetector(), mMapMaterial(), mMapMedium() {} Detector::Detector(const char* name, Bool_t Active) @@ -169,6 +171,10 @@ void Detector::addAlignableVolumes() const LOG(warning) << "Alignable volumes are not yet defined for " << GetName(); } +void Detector::fillParallelWorld() const +{ +} + int Detector::registerSensitiveVolumeAndGetVolID(TGeoVolume const* vol) { // register this volume with FairRoot @@ -195,16 +201,19 @@ int Detector::registerSensitiveVolumeAndGetVolID(std::string const& name) #include #include #include -namespace o2 -{ -namespace base +namespace o2::base { // this goes into the source -void attachMessageBufferToParts(fair::mq::Parts& parts, fair::mq::Channel& channel, void* data, size_t size, - void (*free_func)(void* data, void* hint), void* hint) +void attachMessageBufferToParts(fair::mq::Parts& parts, fair::mq::Channel& channel, void* data, TClass* cl) { - std::unique_ptr message(channel.NewMessage(data, size, free_func, hint)); - parts.AddPart(std::move(message)); + auto msg = channel.Transport()->CreateMessage(4096, fair::mq::Alignment{64}); + // This will serialize the data directly into the message buffer, without any further + // buffer or copying. Notice how the message will have 8 bytes of header and then + // the serialized data as TBufferFile. In principle one could construct a serialized TMessage payload + // however I did not manage to get it to work for every case. + o2::framework::FairOutputTBuffer buffer(*msg); + o2::framework::TMessageSerializer::serialize(buffer, data, cl); + parts.AddPart(std::move(msg)); } void attachDetIDHeaderMessage(int id, fair::mq::Channel& channel, fair::mq::Parts& parts) { @@ -245,17 +254,14 @@ void* decodeShmCore(fair::mq::Parts& dataparts, int index, bool*& busy) void* decodeTMessageCore(fair::mq::Parts& dataparts, int index) { - class TMessageWrapper : public TMessage - { - public: - TMessageWrapper(void* buf, Int_t len) : TMessage(buf, len) { ResetBit(kIsOwner); } - ~TMessageWrapper() override = default; - }; auto rawmessage = std::move(dataparts.At(index)); - auto message = std::make_unique(rawmessage->GetData(), rawmessage->GetSize()); - return message.get()->ReadObjectAny(message.get()->GetClass()); + o2::framework::FairInputTBuffer buffer((char*)rawmessage->GetData(), rawmessage->GetSize()); + buffer.InitMap(); + auto* cl = buffer.ReadClass(); + buffer.SetBufferOffset(0); + buffer.ResetMap(); + return buffer.ReadObjectAny(cl); } -} // namespace base -} // namespace o2 +} // namespace o2::base ClassImp(o2::base::Detector); diff --git a/Detectors/Base/src/DetectorsBaseLinkDef.h b/Detectors/Base/src/DetectorsBaseLinkDef.h index f12f07321dcab..2da1d5bdfad15 100644 --- a/Detectors/Base/src/DetectorsBaseLinkDef.h +++ b/Detectors/Base/src/DetectorsBaseLinkDef.h @@ -24,8 +24,10 @@ #pragma link C++ class o2::base::GeometryManager + ; #pragma link C++ class o2::base::GeometryManager::MatBudgetExt + ; +#pragma link C++ enum o2::base::MatbudGeomBackend; #pragma link C++ class o2::base::MaterialManager + ; #pragma link C++ class o2::MaterialManagerParam + ; +#pragma link C++ class o2::GeometryManagerParam + ; #pragma link C++ class o2::base::SimFieldUtils + ; #pragma link C++ class o2::base::Ray + ; @@ -36,6 +38,11 @@ #pragma link C++ class o2::base::Aligner + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::base::Aligner> + ; +#pragma link C++ class o2::GlobalParams + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::GlobalParams> + ; + #pragma link C++ class o2::data::Stack + ; +#pragma link C++ class o2::base::O2Tessellated - ; + #endif diff --git a/Detectors/Base/src/GRPGeomHelper.cxx b/Detectors/Base/src/GRPGeomHelper.cxx index a171e42a37298..e7e5248493548 100644 --- a/Detectors/Base/src/GRPGeomHelper.cxx +++ b/Detectors/Base/src/GRPGeomHelper.cxx @@ -36,7 +36,7 @@ using namespace o2::base; using namespace o2::framework; namespace o2d = o2::dataformats; -GRPGeomRequest::GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, bool GRPMagField, bool askMatLUT, GeomRequest geom, std::vector& inputs, bool askOnce, bool needPropD) +GRPGeomRequest::GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, bool GRPMagField, bool askMatLUT, GeomRequest geom, std::vector& inputs, bool askOnce, bool needPropD, std::string detMaskString) : askGRPECS(GRPECS), askGRPLHCIF(GRPLHCIF), askGRPMagField(GRPMagField), askMatLUT(askMatLUT), askTime(orbitResetTime), askOnceAllButField(askOnce), needPropagatorD(needPropD) { if (geom == Aligned) { @@ -48,9 +48,12 @@ GRPGeomRequest::GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, } if (geom == Alignments) { askAlignments = true; + o2::detectors::DetID::mask_t algDetMask = DetID::getMask(detMaskString); for (auto id = DetID::First; id <= DetID::Last; id++) { - std::string binding = fmt::format("align{}", DetID::getName(id)); - addInput({binding, DetID::getDataOrigin(id), "ALIGNMENT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/Align", DetID::getName(id)))}, inputs); + if (algDetMask[id]) { + std::string binding = fmt::format("align{}", DetID::getName(id)); + addInput({binding, DetID::getDataOrigin(id), "ALIGNMENT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/Align", DetID::getName(id)))}, inputs); + } } } if (askMatLUT) { @@ -60,7 +63,7 @@ GRPGeomRequest::GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, addInput({"orbitReset", "CTP", "ORBITRESET", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/OrbitReset")}, inputs); } if (askGRPECS) { - addInput({"grpecs", "GLO", "GRPECS", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/GRPECS", true)}, inputs); // Run dependent !!! + addInput({"grpecs", "GLO", "GRPECS", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/GRPECS", 1)}, inputs); // Run dependent !!! } if (askGRPLHCIF) { addInput({"grplhcif", "GLO", "GRPLHCIF", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/GRPLHCIF")}, inputs); @@ -70,6 +73,22 @@ GRPGeomRequest::GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, } } +void GRPGeomRequest::requireAggregateRunInfo(std::vector& inputs) +{ + askAggregateRunInfo = true; + // require dependencies + if (!askGRPECS) { + askGRPECS = true; + addInput({"grpecs", "GLO", "GRPECS", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/GRPECS", 1)}, inputs); + } + if (!askTime) { + askTime = true; + addInput({"orbitReset", "CTP", "ORBITRESET", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/OrbitReset")}, inputs); + } + addInput({"RCTRunInfo", "RCT", "RunInfo", 0, Lifetime::Condition, ccdbParamSpec("RCT/Info/RunInformation", 2)}, inputs); + addInput({"CTPRunOrbit", "CTP", "RunOrbit", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/FirstRunOrbit")}, inputs); // TODO: should become run-depenendent (1) object +} + void GRPGeomRequest::addInput(const o2::framework::InputSpec&& isp, std::vector& inputs) { if (std::find(inputs.begin(), inputs.end(), isp) == inputs.end()) { @@ -121,8 +140,8 @@ bool GRPGeomHelper::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) return true; } if (mRequest->askTime && matcher == ConcreteDataMatcher("CTP", "ORBITRESET", 0)) { - mOrbitResetTimeMS = (*(std::vector*)obj)[0] / 1000; - LOG(info) << "orbit reset time updated to " << mOrbitResetTimeMS; + mOrbitResetTimeMUS = (*(std::vector*)obj)[0]; + LOG(info) << "orbit reset time updated to " << mOrbitResetTimeMUS; return true; } if (mRequest->askMatLUT && matcher == ConcreteDataMatcher("GLO", "MATLUT", 0)) { @@ -156,7 +175,7 @@ bool GRPGeomHelper::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) return false; } -void GRPGeomHelper::checkUpdates(ProcessingContext& pc) const +void GRPGeomHelper::checkUpdates(ProcessingContext& pc) { // request input just to trigger finaliseCCDB if there was an update @@ -215,13 +234,21 @@ void GRPGeomHelper::checkUpdates(ProcessingContext& pc) const if (mRequest->askAlignments) { for (auto id = DetID::First; id <= DetID::Last; id++) { std::string binding = fmt::format("align{}", DetID::getName(id)); - if (pc.inputs().isValid(binding.c_str())) { - pc.inputs().get*>(binding); + if (pc.inputs().getPos(binding.c_str()) < 0) { + continue; } else { - return; + pc.inputs().get*>(binding); } } } + if (mRequest->askAggregateRunInfo) { + const auto hmap = pc.inputs().get("RCTRunInfo"); // metadata only! + auto rl = o2::ccdb::BasicCCDBManager::getRunDuration(hmap); + auto ctfFirstRunOrbitVec = pc.inputs().get*>("CTPRunOrbit"); + mAggregatedRunInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(pc.services().get().runNumber, rl.first, rl.second, mOrbitResetTimeMUS, mGRPECS, ctfFirstRunOrbitVec.get()); + LOGP(debug, "Extracted AggregateRunInfo: runNumber:{}, sor:{}, eor:{}, orbitsPerTF:{}, orbitReset:{}, orbitSOR:{}, orbitEOR:{}", + mAggregatedRunInfo.runNumber, mAggregatedRunInfo.sor, mAggregatedRunInfo.eor, mAggregatedRunInfo.orbitsPerTF, mAggregatedRunInfo.orbitReset, mAggregatedRunInfo.orbitSOR, mAggregatedRunInfo.orbitEOR); + } } } diff --git a/Detectors/Base/src/GeometryManager.cxx b/Detectors/Base/src/GeometryManager.cxx index 885cff7a73588..225f21d8239a1 100644 --- a/Detectors/Base/src/GeometryManager.cxx +++ b/Detectors/Base/src/GeometryManager.cxx @@ -12,10 +12,11 @@ /// \file GeometryManager.cxx /// \brief Implementation of the GeometryManager class -#include // for LOG -#include // for TIter +#include // for LOG +#include // for TIter #include #include // for TGeoHMatrix +#include // for TGeoNavigator #include // for TGeoNode #include // for TGeoPhysicalNode, TGeoPNEntry #include @@ -24,10 +25,27 @@ #include #include "DetectorsBase/GeometryManager.h" +#include "DetectorsBase/GeometryManagerParam.h" #include "DetectorsCommonDataFormats/AlignParam.h" #include "CommonUtils/NameConf.h" #include "DetectorsBase/Aligner.h" +#ifdef O2_WITH_VECGEOM +#include "TGeo2VecGeom/RootGeoManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + using namespace o2::detectors; using namespace o2::base; @@ -256,7 +274,7 @@ bool GeometryManager::applyAlignment(const std::vector guard(sTGMutex); + // A caller that passes its own navigator owns it exclusively, so no locking is needed. A caller + // that passes none shares gGeoManager's current navigator and must still serialize. Deciding + // this from the argument keeps the choice local: it does not depend on -- and cannot be broken + // by -- process-global state such as TGeoManager::GetMaxThreads(). + std::unique_lock guard(sTGMutex, std::defer_lock); + if (!nav) { + guard.lock(); + nav = gGeoManager->GetCurrentNavigator(); + } // Initialize start point and direction - TGeoNode* currentnode = gGeoManager->InitTrack(startD, dir); + TGeoNode* currentnode = nav->InitTrack(startD, dir); if (!currentnode) { LOG(error) << "start point out of geometry: " << x0 << ':' << y0 << ':' << z0; return o2::base::MatBudget(); // return empty struct @@ -438,11 +467,11 @@ o2::base::MatBudget GeometryManager::meanMaterialBudget(float x0, float y0, floa // Locate next boundary within length without computing safety. // Propagate either with length (if no boundary found) or just cross boundary - gGeoManager->FindNextBoundaryAndStep(length, kFALSE); + nav->FindNextBoundaryAndStep(length, kFALSE); Double_t stepTot = 0.0; // Step made - Double_t step = gGeoManager->GetStep(); + Double_t step = nav->GetStep(); // If no boundary within proposed length, return current step data - if (!gGeoManager->IsOnBoundary()) { + if (!nav->IsOnBoundary()) { budStep.meanX2X0 = budStep.length / budStep.meanX2X0; return o2::base::MatBudget(budStep); } @@ -457,7 +486,7 @@ o2::base::MatBudget GeometryManager::meanMaterialBudget(float x0, float y0, floa if (nzero > 3) { // This means navigation has problems on one boundary // Try to cross by making a small step - const double* curPos = gGeoManager->GetCurrentPoint(); + const double* curPos = nav->GetCurrentPoint(); LOG(warning) << "Cannot cross boundary at (" << curPos[0] << ',' << curPos[1] << ',' << curPos[2] << ')'; budTotal.meanRho /= stepTot; budTotal.length = stepTot; @@ -471,14 +500,14 @@ o2::base::MatBudget GeometryManager::meanMaterialBudget(float x0, float y0, floa if (step >= length) { break; } - currentnode = gGeoManager->GetCurrentNode(); + currentnode = nav->GetCurrentNode(); if (!currentnode) { break; } length -= step; accountMaterial(currentnode->GetVolume()->GetMedium()->GetMaterial(), budStep); - gGeoManager->FindNextBoundaryAndStep(length, kFALSE); - step = gGeoManager->GetStep(); + nav->FindNextBoundaryAndStep(length, kFALSE); + step = nav->GetStep(); } budTotal.meanRho /= stepTot; budTotal.length = stepTot; @@ -523,3 +552,129 @@ void GeometryManager::loadGeometry(std::string_view simPrefix, bool applyMisalig applyMisalignent(applyMisalignment); } } + +#ifdef O2_WITH_VECGEOM + +namespace +{ +/// Converts the currently loaded TGeo geometry to VecGeom and sets up navigators, once per +/// process, the first time the VecGeom backend is requested. Not part of loadGeometry(), +/// which every job calls regardless of whether it ever uses the VecGeom backend. +void ensureVecGeomWorldBuilt() +{ + static std::once_flag onceFlag; + std::call_once(onceFlag, []() { + if (!gGeoManager) { + LOG(fatal) << "Cannot build VecGeom geometry: no TGeo geometry loaded (call GeometryManager::loadGeometry() first)"; + } + // Translate geometry and material pointers, then build acceleration structures. + tgeo2vecgeom::RootGeoManager::Instance().SetMaterialConversionHook([](TGeoMaterial const* m) { return (void*)m; }); + tgeo2vecgeom::RootGeoManager::Instance().SetFlattenAssemblies(true); + tgeo2vecgeom::RootGeoManager::Instance().LoadRootGeometry(); + + // Acceleration structures must be built before the navigators/locators reference them. + vecgeom::ABBoxManager::Instance().InitABBoxesForCompleteGeometry(); + // Builds a BVH per logical volume from the ABBoxes computed above. + vecgeom::BVHManager::Init(); + + // For each logical volume, set both a navigator (used for ComputeStep) and a matched + // level locator (used for point relocation after a boundary crossing via GlobalLocator); + // volumes with very few daughters are cheaper to brute-force than to accelerate. + for (auto& lvol : vecgeom::GeoManager::Instance().GetLogicalVolumesMap()) { + auto* vol = lvol.second; + if (vol->GetDaughtersp()->size() <= 2) { + vol->SetNavigator(vecgeom::NewSimpleNavigator<>::Instance()); + vol->SetLevelLocator(vecgeom::SimpleLevelLocator::GetInstance()); + } else { + vol->SetNavigator(vecgeom::BVHNavigator<>::Instance()); + vol->SetLevelLocator(vecgeom::BVHLevelLocator::GetInstance()); + } + } + }); +} +} // namespace + +//_____________________________________________________________________________________ +o2::base::MatBudget GeometryManager::vecGeomMaterialBudget(float x0, float y0, float z0, float x1, float y1, float z1) +{ + // Mean material budget between "0" and "1" via VecGeom's BVH-accelerated ray/boundary + // intersection, instead of TGeo. + ensureVecGeomWorldBuilt(); + + using Vector3D = vecgeom::Vector3D; + + double length, start[3] = {x0, y0, z0}; + double dir[3] = {x1 - x0, y1 - y0, z1 - z0}; + if ((length = dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]) < TGeoShape::Tolerance() * TGeoShape::Tolerance()) { + return o2::base::MatBudget(); // return empty struct + } + length = std::sqrt(length); + double invlen = 1. / length; + for (int i = 3; i--;) { + dir[i] *= invlen; + } + + thread_local static vecgeom::NavigationState* newnavstate = vecgeom::NavigationState::MakeInstance(vecgeom::GeoManager::Instance().getMaxDepth()); + thread_local static vecgeom::NavigationState* currnavstate = vecgeom::NavigationState::MakeInstance(vecgeom::GeoManager::Instance().getMaxDepth()); + thread_local static vecgeom::NavigationState* startCache = vecgeom::NavigationState::MakeInstance(vecgeom::GeoManager::Instance().getMaxDepth()); + thread_local static bool startCacheValid = false; + + Vector3D currPoint(x0, y0, z0); + Vector3D dirr(dir[0], dir[1], dir[2]); + constexpr double kPush = 1.E-6; // mimick the nudging of TGeo's FindNextBoundaryAndStep + auto world = vecgeom::GeoManager::Instance().GetWorld(); + o2::base::MatBudget budTot, budStep; + budStep.length = length; + + // Locate the starting volume, reusing the path from the previous call when still valid. + if (startCacheValid && !startCache->IsOutside()) { + startCache->CopyTo(currnavstate); + vecgeom::Transformation3D m; + currnavstate->TopMatrix(m); + vecgeom::GlobalLocator::RelocatePointFromPath(m.Transform(currPoint), *currnavstate); + } else { + currnavstate->Clear(); + vecgeom::GlobalLocator::LocateGlobalPoint(world, currPoint, *currnavstate, true); + } + if (currnavstate->IsOutside() || currnavstate->Top() == nullptr) { + LOG(error) << "start point out of geometry: " << x0 << ':' << y0 << ':' << z0; + startCacheValid = false; + return o2::base::MatBudget(); + } + currnavstate->CopyTo(startCache); + startCacheValid = true; + + double stepTot = 0.; + double remainingDist = length; + Int_t nzero = 0; + while (remainingDist > 1.E-10) { + auto* lvol = currnavstate->Top()->GetLogicalVolume(); + accountMaterial(static_cast(lvol->GetMaterialPtr()), budStep); + vecgeom::VNavigator const* navigator = lvol->GetNavigator(); + double step = static_cast(navigator->ComputeStepAndPropagatedState(currPoint, dirr, remainingDist, *currnavstate, *newnavstate)); + if (step < 2.E-10) { + nzero++; + } else { + nzero = 0; + } + if (nzero > 3) { + // This means navigation has problems on one boundary + LOG(warning) << "Cannot cross boundary at (" << currPoint[0] << ',' << currPoint[1] << ',' << currPoint[2] << ')'; + budTot.meanRho /= stepTot; + budTot.length = stepTot; + return o2::base::MatBudget(budTot); + } + + remainingDist -= step; + stepTot += step; + budTot.meanRho += step * budStep.meanRho; + budTot.meanX2X0 += step / budStep.meanX2X0; + currPoint = currPoint + (step + kPush) * dirr; + std::swap(currnavstate, newnavstate); + } + budTot.meanRho /= stepTot; + budTot.length = stepTot; + return o2::base::MatBudget(budTot); +} + +#endif // O2_WITH_VECGEOM diff --git a/Detectors/Base/src/GeometryManagerParam.cxx b/Detectors/Base/src/GeometryManagerParam.cxx new file mode 100644 index 0000000000000..e3c3ff3d687a7 --- /dev/null +++ b/Detectors/Base/src/GeometryManagerParam.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DetectorsBase/GeometryManagerParam.h" +O2ParamImpl(o2::GeometryManagerParam); diff --git a/Detectors/Base/src/GlobalParams.cxx b/Detectors/Base/src/GlobalParams.cxx new file mode 100644 index 0000000000000..b399a7d009c64 --- /dev/null +++ b/Detectors/Base/src/GlobalParams.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DetectorsBase/GlobalParams.h" +O2ParamImpl(o2::GlobalParams); diff --git a/Detectors/Base/src/MatLayerCyl.cxx b/Detectors/Base/src/MatLayerCyl.cxx index 1059074666952..c35293d07c10e 100644 --- a/Detectors/Base/src/MatLayerCyl.cxx +++ b/Detectors/Base/src/MatLayerCyl.cxx @@ -23,10 +23,12 @@ using namespace o2::base; using flatObject = o2::gpu::FlatObject; +#ifndef GPUCA_GPUCODE //________________________________________________________________________________ MatLayerCyl::MatLayerCyl() : mNZBins(0), mNPhiBins(0), mNPhiSlices(0), mZHalf(0.f), mRMin2(0.f), mRMax2(0.f), mDZ(0.f), mDZInv(0.f), mDPhi(0.f), mDPhiInv(0.f), mPhiBin2Slice(nullptr), mSliceCos(nullptr), mSliceSin(nullptr), mCells(nullptr) { } +#endif #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version //________________________________________________________________________________ @@ -81,7 +83,7 @@ void MatLayerCyl::initSegmentation(float rMin, float rMax, float zHalfSpan, int // int offs = 0; - o2::gpu::resizeArray(mPhiBin2Slice, 0, nphi, reinterpret_cast(mFlatBufferPtr + offs)); + o2::gpu::FlatObject::FlatObject::resizeArray(mPhiBin2Slice, 0, nphi, reinterpret_cast(mFlatBufferPtr + offs)); mNPhiSlices = mNPhiBins = nphi; for (int i = nphi; i--;) { @@ -90,24 +92,24 @@ void MatLayerCyl::initSegmentation(float rMin, float rMax, float zHalfSpan, int offs = alignSize(offs + nphi * sizeof(short), getBufferAlignmentBytes()); // account for alignment - o2::gpu::resizeArray(mSliceCos, 0, nphi, reinterpret_cast(mFlatBufferPtr + offs)); // in the beginning nslice = nphi + o2::gpu::FlatObject::resizeArray(mSliceCos, 0, nphi, reinterpret_cast(mFlatBufferPtr + offs)); // in the beginning nslice = nphi offs = alignSize(offs + nphi * sizeof(float), getBufferAlignmentBytes()); // account for alignment - o2::gpu::resizeArray(mSliceSin, 0, nphi, reinterpret_cast(mFlatBufferPtr + offs)); // in the beginning nslice = nphi + o2::gpu::FlatObject::resizeArray(mSliceSin, 0, nphi, reinterpret_cast(mFlatBufferPtr + offs)); // in the beginning nslice = nphi offs = alignSize(offs + nphi * sizeof(float), getBufferAlignmentBytes()); // account for alignment for (int i = nphi; i--;) { - mSliceCos[i] = std::cos(getPhiBinMin(i)); - mSliceSin[i] = std::sin(getPhiBinMin(i)); + mSliceCos[i] = o2::math_utils::cos(getPhiBinMin(i)); + mSliceSin[i] = o2::math_utils::sin(getPhiBinMin(i)); } - o2::gpu::resizeArray(mCells, 0, getNCells(), reinterpret_cast(mFlatBufferPtr + offs)); + o2::gpu::FlatObject::resizeArray(mCells, 0, getNCells(), reinterpret_cast(mFlatBufferPtr + offs)); mConstructionMask = InProgress; } //________________________________________________________________________________ -void MatLayerCyl::populateFromTGeo(int ntrPerCell) +void MatLayerCyl::populateFromTGeo(int ntrPerCell, MatbudGeomBackend backend) { /// populate layer with info extracted from TGeometry, using ntrPerCell test tracks per cell assert(mConstructionMask != Constructed); @@ -115,13 +117,13 @@ void MatLayerCyl::populateFromTGeo(int ntrPerCell) ntrPerCell = ntrPerCell > 1 ? ntrPerCell : 1; for (int iz = getNZBins(); iz--;) { for (int ip = getNPhiBins(); ip--;) { - populateFromTGeo(ip, iz, ntrPerCell); + populateFromTGeo(ip, iz, ntrPerCell, nullptr, backend); } } } //________________________________________________________________________________ -void MatLayerCyl::populateFromTGeo(int ip, int iz, int ntrPerCell) +void MatLayerCyl::populateFromTGeo(int ip, int iz, int ntrPerCell, TGeoNavigator* nav, MatbudGeomBackend backend) { /// populate cell with info extracted from TGeometry, using ntrPerCell test tracks per cell @@ -134,7 +136,16 @@ void MatLayerCyl::populateFromTGeo(int ip, int iz, int ntrPerCell) float dzt = zs > 0.f ? 0.25 * dz : -0.25 * dz; // to avoid 90 degree polar angle for (int isp = ntrPerCell; isp--;) { o2::math_utils::sincos(phmn + (isp + 0.5) * getDPhi() / ntrPerCell, sn, cs); - auto bud = o2::base::GeometryManager::meanMaterialBudget(rMin * cs, rMin * sn, zs - dzt, rMax * cs, rMax * sn, zs + dzt); + o2::base::MatBudget bud; + if (backend == MatbudGeomBackend::ROOT) { + bud = o2::base::GeometryManager::meanMaterialBudget(rMin * cs, rMin * sn, zs - dzt, rMax * cs, rMax * sn, zs + dzt, nav); + } else { +#ifdef O2_WITH_VECGEOM + bud = o2::base::GeometryManager::vecGeomMaterialBudget(rMin * cs, rMin * sn, zs - dzt, rMax * cs, rMax * sn, zs + dzt); +#else + LOG(fatal) << "MatbudGeomBackend::VECGEOM requested but O2 was built without VecGeom support (TGeo2VecGeom not found at configure time)"; +#endif + } if (bud.length > 0.) { meanRho += bud.length * bud.meanRho; meanX2X0 += bud.meanX2X0; // we store actually not X2X0 but 1./X0 @@ -193,20 +204,25 @@ void MatLayerCyl::optimizePhiSlices(float maxRelDiff) return; } int newSl = 0; + std::vector phi2SlNew(getNPhiBins()); + for (int i = 0; i < getNPhiBins(); i++) { + phi2SlNew[i] = mPhiBin2Slice[i]; + } for (int is = 1; is < getNPhiSlices(); is++) { if (!canMergePhiSlices(is - 1, is, maxRelDiff)) { newSl++; + } else { + mPhiBin2Slice[is] = mPhiBin2Slice[is - 1]; } - mPhiBin2Slice[is] = newSl; + phi2SlNew[is] = newSl; // new numbering } - LOG(info) << newSl + 1 << " slices out of " << getNPhiBins(); if (newSl + 1 == getNPhiSlices()) { return; } newSl = 0; int slMin = 0, slMax = 0, is = 0; while (is++ < getNPhiSlices()) { - while (is < getNPhiSlices() && mPhiBin2Slice[is] == newSl) { // select similar slices + while (is < getNPhiSlices() && phi2SlNew[is] == newSl) { // select similar slices slMax++; is++; } @@ -229,16 +245,19 @@ void MatLayerCyl::optimizePhiSlices(float maxRelDiff) newSl++; slMin = slMax = is; } + for (int i = 0; i < getNPhiBins(); i++) { + mPhiBin2Slice[i] = phi2SlNew[i]; + } mNPhiSlices = newSl; // relocate arrays to avoid spaces after optimization // mSliceCos pointer does not change, but sliceSin needs to be relocated auto offs = alignSize(newSl * sizeof(float), getBufferAlignmentBytes()); char* dst = ((char*)mSliceCos) + offs; // account for alignment - o2::gpu::resizeArray(mSliceSin, getNPhiBins(), newSl, reinterpret_cast(dst)); + o2::gpu::FlatObject::resizeArray(mSliceSin, getNPhiBins(), newSl, reinterpret_cast(dst)); // adjust mCells array dst = ((char*)mSliceSin) + offs; // account for alignment - o2::gpu::resizeArray(mCells, getNPhiBins() * getNZBins(), newSl * getNZBins(), reinterpret_cast(dst)); + o2::gpu::FlatObject::resizeArray(mCells, getNPhiBins() * getNZBins(), newSl * getNZBins(), reinterpret_cast(dst)); mFlatBufferSize = estimateFlatBufferSize(); LOG(info) << "Updated Nslices = " << getNPhiSlices(); } @@ -265,8 +284,8 @@ void MatLayerCyl::getMeanRMS(MatCell& mean, MatCell& rms) const rms.meanX2X0 /= nc; rms.meanRho -= mean.meanRho * mean.meanRho; rms.meanX2X0 -= mean.meanX2X0 * mean.meanX2X0; - rms.meanRho = rms.meanRho > 0.f ? std::sqrt(rms.meanRho) : 0.f; - rms.meanX2X0 = rms.meanX2X0 > 0.f ? std::sqrt(rms.meanX2X0) : 0.f; + rms.meanRho = rms.meanRho > 0.f ? o2::math_utils::sqrt(rms.meanRho) : 0.f; + rms.meanX2X0 = rms.meanX2X0 > 0.f ? o2::math_utils::sqrt(rms.meanX2X0) : 0.f; } //________________________________________________________________________________ @@ -303,15 +322,30 @@ void MatLayerCyl::flatten(char* newPtr) // make object flat: move all content to single internally allocated buffer assert(mConstructionMask == InProgress); fixPointers(mFlatBufferPtr, newPtr); - auto old = o2::gpu::resizeArray(mFlatBufferPtr, getFlatBufferSize(), getFlatBufferSize(), newPtr); + auto old = o2::gpu::FlatObject::resizeArray(mFlatBufferPtr, getFlatBufferSize(), getFlatBufferSize(), newPtr); delete[] old; mFlatBufferContainer = nullptr; mConstructionMask = Constructed; } +//________________________________________________________________________________ +void MatLayerCyl::scale(float factor, bool _x2x0, bool _rho) +{ + LOGP(info, "Scaling layer {:.3f} +#include #include "CommonUtils/TreeStreamRedirector.h" +#include +#include +#include +#include +#include +#include +#include +#include //#define _DBG_LOC_ // for local debugging only #endif // !GPUCA_ALIGPUCODE - +#undef NDEBUG using namespace o2::base; using flatObject = o2::gpu::FlatObject; @@ -41,7 +48,7 @@ void MatLayerCylSet::addLayer(float rmin, float rmax, float zmax, float dz, floa if (!nlr) { // book local storage auto sz = sizeof(MatLayerCylSetLayout); - o2::gpu::resizeArray(mFlatBufferContainer, 0, sz); + o2::gpu::FlatObject::resizeArray(mFlatBufferContainer, 0, sz); mFlatBufferPtr = mFlatBufferContainer; mFlatBufferSize = sz; //--------------???? @@ -55,7 +62,7 @@ void MatLayerCylSet::addLayer(float rmin, float rmax, float zmax, float dz, floa LOG(fatal) << "new layer overlaps with layer " << il; } } - auto* oldLayers = o2::gpu::resizeArray(get()->mLayers, nlr, nlr + 1); + auto* oldLayers = o2::gpu::FlatObject::resizeArray(get()->mLayers, nlr, nlr + 1); // dynamyc buffers of old layers were used in new ones, detach them for (int i = nlr; i--;) { oldLayers[i].clearInternalBufferPtr(); @@ -71,11 +78,32 @@ void MatLayerCylSet::addLayer(float rmin, float rmax, float zmax, float dz, floa } //________________________________________________________________________________ -void MatLayerCylSet::populateFromTGeo(int ntrPerCell) +int MatLayerCylSet::getNThreadsFromEnv() { - ///< populate layers, using ntrPerCell test tracks per cell + ///< number of threads requested via NTHREADS_MATBUD, or 1 if unset/invalid + const char* env = std::getenv("NTHREADS_MATBUD"); + if (!env) { + return 1; + } + int n = std::atoi(env); + if (n < 1) { + LOG(warning) << "Ignoring invalid NTHREADS_MATBUD=" << env; + return 1; + } + return n; +} + +//________________________________________________________________________________ +void MatLayerCylSet::populateFromTGeo(int ntrPerCell, int nThreads, MatbudGeomBackend backend) +{ + ///< populate layers, using ntrPerCell test tracks per cell. + ///< nThreads < 0 takes the number of threads from the NTHREADS_MATBUD environment variable. assert(mConstructionMask == InProgress); + if (backend == MatbudGeomBackend::VECGEOM && !GeometryManager::isVecGeomAvailable()) { + LOG(fatal) << "MatbudGeomBackend::VECGEOM requested but O2 was built without VecGeom support (TGeo2VecGeom not found at configure time)"; + } + int nlr = getNLayers(); if (!nlr) { LOG(error) << "The LUT is not yet initialized"; @@ -85,15 +113,126 @@ void MatLayerCylSet::populateFromTGeo(int ntrPerCell) LOG(error) << "The LUT is already populated"; return; } + + if (nThreads < 0) { + nThreads = getNThreadsFromEnv(); + } + + using Clock = std::chrono::steady_clock; + auto seconds = [](Clock::time_point a, Clock::time_point b) { + return std::chrono::duration(b - a).count(); + }; + + if (nThreads <= 1) { + for (int i = 0; i < nlr; i++) { + LOG(info) << "Populating with " << ntrPerCell << " trials Lr " << i; + get()->mLayers[i].print(); + } + const auto tSetupStart = Clock::now(); +#ifdef O2_WITH_VECGEOM + if (backend == MatbudGeomBackend::VECGEOM) { + // Trigger the lazy VecGeom world build/BVH init here so it counts as "setup" below, + // not as fill time for whichever cell happens first. + GeometryManager::vecGeomMaterialBudget(0.f, 0.f, 0.f, 0.f, 0.f, 1.f); + } +#endif + const auto tFillStart = Clock::now(); + for (int i = 0; i < nlr; i++) { + get()->mLayers[i].populateFromTGeo(ntrPerCell, backend); + } + const auto tFillEnd = Clock::now(); + finalizeStructures(); + LOG(info) << "LUT fill: 1 thread, setup " << seconds(tSetupStart, tFillStart) + << " s, cells " << seconds(tFillStart, tFillEnd) << " s"; + return; + } + + // Cells of all layers form one flat index range so that the load is balanced across + // threads even though layers differ a lot in cell count. layerOffsets[i] is the first + // flat index of layer i; a binary search maps a flat index back to (layer, iz, iphi). + std::vector layerOffsets(nlr + 1, 0); for (int i = 0; i < nlr; i++) { - printf("Populating with %d trials Lr %3d ", ntrPerCell, i); + LOG(info) << "Queuing " << ntrPerCell << " trials Lr " << i; get()->mLayers[i].print(); - get()->mLayers[i].populateFromTGeo(ntrPerCell); + const auto& lr = get()->mLayers[i]; + layerOffsets[i + 1] = layerOffsets[i] + size_t(lr.getNZBins()) * lr.getNPhiBins(); } + const size_t totalCells = layerOffsets[nlr]; + + const auto tSetupStart = Clock::now(); + + auto fillRange = [this, ntrPerCell, backend, &layerOffsets](const tbb::blocked_range& range, TGeoNavigator* nav) { + for (size_t idx = range.begin(); idx != range.end(); ++idx) { + auto it = std::upper_bound(layerOffsets.begin(), layerOffsets.end(), idx); + const int layerIdx = int(std::distance(layerOffsets.begin(), it)) - 1; + const size_t cellInLayer = idx - layerOffsets[layerIdx]; + auto& layer = this->get()->mLayers[layerIdx]; + const int nphi = layer.getNPhiBins(); + layer.populateFromTGeo(int(cellInLayer % nphi), int(cellInLayer / nphi), ntrPerCell, nav, backend); + } + }; + + Clock::time_point tFillStart, tFillEnd; + if (backend == MatbudGeomBackend::ROOT) { + // TGeo has to be told that several threads will navigate it, and each thread needs its own + // navigator. SetMaxThreads() is one-way -- TGeoManager has no API to return to + // single-threaded mode -- so we do not pretend to restore it; that is harmless because + // meanMaterialBudget() decides whether to lock from its own argument, not from this global. + // The navigators we book are ours, though, so those we do give back. + gGeoManager->SetMaxThreads(nThreads); + + tbb::enumerable_thread_specific threadNavigators( + []() { return gGeoManager->AddNavigator(); }); + + tFillStart = Clock::now(); + { + tbb::global_control threadControl(tbb::global_control::max_allowed_parallelism, nThreads); + tbb::parallel_for(tbb::blocked_range(0, totalCells), + [&fillRange, &threadNavigators](const tbb::blocked_range& range) { + fillRange(range, threadNavigators.local()); + }); + } + tFillEnd = Clock::now(); + + for (TGeoNavigator* nav : threadNavigators) { + gGeoManager->RemoveNavigator(nav); + } + } else { + // VecGeom navigation needs no per-thread navigator bookkeeping. Trigger the lazy world + // build/BVH init before tFillStart so it counts as "setup", not fill time. +#ifdef O2_WITH_VECGEOM + GeometryManager::vecGeomMaterialBudget(0.f, 0.f, 0.f, 0.f, 0.f, 1.f); +#endif + tFillStart = Clock::now(); + { + tbb::global_control threadControl(tbb::global_control::max_allowed_parallelism, nThreads); + tbb::parallel_for(tbb::blocked_range(0, totalCells), + [&fillRange](const tbb::blocked_range& range) { + fillRange(range, nullptr); + }); + } + tFillEnd = Clock::now(); + } + + finalizeStructures(); + const auto tEnd = Clock::now(); + + // Reported separately because only the middle term scales: the setup walks every volume + // in the geometry (TGeoManager::SetMaxThreads) and the teardown is serial by nature. + LOG(info) << "LUT fill: " << nThreads << " threads, setup " << seconds(tSetupStart, tFillStart) + << " s, cells " << seconds(tFillStart, tFillEnd) + << " s, finalize " << seconds(tFillEnd, tEnd) << " s"; +} + +//________________________________________________________________________________ +void MatLayerCylSet::finalizeStructures() +{ // build layer search structures + assert(mConstructionMask == InProgress); + int nlr = getNLayers(); int nR2Int = 2 * (nlr + 1); - o2::gpu::resizeArray(get()->mR2Intervals, 0, nR2Int); - o2::gpu::resizeArray(get()->mInterval2LrID, 0, nR2Int); + o2::gpu::FlatObject::resizeArray(get()->mR2Intervals, 0, nR2Int); + o2::gpu::FlatObject::resizeArray(get()->mInterval2LrID, 0, nR2Int); get()->mR2Intervals[0] = get()->mRMin2; get()->mR2Intervals[1] = get()->mRMax2; get()->mInterval2LrID[0] = 0; @@ -102,7 +241,7 @@ void MatLayerCylSet::populateFromTGeo(int ntrPerCell) for (int i = 1; i < nlr; i++) { const auto& lr = getLayer(i); - if (std::sqrt(lr.getRMin2()) > std::sqrt(get()->mR2Intervals[nRIntervals] + Ray::Tiny)) { + if (o2::math_utils::sqrt(lr.getRMin2()) > o2::math_utils::sqrt(get()->mR2Intervals[nRIntervals] + Ray::Tiny)) { // register gap get()->mInterval2LrID[nRIntervals] = -1; get()->mR2Intervals[++nRIntervals] = lr.getRMin2(); @@ -110,8 +249,8 @@ void MatLayerCylSet::populateFromTGeo(int ntrPerCell) get()->mInterval2LrID[nRIntervals] = i; get()->mR2Intervals[++nRIntervals] = lr.getRMax2(); } - delete[] o2::gpu::resizeArray(get()->mInterval2LrID, nR2Int, nRIntervals); // rebook with precise size - delete[] o2::gpu::resizeArray(get()->mR2Intervals, nR2Int, ++nRIntervals); // rebook with precise size + delete[] o2::gpu::FlatObject::resizeArray(get()->mInterval2LrID, nR2Int, nRIntervals); // rebook with precise size + delete[] o2::gpu::FlatObject::resizeArray(get()->mR2Intervals, nR2Int, ++nRIntervals); // rebook with precise size // } @@ -169,6 +308,29 @@ void MatLayerCylSet::writeToFile(const std::string& outFName) outf.Close(); } +void MatLayerCylSet::initLayerVoxelLU() +{ + if (mInitializedLayerVoxelLU) { + LOG(info) << "Layer voxel already initialized; Aborting"; + return; + } + LOG(info) << "Initializing voxel layer lookup"; + // do some check if voxels are dimensioned correctly + if (LayerRMax < get()->mRMax) { + LOG(fatal) << "Cannot initialized layer voxel lookup due to dimension problem (fix constants in MatLayerCylSet.h)"; + } + for (int voxel = 0; voxel < NumVoxels; ++voxel) { + // check the 2 extremes of this voxel "covering" + const auto lowerR = voxel * VoxelRDelta; + const auto upperR = lowerR + VoxelRDelta; + const auto lowerSegment = searchSegment(lowerR * lowerR); + const auto upperSegment = searchSegment(upperR * upperR); + mLayerVoxelLU[2 * voxel] = lowerSegment; + mLayerVoxelLU[2 * voxel + 1] = upperSegment; + } + mInitializedLayerVoxelLU = true; +} + //________________________________________________________________________________ MatLayerCylSet* MatLayerCylSet::loadFromFile(const std::string& inpFName) { @@ -182,7 +344,8 @@ MatLayerCylSet* MatLayerCylSet::loadFromFile(const std::string& inpFName) LOG(error) << "Failed to load mat.LUT from " << inpFName; return nullptr; } - return rectifyPtrFromFile(mb); + auto rptr = rectifyPtrFromFile(mb); + return rptr; } //________________________________________________________________________________ @@ -192,6 +355,7 @@ MatLayerCylSet* MatLayerCylSet::rectifyPtrFromFile(MatLayerCylSet* ptr) if (ptr && !ptr->get()) { ptr->fixPointers(); } + ptr->initLayerVoxelLU(); return ptr; } @@ -225,6 +389,33 @@ void MatLayerCylSet::print(bool data) const float(getFlatBufferSize()) / 1024 / 1024); } +//________________________________________________________________________________ +void MatLayerCylSet::scaleLayersByID(int lrFrom, int lrTo, float factor, bool _x2x0, bool _rho) +{ + lrFrom = std::max(0, std::min(lrFrom, get()->mNLayers - 1)); + lrTo = std::max(0, std::min(lrTo, get()->mNLayers - 1)); + int dir = lrFrom >= lrTo ? -1 : 1; + lrTo += dir; + for (int i = lrFrom; i != lrTo; i += dir) { + get()->mLayers[i].scale(factor, _x2x0, _rho); + } +} + +//________________________________________________________________________________ +void MatLayerCylSet::scaleLayersByR(float rFrom, float rTo, float factor, bool _x2x0, bool _rho) +{ + if (rFrom > rTo) { + std::swap(rFrom, rTo); + } + Ray ray(std::max(getRMin(), rFrom), 0., 0., std::min(getRMax(), rTo), 0., 0.); + short lmin, lmax; + if (!getLayersRange(ray, lmin, lmax)) { + LOGP(warn, "No layers found for {} < r < {}", rFrom, rTo); + return; + } + scaleLayersByID(lmin, lmax, factor, _x2x0, _rho); +} + #endif //!GPUCA_ALIGPUCODE #ifndef GPUCA_GPUCODE @@ -259,10 +450,11 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa while (lrID >= lmin) { // go from outside to inside const auto& lr = getLayer(lrID); int nphiSlices = lr.getNPhiSlices(); - int nc = ray.crossLayer(lr); + int nc = ray.crossLayer(lr); // determines how many crossings this ray has with this tubular layer for (int ic = nc; ic--;) { float cross1, cross2; ray.getCrossParams(ic, cross1, cross2); // tmax,tmin of crossing the layer + auto phi0 = ray.getPhi(cross1), phi1 = ray.getPhi(cross2), dPhi = phi0 - phi1; auto phiID = lr.getPhiSliceID(phi0), phiIDLast = lr.getPhiSliceID(phi1); // account for eventual wrapping around 0 @@ -275,6 +467,7 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa phiID += nphiSlices; } } + int stepPhiID = phiID > phiIDLast ? -1 : 1; bool checkMorePhi = true; auto tStartPhi = cross1, tEndPhi = 0.f; @@ -288,6 +481,12 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa if (tEndPhi == Ray::InvalidT) { break; // ray parallel to radial line, abandon check for phi bin change } + const auto tMarginPhi = 1.e-6f + 1.e-5f * (cross1 - cross2); + // if (!(tEndPhi >= cross2 - tMarginPhi) | !(tEndPhi <= cross1 + tMarginPhi)) { // use non-short-circuit | to reject eventual NANs + if (tEndPhi < cross2 - tMarginPhi || tEndPhi > cross1 + tMarginPhi) { + tEndPhi = cross2; + checkMorePhi = false; + } } auto zID = lr.getZBinID(ray.getZ(tStartPhi)); auto zIDLast = lr.getZBinID(ray.getZ(tEndPhi)); @@ -382,21 +581,40 @@ GPUd() bool MatLayerCylSet::getLayersRange(const Ray& ray, short& lmin, short& l return false; } int lmxInt, lmnInt; - lmxInt = rmax2 < getRMax2() ? searchSegment(rmax2, 0) : get()->mNRIntervals - 2; - lmnInt = rmin2 >= getRMin2() ? searchSegment(rmin2, 0, lmxInt + 1) : 0; + if (!mInitializedLayerVoxelLU) { + lmxInt = rmax2 < getRMax2() ? searchSegment(rmax2, 0) : get()->mNRIntervals - 2; + lmnInt = rmin2 >= getRMin2() ? searchSegment(rmin2, 0, lmxInt + 1) : 0; + } else { + lmxInt = rmax2 < getRMax2() ? searchLayerFast(rmax2, 0) : get()->mNRIntervals - 2; + lmnInt = rmin2 >= getRMin2() ? searchLayerFast(rmin2, 0, lmxInt + 1) : 0; + } + const auto* interval2LrID = get()->mInterval2LrID; lmax = interval2LrID[lmxInt]; lmin = interval2LrID[lmnInt]; // make sure lmnInt and/or lmxInt are not in the gap if (lmax < 0) { - lmax = interval2LrID[--lmxInt]; // rmax2 is in the gap, take highest layer below rmax2 + lmax = interval2LrID[lmxInt - 1]; // rmax2 is in the gap, take highest layer below rmax2 } if (lmin < 0) { - lmin = interval2LrID[++lmnInt]; // rmin2 is in the gap, take lowest layer above rmin2 + lmin = interval2LrID[lmnInt + 1]; // rmin2 is in the gap, take lowest layer above rmin2 } return lmin <= lmax; // valid if both are not in the same gap } +GPUd() int MatLayerCylSet::searchLayerFast(float r2, int low, int high) const +{ + // we can avoid the sqrt .. at the cost of more memory in the lookup + const auto index = 2 * int(o2::gpu::CAMath::Sqrt(r2) * InvVoxelRDelta); + const auto layersfirst = mLayerVoxelLU[index]; + const auto layerslast = mLayerVoxelLU[index + 1]; + if (layersfirst != layerslast) { + // this means the voxel is undecided and we revert to search + return searchSegment(r2, layersfirst, layerslast + 1); + } + return layersfirst; +} + GPUd() int MatLayerCylSet::searchSegment(float val, int low, int high) const { ///< search segment val belongs to. The val MUST be within the boundaries @@ -416,6 +634,7 @@ GPUd() int MatLayerCylSet::searchSegment(float val, int low, int high) const } mid = (low + high) >> 1; } + return mid; } @@ -428,14 +647,14 @@ void MatLayerCylSet::flatten() int sz = estimateFlatBufferSize(); // create new internal buffer with total size and copy data - delete[] o2::gpu::resizeArray(mFlatBufferContainer, mFlatBufferSize, sz); + delete[] o2::gpu::FlatObject::resizeArray(mFlatBufferContainer, mFlatBufferSize, sz); mFlatBufferPtr = mFlatBufferContainer; mFlatBufferSize = sz; int nLr = getNLayers(); auto offs = alignSize(sizeof(MatLayerCylSetLayout), getBufferAlignmentBytes()); // account for the alignment // move array of layer pointers to the flat array - auto* oldLayers = o2::gpu::resizeArray(get()->mLayers, nLr, nLr, (MatLayerCyl*)(mFlatBufferPtr + offs)); + auto* oldLayers = o2::gpu::FlatObject::resizeArray(get()->mLayers, nLr, nLr, (MatLayerCyl*)(mFlatBufferPtr + offs)); // dynamyc buffers of old layers were used in new ones, detach them for (int i = nLr; i--;) { oldLayers[i].clearInternalBufferPtr(); @@ -444,11 +663,11 @@ void MatLayerCylSet::flatten() offs = alignSize(offs + nLr * sizeof(MatLayerCyl), MatLayerCyl::getClassAlignmentBytes()); // account for the alignment // move array of R2 boundaries to the flat array - delete[] o2::gpu::resizeArray(get()->mR2Intervals, nLr + 1, nLr + 1, (float*)(mFlatBufferPtr + offs)); + delete[] o2::gpu::FlatObject::resizeArray(get()->mR2Intervals, nLr + 1, nLr + 1, (float*)(mFlatBufferPtr + offs)); offs = alignSize(offs + (nLr + 1) * sizeof(float), getBufferAlignmentBytes()); // account for the alignment // move array of R2 boundaries to the flat array - delete[] o2::gpu::resizeArray(get()->mInterval2LrID, nLr, nLr, (int*)(mFlatBufferPtr + offs)); + delete[] o2::gpu::FlatObject::resizeArray(get()->mInterval2LrID, nLr, nLr, (int*)(mFlatBufferPtr + offs)); offs = alignSize(offs + nLr * sizeof(int), getBufferAlignmentBytes()); // account for the alignment for (int il = 0; il < nLr; il++) { @@ -525,3 +744,55 @@ void MatLayerCylSet::fixPointers(char* oldPtr, char* newPtr, bool newPtrValid) } } #endif // !GPUCA_GPUCODE + +#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version + +MatLayerCylSet* MatLayerCylSet::extractCopy(float rmin, float rmax, float tolerance, const MatLayerCylSet* addTo) const +{ + // extract layers in the covering rmin-rmax range. If addTo is provided, simply substitute its layers by those from this + if (addTo && addTo->getNLayers() != getNLayers()) { + LOGP(fatal, "addTo has {} layers, this has {}", addTo->getNLayers(), getNLayers()); + } + Ray ray(std::max(getRMin(), rmin), 0., 0., std::min(getRMax(), rmax), 0., 0.); + short lmin, lmax; + if (!getLayersRange(ray, lmin, lmax)) { + LOGP(warn, "No layers found for {} < r < {}", rmin, rmax); + return nullptr; + } + LOGP(info, "Will extract layers {}:{} (out of {} layers) for {} < r < {}", lmin, lmax, getNLayers(), rmin, rmax); + MatLayerCylSet* copy = new MatLayerCylSet(); + int lrCount = 0, lrCounOld = 0, lrCountTot = 0; + auto addLr = [copy, &lrCountTot](const MatLayerCyl& lr) { + float drphi = lr.getDPhi() * (lr.getRMin() + lr.getRMax()) / 2. * 0.999; + copy->addLayer(lr.getRMin(), lr.getRMax(), lr.getZMax(), lr.getDZ(), drphi); + auto& lrNew = copy->getLayer(lrCountTot++); + for (int iz = 0; iz < lrNew.getNZBins(); iz++) { + for (int ip = 0; ip < lrNew.getNPhiBins(); ip++) { + lrNew.getCellPhiBin(ip, iz).set(lr.getCellPhiBin(ip, iz)); + } + } + }; + if (addTo) { + for (int il = 0; il < lmin; il++) { + addLr(addTo->getLayer(il)); + lrCounOld++; + } + } + for (int il = lmin; il <= lmax; il++) { + addLr(getLayer(il)); + lrCount++; + } + if (addTo) { + for (int il = lmax + 1; il < getNLayers(); il++) { + addLr(addTo->getLayer(il)); + lrCounOld++; + } + } + copy->finalizeStructures(); + copy->optimizePhiSlices(tolerance); + copy->flatten(); + LOGP(info, "Added layers {}:{} for {}second << " from material match"; + } + return iter->second; } - return o2::conf::SimMaterialParams::Instance().globalDensityFactor; + // density on module level + iter = mDensityMap.find(modname); + if (iter != mDensityMap.end()) { + if (debug) { + LOG(info) << "MatManager - " << modname << "/" << matname << " : applying density " << iter->second << " from module match"; + } + return iter->second; + } + // global factor + const auto global = o2::conf::SimMaterialParams::Instance().globalDensityFactor; + if (debug && global != 1.0) { + LOG(info) << "MatManager - " << modname << "/" << matname << " : applying global density " << iter->second; + } + return global; } void MaterialManager::Material(const char* modname, Int_t imat, const char* name, Float_t a, Float_t z, Float_t dens, Float_t radl, Float_t absl, Float_t* buf, Int_t nwbuf) { TString uniquename = modname; - auto densityFactor = getDensity(modname); + auto densityFactor = getDensity(modname, name); + uniquename.Append("_"); uniquename.Append(name); if (TVirtualMC::GetMC()) { @@ -173,7 +202,7 @@ void MaterialManager::Mixture(const char* modname, Int_t imat, const char* name, Int_t nlmat, Float_t* wmat) { TString uniquename = modname; - auto densityFactor = getDensity(modname); + auto densityFactor = getDensity(modname, name); uniquename.Append("_"); uniquename.Append(name); diff --git a/Detectors/Base/src/O2Tessellated.cxx b/Detectors/Base/src/O2Tessellated.cxx new file mode 100644 index 0000000000000..5c52625841868 --- /dev/null +++ b/Detectors/Base/src/O2Tessellated.cxx @@ -0,0 +1,1531 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel 2026 + +// An implementation of TGeoTessellated augmented with efficient navigation functions. +// Asked for integration into ROOT here https://github.com/root-project/root/pull/21045 +// Will be deleted once we get this from ROOT. + +#include +#include + +#include "TGeoManager.h" +#include "TGeoMatrix.h" +#include "TGeoVolume.h" +#include "TVirtualGeoPainter.h" +#include "DetectorsBase/O2Tessellated.h" +#include "TBuffer3D.h" +#include "TBuffer3DTypes.h" +#include "TMath.h" +#include "TBuffer.h" + +#include +#include + +// THIS IS THIRD PARTY CODE (TO BE PUT IN ROOT) WHICH DOES NOT NEED TO ADHERE TO OUR LINTING +// NOLINTBEGIN + +// include the Third-party BVH headers +#include "bvh2_third_party.h" +// some kernels on top of BVH +#include "bvh2_extra_kernels.h" + +#include +#include + +using namespace o2::base; +ClassImp(O2Tessellated); + +using Vertex_t = Tessellated::Vertex_t; + +//////////////////////////////////////////////////////////////////////////////// +/// Compact consecutive equal vertices + +int TGeoFacet::CompactFacet(Vertex_t* vert, int nvertices) +{ + // Compact the common vertices and return new facet + if (nvertices < 2) + return nvertices; + int nvert = nvertices; + int i = 0; + while (i < nvert) { + if (vert[(i + 1) % nvert] == vert[i]) { + // shift last vertices left by one element + for (int j = i + 2; j < nvert; ++j) + vert[j - 1] = vert[j]; + nvert--; + } + i++; + } + return nvert; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check if a connected neighbour facet has compatible normal + +bool TGeoFacet::IsNeighbour(const TGeoFacet& other, bool& flip) const +{ + + // Find a connecting segment + bool neighbour = false; + int line1[2], line2[2]; + int npoints = 0; + for (int i = 0; i < fNvert; ++i) { + auto ivert = fIvert[i]; + // Check if the other facet has the same vertex + for (int j = 0; j < other.GetNvert(); ++j) { + if (ivert == other[j]) { + line1[npoints] = i; + line2[npoints] = j; + if (++npoints == 2) { + neighbour = true; + bool order1 = line1[1] == line1[0] + 1; + bool order2 = line2[1] == (line2[0] + 1) % other.GetNvert(); + flip = (order1 == order2); + return neighbour; + } + } + } + } + return neighbour; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Constructor. In case nfacets is zero, it is user's responsibility to +/// call CloseShape once all faces are defined. + +O2Tessellated::O2Tessellated(const char* name, int nfacets) : TGeoBBox(name, 0, 0, 0) +{ + fNfacets = nfacets; + if (nfacets) + fFacets.reserve(nfacets); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Constructor providing directly the array of vertices. Facets have to be added +/// providing vertex indices rather than coordinates. + +O2Tessellated::O2Tessellated(const char* name, const std::vector& vertices) : TGeoBBox(name, 0, 0, 0) +{ + fVertices = vertices; + fNvert = fVertices.size(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Construct from TGeoTessellated + +O2Tessellated::O2Tessellated(TGeoTessellated const& tsl, bool check) : TGeoBBox(tsl.GetName(), 0, 0, 0) +{ + fNfacets = tsl.GetNfacets(); + fNvert = tsl.GetNvertices(); + fNseg = tsl.GetNsegments(); + + // copy facet and vertex done + fVertices.reserve(fNvert); + fFacets.reserve(fNfacets); + for (int i = 0; i < fNfacets; ++i) { + fFacets.push_back(tsl.GetFacet(i)); + } + for (int i = 0; i < fNvert; ++i) { + fVertices.push_back(tsl.GetVertex(i)); + } + // finish remaining structures + CloseShape(check); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add a vertex checking for duplicates, returning the vertex index + +int O2Tessellated::AddVertex(Vertex_t const& vert) +{ + constexpr double tolerance = 1.e-10; + auto vertexHash = [&](Vertex_t const& vertex) { + // Compute hash for the vertex + long hash = 0; + // helper function to generate hash from integer numbers + auto hash_combine = [](long seed, const long value) { + return seed ^ (std::hash{}(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); + }; + for (int i = 0; i < 3; i++) { + // use tolerance to generate int with the desired precision from a real number for hashing + hash = hash_combine(hash, std::roundl(vertex[i] / tolerance)); + } + return hash; + }; + + auto hash = vertexHash(vert); + bool isAdded = false; + int ivert = -1; + // Get the compatible vertices + auto range = fVerticesMap.equal_range(hash); + for (auto it = range.first; it != range.second; ++it) { + ivert = it->second; + if (fVertices[ivert] == vert) { + isAdded = true; + break; + } + } + if (!isAdded) { + ivert = fVertices.size(); + fVertices.push_back(vert); + fVerticesMap.insert(std::make_pair(hash, ivert)); + } + return ivert; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a triangular facet from vertex positions in absolute coordinates + +bool O2Tessellated::AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + + Vertex_t vert[3]; + vert[0] = pt0; + vert[1] = pt1; + vert[2] = pt2; + int nvert = TGeoFacet::CompactFacet(vert, 3); + if (nvert < 3) { + Error("AddFacet", "Triangular facet at index %d degenerated. Not adding.", GetNfacets()); + return false; + } + int ind[3]; + for (auto i = 0; i < 3; ++i) + ind[i] = AddVertex(vert[i]); + fNseg += 3; + fFacets.emplace_back(ind[0], ind[1], ind[2]); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a triangular facet from indices of vertices + +bool O2Tessellated::AddFacet(int i0, int i1, int i2) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + if (fVertices.empty()) { + Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName()); + return false; + } + + fNseg += 3; + fFacets.emplace_back(i0, i1, i2); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a quadrilateral facet from vertex positions in absolute coordinates + +bool O2Tessellated::AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2, const Vertex_t& pt3) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + Vertex_t vert[4]; + vert[0] = pt0; + vert[1] = pt1; + vert[2] = pt2; + vert[3] = pt3; + int nvert = TGeoFacet::CompactFacet(vert, 4); + if (nvert < 3) { + Error("AddFacet", "Quadrilateral facet at index %d degenerated. Not adding.", GetNfacets()); + return false; + } + + int ind[4]; + for (auto i = 0; i < nvert; ++i) + ind[i] = AddVertex(vert[i]); + fNseg += nvert; + if (nvert == 3) + fFacets.emplace_back(ind[0], ind[1], ind[2]); + else + fFacets.emplace_back(ind[0], ind[1], ind[2], ind[3]); + + if (fNfacets > 0 && GetNfacets() == fNfacets) + CloseShape(false); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a quadrilateral facet from indices of vertices + +bool O2Tessellated::AddFacet(int i0, int i1, int i2, int i3) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + if (fVertices.empty()) { + Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName()); + return false; + } + + fNseg += 4; + fFacets.emplace_back(i0, i1, i2, i3); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Compute normal for a given facet + +Vertex_t O2Tessellated::FacetComputeNormal(int ifacet, bool& degenerated) const +{ + // Compute normal using non-zero segments + constexpr double kTolerance = 1.e-20; + auto const& facet = fFacets[ifacet]; + int nvert = facet.GetNvert(); + degenerated = true; + Vertex_t normal; + for (int i = 0; i < nvert - 1; ++i) { + Vertex_t e1 = fVertices[facet[i + 1]] - fVertices[facet[i]]; + if (e1.Mag2() < kTolerance) + continue; + for (int j = i + 1; j < nvert; ++j) { + Vertex_t e2 = fVertices[facet[(j + 1) % nvert]] - fVertices[facet[j]]; + if (e2.Mag2() < kTolerance) + continue; + normal = Vertex_t::Cross(e1, e2); + // e1 and e2 may be colinear + if (normal.Mag2() < kTolerance) + continue; + normal.Normalize(); + degenerated = false; + break; + } + if (!degenerated) + break; + } + return normal; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check validity of facet + +bool O2Tessellated::FacetCheck(int ifacet) const +{ + constexpr double kTolerance = 1.e-10; + auto const& facet = fFacets[ifacet]; + int nvert = facet.GetNvert(); + bool degenerated = true; + FacetComputeNormal(ifacet, degenerated); + if (degenerated) { + std::cout << "Facet: " << ifacet << " is degenerated\n"; + return false; + } + + // Compute surface area + double surfaceArea = 0.; + for (int i = 1; i < nvert - 1; ++i) { + Vertex_t e1 = fVertices[facet[i]] - fVertices[facet[0]]; + Vertex_t e2 = fVertices[facet[i + 1]] - fVertices[facet[0]]; + surfaceArea += 0.5 * Vertex_t::Cross(e1, e2).Mag(); + } + if (surfaceArea < kTolerance) { + std::cout << "Facet: " << ifacet << " has zero surface area\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Close the shape: calculate bounding box and compact vertices + +void O2Tessellated::CloseShape(bool check, bool fixFlipped, bool verbose) +{ + if (fIsClosed && fBVH) { + return; + } + // Compute bounding box + fDefined = true; + fNvert = fVertices.size(); + fNfacets = fFacets.size(); + ComputeBBox(); + + BuildBVH(); + if (fOutwardNormals.size() == 0) { + CalculateNormals(); + } else { + // short check if the normal container is of correct size + if (fOutwardNormals.size() != fFacets.size()) { + std::cerr << "Inconsistency in normal container"; + } + } + fIsClosed = true; + + // Cleanup the vertex map + std::multimap().swap(fVerticesMap); + + if (fVertices.size() > 0) { + if (!check) + return; + + // Check facets + for (auto i = 0; i < fNfacets; ++i) + FacetCheck(i); + + fClosedBody = CheckClosure(fixFlipped, verbose); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check closure of the solid and check/fix flipped normals + +bool O2Tessellated::CheckClosure(bool fixFlipped, bool verbose) +{ + int* nn = new int[fNfacets]; + bool* flipped = new bool[fNfacets]; + bool hasorphans = false; + bool hasflipped = false; + for (int i = 0; i < fNfacets; ++i) { + nn[i] = 0; + flipped[i] = false; + } + + for (int icrt = 0; icrt < fNfacets; ++icrt) { + // all neighbours checked? + if (nn[icrt] >= fFacets[icrt].GetNvert()) + continue; + for (int i = icrt + 1; i < fNfacets; ++i) { + bool isneighbour = fFacets[icrt].IsNeighbour(fFacets[i], flipped[i]); + if (isneighbour) { + if (flipped[icrt]) + flipped[i] = !flipped[i]; + if (flipped[i]) + hasflipped = true; + nn[icrt]++; + nn[i]++; + if (nn[icrt] == fFacets[icrt].GetNvert()) + break; + } + } + if (nn[icrt] < fFacets[icrt].GetNvert()) + hasorphans = true; + } + + if (hasorphans && verbose) { + Error("Check", "Tessellated solid %s has following not fully connected facets:", GetName()); + for (int icrt = 0; icrt < fNfacets; ++icrt) { + if (nn[icrt] < fFacets[icrt].GetNvert()) + std::cout << icrt << " (" << fFacets[icrt].GetNvert() << " edges, " << nn[icrt] << " neighbours)\n"; + } + } + fClosedBody = !hasorphans; + int nfixed = 0; + if (hasflipped) { + if (verbose) + Warning("Check", "Tessellated solid %s has following facets with flipped normals:", GetName()); + for (int icrt = 0; icrt < fNfacets; ++icrt) { + if (flipped[icrt]) { + if (verbose) + std::cout << icrt << "\n"; + if (fixFlipped) { + fFacets[icrt].Flip(); + nfixed++; + } + } + } + if (nfixed && verbose) + Info("Check", "Automatically flipped %d facets to match first defined facet", nfixed); + } + delete[] nn; + delete[] flipped; + + return !hasorphans; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Compute bounding box + +void O2Tessellated::ComputeBBox() +{ + const double kBig = TGeoShape::Big(); + double vmin[3] = {kBig, kBig, kBig}; + double vmax[3] = {-kBig, -kBig, -kBig}; + for (const auto& facet : fFacets) { + for (int i = 0; i < facet.GetNvert(); ++i) { + for (int j = 0; j < 3; ++j) { + vmin[j] = TMath::Min(vmin[j], fVertices[facet[i]].operator[](j)); + vmax[j] = TMath::Max(vmax[j], fVertices[facet[i]].operator[](j)); + } + } + } + fDX = 0.5 * (vmax[0] - vmin[0]); + fDY = 0.5 * (vmax[1] - vmin[1]); + fDZ = 0.5 * (vmax[2] - vmin[2]); + for (int i = 0; i < 3; ++i) + fOrigin[i] = 0.5 * (vmax[i] + vmin[i]); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Returns numbers of vertices, segments and polygons composing the shape mesh. + +void O2Tessellated::GetMeshNumbers(int& nvert, int& nsegs, int& npols) const +{ + nvert = fNvert; + nsegs = fNseg; + npols = GetNfacets(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Creates a TBuffer3D describing *this* shape. +/// Coordinates are in local reference frame. + +TBuffer3D* O2Tessellated::MakeBuffer3D() const +{ + const int nvert = fNvert; + const int nsegs = fNseg; + const int npols = GetNfacets(); + auto buff = new TBuffer3D(TBuffer3DTypes::kGeneric, nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols); + if (buff) { + SetPoints(buff->fPnts); + SetSegsAndPols(*buff); + } + return buff; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Prints basic info + +void O2Tessellated::Print(Option_t*) const +{ + std::cout << "=== Tessellated shape " << GetName() << " having " << GetNvertices() << " vertices and " + << GetNfacets() << " facets\n"; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fills TBuffer3D structure for segments and polygons. + +void O2Tessellated::SetSegsAndPols(TBuffer3D& buff) const +{ + const int c = GetBasicColor(); + int* segs = buff.fSegs; + int* pols = buff.fPols; + + int indseg = 0; // segment internal data index + int indpol = 0; // polygon internal data index + int sind = 0; // segment index + for (const auto& facet : fFacets) { + auto nvert = facet.GetNvert(); + pols[indpol++] = c; + pols[indpol++] = nvert; + for (auto j = 0; j < nvert; ++j) { + int k = (j + 1) % nvert; + // segment made by next consecutive points + segs[indseg++] = c; + segs[indseg++] = facet[j]; + segs[indseg++] = facet[k]; + // add segment to current polygon and increment segment index + pols[indpol + nvert - j - 1] = sind++; + } + indpol += nvert; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill tessellated points to an array. + +void O2Tessellated::SetPoints(double* points) const +{ + int ind = 0; + for (const auto& vertex : fVertices) { + vertex.CopyTo(&points[ind]); + ind += 3; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill tessellated points in float. + +void O2Tessellated::SetPoints(Float_t* points) const +{ + int ind = 0; + for (const auto& vertex : fVertices) { + points[ind++] = vertex.x(); + points[ind++] = vertex.y(); + points[ind++] = vertex.z(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Resize the shape by scaling vertices within maxsize and center to origin + +void O2Tessellated::ResizeCenter(double maxsize) +{ + using Vector3_t = Vertex_t; + + if (!fDefined) { + Error("ResizeCenter", "Not all faces are defined"); + return; + } + Vector3_t origin(fOrigin[0], fOrigin[1], fOrigin[2]); + double maxedge = TMath::Max(TMath::Max(fDX, fDY), fDZ); + double scale = maxsize / maxedge; + for (size_t i = 0; i < fVertices.size(); ++i) { + fVertices[i] = scale * (fVertices[i] - origin); + } + fOrigin[0] = fOrigin[1] = fOrigin[2] = 0; + fDX *= scale; + fDY *= scale; + fDZ *= scale; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fills a static 3D buffer and returns a reference. + +const TBuffer3D& O2Tessellated::GetBuffer3D(int reqSections, Bool_t localFrame) const +{ + static TBuffer3D buffer(TBuffer3DTypes::kGeneric); + + FillBuffer3D(buffer, reqSections, localFrame); + + const int nvert = fNvert; + const int nsegs = fNseg; + const int npols = GetNfacets(); + + if (reqSections & TBuffer3D::kRawSizes) { + if (buffer.SetRawSizes(nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols)) { + buffer.SetSectionsValid(TBuffer3D::kRawSizes); + } + } + if ((reqSections & TBuffer3D::kRaw) && buffer.SectionsValid(TBuffer3D::kRawSizes)) { + SetPoints(buffer.fPnts); + if (!buffer.fLocalFrame) { + TransformPoints(buffer.fPnts, buffer.NbPnts()); + } + + SetSegsAndPols(buffer); + buffer.SetSectionsValid(TBuffer3D::kRaw); + } + + return buffer; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Reads a single tessellated solid from an .obj file. + +O2Tessellated* O2Tessellated::ImportFromObjFormat(const char* objfile, bool check, bool verbose) +{ + using std::vector, std::string, std::ifstream, std::stringstream, std::endl; + + vector vertices; + vector sfacets; + + struct FacetInd_t { + int i0 = -1; + int i1 = -1; + int i2 = -1; + int i3 = -1; + int nvert = 0; + FacetInd_t(int a, int b, int c) + { + i0 = a; + i1 = b; + i2 = c; + nvert = 3; + }; + FacetInd_t(int a, int b, int c, int d) + { + i0 = a; + i1 = b; + i2 = c; + i3 = d; + nvert = 4; + }; + }; + + vector facets; + // List of geometric vertices, with (x, y, z [,w]) coordinates, w is optional and defaults to 1.0. + // struct vtx_t { double x = 0; double y = 0; double z = 0; double w = 1; }; + + // Texture coordinates in u, [,v ,w]) coordinates, these will vary between 0 and 1. v, w are optional and default to + // 0. + // struct tex_t { double u; double v; double w; }; + + // List of vertex normals in (x,y,z) form; normals might not be unit vectors. + // struct vn_t { double x; double y; double z; }; + + // Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement + // struct vp_t { double u; double v; double w; }; + + // Faces are defined using lists of vertex, texture and normal indices which start at 1. + // Polygons such as quadrilaterals can be defined by using more than three vertex/texture/normal indices. + // f v1//vn1 v2//vn2 v3//vn3 ... + + // Records starting with the letter "l" specify the order of the vertices which build a polyline. + // l v1 v2 v3 v4 v5 v6 ... + + string line; + int ind[4] = {0}; + ifstream file(objfile); + if (!file.is_open()) { + ::Error("O2Tessellated::ImportFromObjFormat", "Unable to open %s", objfile); + return nullptr; + } + + while (getline(file, line)) { + stringstream ss(line); + string tag; + + // We ignore everything which is not a vertex or a face + if (line.rfind('v', 0) == 0 && line.rfind("vt", 0) != 0 && line.rfind("vn", 0) != 0 && line.rfind("vn", 0) != 0) { + // Decode the vertex + double pos[4] = {0, 0, 0, 1}; + ss >> tag >> pos[0] >> pos[1] >> pos[2] >> pos[3]; + vertices.emplace_back(pos[0] * pos[3], pos[1] * pos[3], pos[2] * pos[3]); + } + + else if (line.rfind('f', 0) == 0) { + // Decode the face + ss >> tag; + string word; + sfacets.clear(); + while (ss >> word) + sfacets.push_back(word); + if (sfacets.size() > 4 || sfacets.size() < 3) { + ::Error("O2Tessellated::ImportFromObjFormat", "Detected face having unsupported %zu vertices", + sfacets.size()); + return nullptr; + } + int nvert = 0; + for (auto& sword : sfacets) { + stringstream ssword(sword); + string token; + getline(ssword, token, '/'); // just need the vertex index, which is the first token + // Convert string token to integer + + ind[nvert++] = stoi(token) - 1; + if (ind[nvert - 1] < 0) { + ::Error("O2Tessellated::ImportFromObjFormat", "Unsupported relative vertex index definition in %s", + objfile); + return nullptr; + } + } + if (nvert == 3) + facets.emplace_back(ind[0], ind[1], ind[2]); + else + facets.emplace_back(ind[0], ind[1], ind[2], ind[3]); + } + } + + int nvertices = (int)vertices.size(); + int nfacets = (int)facets.size(); + if (nfacets < 3) { + ::Error("O2Tessellated::ImportFromObjFormat", "Not enough faces detected in %s", objfile); + return nullptr; + } + + string sobjfile(objfile); + if (verbose) + std::cout << "Read " << nvertices << " vertices and " << nfacets << " facets from " << sobjfile << endl; + + auto tsl = new O2Tessellated(sobjfile.erase(sobjfile.find_last_of('.')).c_str(), vertices); + + for (int i = 0; i < nfacets; ++i) { + auto facet = facets[i]; + if (facet.nvert == 3) + tsl->AddFacet(facet.i0, facet.i1, facet.i2); + else + tsl->AddFacet(facet.i0, facet.i1, facet.i2, facet.i3); + } + tsl->CloseShape(check, true, verbose); + tsl->Print(); + return tsl; +} + +// implementation of some geometry helper functions in anonymous namespace +namespace +{ + +using Vertex_t = Tessellated::Vertex_t; +// The classic Moeller-Trumbore ray triangle-intersection kernel: +// - Compute triangle edges e1, e2 +// - Compute determinant det +// - Reject parallel rays +// - Compute barycentric coordinates u, v +// - Compute ray parameter t +double rayTriangle(const Vertex_t& orig, const Vertex_t& dir, const Vertex_t& v0, const Vertex_t& v1, + const Vertex_t& v2, double rayEPS = 1e-8) +{ + constexpr double EPS = 1e-8; + const double INF = std::numeric_limits::infinity(); + Vertex_t e1{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]}; + Vertex_t e2{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]}; + auto p = Vertex_t::Cross(dir, e2); + auto det = e1.Dot(p); + if (std::abs(det) <= EPS) { + return INF; + } + + Vertex_t tvec{orig[0] - v0[0], orig[1] - v0[1], orig[2] - v0[2]}; + auto invDet = 1.0 / det; + auto u = tvec.Dot(p) * invDet; + if (u < 0.0 || u > 1.0) { + return INF; + } + auto q = Vertex_t::Cross(tvec, e1); + auto v = dir.Dot(q) * invDet; + if (v < 0.0 || u + v > 1.0) { + return INF; + } + auto t = e2.Dot(q) * invDet; + return (t > rayEPS) ? t : INF; +} + +template +struct Vec3f { + T x, y, z; +}; + +template +inline Vec3f operator-(const Vec3f& a, const Vec3f& b) +{ + return {a.x - b.x, a.y - b.y, a.z - b.z}; +} + +template +inline Vec3f cross(const Vec3f& a, const Vec3f& b) +{ + return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; +} + +template +inline T dot(const Vec3f& a, const Vec3f& b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +// Kernel to get closest/shortest distance between a point and a triangl (a,b,c). +// Performed by default in float since Safety can be approximate. +// Project point onto triangle plane +// If projection lies inside → distance to plane +// Otherwise compute min distance to the three edges +// Return squared distance +template +T pointTriangleDistSq(const Vec3f& p, const Vec3f& a, const Vec3f& b, const Vec3f& c) +{ + // Edges + Vec3f ab = b - a; + Vec3f ac = c - a; + Vec3f ap = p - a; + + auto d1 = dot(ab, ap); + auto d2 = dot(ac, ap); + if (d1 <= T(0.0) && d2 <= T(0.0)) { + return dot(ap, ap); // barycentric (1,0,0) + } + + Vec3f bp = p - b; + auto d3 = dot(ab, bp); + auto d4 = dot(ac, bp); + if (d3 >= T(0.0) && d4 <= d3) { + return dot(bp, bp); // (0,1,0) + } + + T vc = d1 * d4 - d3 * d2; + if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) { + T v = d1 / (d1 - d3); + Vec3f proj = {a.x + v * ab.x, a.y + v * ab.y, a.z + v * ab.z}; + Vec3f d = p - proj; + return dot(d, d); // edge AB + } + + Vec3f cp = p - c; + T d5 = dot(ab, cp); + T d6 = dot(ac, cp); + if (d6 >= T(0.0f) && d5 <= d6) { + return dot(cp, cp); // (0,0,1) + } + + T vb = d5 * d2 - d1 * d6; + if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) { + T w = d2 / (d2 - d6); + Vec3f proj = {a.x + w * ac.x, a.y + w * ac.y, a.z + w * ac.z}; + Vec3f d = p - proj; + return dot(d, d); // edge AC + } + + T va = d3 * d6 - d5 * d4; + if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) { + T w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + Vec3f proj = {b.x + w * (c.x - b.x), b.y + w * (c.y - b.y), b.z + w * (c.z - b.z)}; + Vec3f d = p - proj; + return dot(d, d); // edge BC + } + + // Inside face region + T denom = T(1.0f) / (va + vb + vc); + T v = vb * denom; + T w = vc * denom; + + Vec3f proj = {a.x + ab.x * v + ac.x * w, a.y + ab.y * v + ac.y * w, a.z + ab.z * v + ac.z * w}; + + Vec3f d = p - proj; + return dot(d, d); +} + +template +inline Vec3f normalize(const Vec3f& v) +{ + T len2 = dot(v, v); + if (len2 == T(0.0f)) { + std::cerr << "Degnerate triangle. Cannot determine normal"; + return {0, 0, 0}; + } + T invLen = T(1.0f) / std::sqrt(len2); + return {v.x * invLen, v.y * invLen, v.z * invLen}; +} + +template +inline Vec3f triangleNormal(const Vec3f& a, const Vec3f& b, const Vec3f& c) +{ + const Vec3f e1 = b - a; + const Vec3f e2 = c - a; + return normalize(cross(e1, e2)); +} + +} // end anonymous namespace + +//////////////////////////////////////////////////////////////////////////////// +/// DistFromOutside + +Double_t O2Tessellated::DistFromOutside(const Double_t* point, const Double_t* dir, Int_t /*iact*/, Double_t stepmax, + Double_t* /*safe*/) const +{ + // use the BVH intersector in combination with leaf ray-triangle testing + double local_step = Big(); // we need this otherwise the lambda get's confused + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return -1.; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // let's do very quick checks against the top node + const auto topnode_bbox = mybvh->get_root().get_bbox(); + if ((-point[0] + topnode_bbox.min[0]) > stepmax) { + return Big(); + } + if ((-point[1] + topnode_bbox.min[1]) > stepmax) { + return Big(); + } + if ((-point[2] + topnode_bbox.min[2]) > stepmax) { + return Big(); + } + if ((point[0] - topnode_bbox.max[0]) > stepmax) { + return Big(); + } + if ((point[1] - topnode_bbox.max[1]) > stepmax) { + return Big(); + } + if ((point[2] - topnode_bbox.max[2]) > stepmax) { + return Big(); + } + + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(dir[0], dir[1], dir[2]), // direction + 0.0f, // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + Vertex_t dir_v{dir[0], dir[1], dir[2]}; + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + const auto& facet = fFacets[objectid]; + const auto& n = fOutwardNormals[objectid]; + + // quick normal test. Coming from outside, the dot product must be negative + if (n.Dot(dir_v) > 0.) { + continue; + } + + auto thisdist = rayTriangle(Vertex_t(point[0], point[1], point[2]), dir_v, + fVertices[facet[0]], fVertices[facet[1]], fVertices[facet[2]], 0.); + + if (thisdist < local_step) { + local_step = thisdist; + } + } + return false; // go on after this + }); + + return local_step; +} + +//////////////////////////////////////////////////////////////////////////////// +/// DistFromOutside + +Double_t O2Tessellated::DistFromInside(const Double_t* point, const Double_t* dir, Int_t /*iact*/, Double_t /*stepmax*/, + Double_t* /*safe*/) const +{ + // use the BVH intersector in combination with leaf ray-triangle testing + double local_step = Big(); // we need this otherwise the lambda get's confused + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return -1.; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(dir[0], dir[1], dir[2]), // direction + 0., // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + Vertex_t dir_v{dir[0], dir[1], dir[2]}; + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + auto facet = fFacets[objectid]; + const auto& n = fOutwardNormals[objectid]; + + // Only exiting surfaces are relevant (from inside--> dot product must be positive) + if (n.Dot(dir_v) <= 0.) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = + rayTriangle(Vertex_t{point[0], point[1], point[2]}, dir_v, v0, v1, v2, 0.); + if (t < local_step) { + local_step = t; + } + } + return false; // go on after this + }); + + return local_step; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Capacity + +Double_t O2Tessellated::Capacity() const +{ + // For explanation of the following algorithm see: + // https://en.wikipedia.org/wiki/Polyhedron#Volume + // http://wwwf.imperial.ac.uk/~rn/centroid.pdf + + double vol = 0.0; + for (size_t i = 0; i < fFacets.size(); ++i) { + auto& facet = fFacets[i]; + auto a = fVertices[facet[0]]; + auto b = fVertices[facet[1]]; + auto c = fVertices[facet[2]]; + vol += + a[0] * (b[1] * c[2] - b[2] * c[1]) + b[0] * (c[1] * a[2] - c[2] * a[1]) + c[0] * (a[1] * b[2] - a[2] * b[1]); + } + return vol / 6.0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// BuildBVH + +void O2Tessellated::BuildBVH() +{ + using Scalar = float; + using BBox = bvh::v2::BBox; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + + // helper determining axis aligned bounding box from a facet; + auto GetBoundingBox = [this](TGeoFacet const& facet) { +#ifndef NDEBUG + const auto nvertices = facet.GetNvert(); + assert(nvertices == 3); // for now only triangles +#endif + const auto& v1 = fVertices[facet[0]]; + const auto& v2 = fVertices[facet[1]]; + const auto& v3 = fVertices[facet[2]]; + BBox bbox; + bbox.min[0] = std::min(std::min(v1[0], v2[0]), v3[0]) - 0.001f; + bbox.min[1] = std::min(std::min(v1[1], v2[1]), v3[1]) - 0.001f; + bbox.min[2] = std::min(std::min(v1[2], v2[2]), v3[2]) - 0.001f; + bbox.max[0] = std::max(std::max(v1[0], v2[0]), v3[0]) + 0.001f; + bbox.max[1] = std::max(std::max(v1[1], v2[1]), v3[1]) + 0.001f; + bbox.max[2] = std::max(std::max(v1[2], v2[2]), v3[2]) + 0.001f; + return bbox; + }; + + // we need bounding boxes enclosing the primitives and centers of primitives + // (replaced here by centers of bounding boxes) to build the bvh + std::vector bboxes; + std::vector centers; + + // loop over all the triangles/Facets; + int nd = fFacets.size(); + for (int i = 0; i < nd; ++i) { + auto& facet = fFacets[i]; + + // fetch the bounding box of this node and add to the vector of bounding boxes + (bboxes).push_back(GetBoundingBox(facet)); + centers.emplace_back((bboxes).back().get_center()); + } + + // check if some previous object is registered and delete if necessary + if (fBVH) { + delete (Bvh*)fBVH; + fBVH = nullptr; + } + + // create the bvh + typename bvh::v2::DefaultBuilder::Config config; + config.quality = bvh::v2::DefaultBuilder::Quality::High; + auto bvh = bvh::v2::DefaultBuilder::build(bboxes, centers, config); + auto bvhptr = new Bvh; + *bvhptr = std::move(bvh); // copy structure + fBVH = (void*)(bvhptr); + + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Contains + +bool O2Tessellated::Contains(Double_t const* point) const +{ + // we do the parity test + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return false; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // let's do very quick checks against the top node + if (!TGeoBBox::Contains(point)) { + return false; + } + + // An arbitrary test direction. + // Doesn't need to be normalized and probes all normals. Also ensuring to be skewed somewhat + // without evident symmetries. + Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757}; + + double local_step = Big(); + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(test_dir[0], test_dir[1], test_dir[2]), // direction + 0.0f, // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + size_t crossings = 0; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + auto& facet = fFacets[objectid]; + + // for the parity test, we probe all crossing surfaces + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(Vertex_t(point[0], point[1], point[2]), + test_dir, v0, v1, v2, 0.); + + if (t != std::numeric_limits::infinity()) { + ++crossings; + } + } + return false; + }); + + return crossings & 1; +} + +namespace +{ + +// Helper classes/structs used for priority queue - BVH traversal +// structure keeping cost (value) for a BVH index +struct BVHPrioElement { + size_t bvh_node_id; + float value; +}; + +// A priority queue for BVHPrioElement with an additional clear method +// for quick reset. We intentionally derive from std::priority_queue here to expose a +// clear() convenience method via access to the protected container `c`. +// This is internal, non-polymorphic code and relies on standard-library +// implementation details that are stable across supported platforms. +template +class BVHPrioQueue : public std::priority_queue, Comparator> +{ + public: + using std::priority_queue, + Comparator>::priority_queue; // constructor inclusion + + // convenience method to quickly clear/reset the queue (instead of having to pop one by one) + void clear() { this->c.clear(); } +}; + +} // namespace + +/// a reusable safety kernel, which optionally returns the closest face +template +inline Double_t O2Tessellated::SafetyKernel(const Double_t* point, bool in, int* closest_facet_id) const +{ + // This is the classic traversal/pruning of a BVH based on priority queue search + + float smallest_safety_sq = TGeoShape::Big(); + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + + // testpoint object in float for quick BVH interaction + Vec3 testpoint(point[0], point[1], point[2]); + + auto currnode = mybvh->nodes[0]; // we start from the top BVH node + // we do a quick check on the top node (in case we are outside shape) + bool outside_top = false; + if (!in) { + outside_top = !bvh::v2::extra::contains(currnode.get_bbox(), testpoint); + if (outside_top) { + const auto safety_sq_to_top = bvh::v2::extra::SafetySqToNode(currnode.get_bbox(), testpoint); + // we simply return safety to the outer bounding box as an estimate + return std::sqrt(safety_sq_to_top); + } + } + + // comparator bringing out "smallest" value on top + auto cmp = [](BVHPrioElement a, BVHPrioElement b) { return a.value > b.value; }; + static thread_local BVHPrioQueue queue(cmp); + queue.clear(); + + // algorithm is based on standard iterative tree traversal with priority queues + float current_safety_to_node_sq = 0.f; + + if (returnFace) { + *closest_facet_id = -1; + } + + do { + if (currnode.is_leaf()) { + // we are in a leaf node and actually talk to a face/triangular primitive + const auto begin_prim_id = currnode.index.first_id(); + const auto end_prim_id = begin_prim_id + currnode.index.prim_count(); + + for (auto p_id = begin_prim_id; p_id < end_prim_id; p_id++) { + const auto object_id = mybvh->prim_ids[p_id]; + + const auto& facet = fFacets[object_id]; + const auto& v1 = fVertices[facet[0]]; + const auto& v2 = fVertices[facet[1]]; + const auto& v3 = fVertices[facet[2]]; + + auto thissafetySQ = pointTriangleDistSq(Vec3f{point[0], point[1], point[2]}, Vec3f{v1[0], v1[1], v1[2]}, + Vec3f{v2[0], v2[1], v2[2]}, Vec3f{v3[0], v3[1], v3[2]}); + + if (thissafetySQ < smallest_safety_sq) { + smallest_safety_sq = thissafetySQ; + if (returnFace) { + *closest_facet_id = object_id; + } + } + } + } else { + // not a leave node ... for further traversal, + // we inject the children into priority queue based on distance to it's bounding box + const auto leftchild_id = currnode.index.first_id(); + const auto rightchild_id = leftchild_id + 1; + + for (size_t childid : {leftchild_id, rightchild_id}) { + if (childid >= mybvh->nodes.size()) { + continue; + } + + const auto& node = mybvh->nodes[childid]; + const auto inside = bvh::v2::extra::contains(node.get_bbox(), testpoint); + + if (inside) { + // this must be further considered because we are inside the bounding box + queue.push(BVHPrioElement{childid, -1.}); + } else { + auto safety_to_node_square = bvh::v2::extra::SafetySqToNode(node.get_bbox(), testpoint); + if (safety_to_node_square <= smallest_safety_sq) { + // this should be further considered + queue.push(BVHPrioElement{childid, safety_to_node_square}); + } + } + } + } + + if (queue.size() > 0) { + auto currElement = queue.top(); + currnode = mybvh->nodes[currElement.bvh_node_id]; + current_safety_to_node_sq = currElement.value; + queue.pop(); + } else { + break; + } + } while (current_safety_to_node_sq <= smallest_safety_sq); + + return std::nextafter(std::sqrt(smallest_safety_sq), 0.0f); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Safety + +Double_t O2Tessellated::Safety(const Double_t* point, Bool_t in) const +{ + // we could use some caching here (in future) since queries to the solid will likely + // be made with some locality + + if (in) { + call_counter++; + // distance to last known evaluation + const auto xd = float(point[0]) - mLast_x; + const auto yd = float(point[1]) - mLast_y; + const auto zd = float(point[2]) - mLast_z; + const auto d2 = xd * xd + yd * yd + zd * zd; + + if (d2 < mCachedSafety * mCachedSafety) { + // we moved less than known safety + cached_counter++; + return mCachedSafety - std::sqrt(d2); + } + } + + // fall-back to precise safety kernel + const auto safety = SafetyKernel(point, in); + if (in) { + mLast_x = point[0]; + mLast_y = point[1]; + mLast_z = point[2]; + mCachedSafety = safety; + } + return safety; +} + +//////////////////////////////////////////////////////////////////////////////// +/// ComputeNormal interface + +void O2Tessellated::ComputeNormal(const Double_t* point, const Double_t* dir, Double_t* norm) const +{ + // We take the approach to identify closest facet to the point via safety + // and returning the normal from this face. + + // TODO: Before doing that we could check for cached points from other queries + + // use safety kernel + int closest_face_id = -1; + SafetyKernel(point, true, &closest_face_id); + + if (closest_face_id < 0) { + norm[0] = 1.; + norm[1] = 0.; + norm[2] = 0.; + return; + } + + const auto& n = fOutwardNormals[closest_face_id]; + norm[0] = n[0]; + norm[1] = n[1]; + norm[2] = n[2]; + + // change sign depending on dir + if (norm[0] * dir[0] + norm[1] * dir[1] + norm[2] * dir[2] < 0) { + norm[0] = -norm[0]; + norm[1] = -norm[1]; + norm[2] = -norm[2]; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) DistFromInside function + +Double_t O2Tessellated::DistFromInside_Loop(const Double_t* point, const Double_t* dir) const +{ + Vertex_t p(point[0], point[1], point[2]); + Vertex_t d(dir[0], dir[1], dir[2]); + + double dist = Big(); + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + const auto& n = fOutwardNormals[i]; + + // Only exiting surfaces are relevant (from inside--> dot product must be positive) + if (n.Dot(d) <= 0.0) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, d, v0, v1, v2, 0.); + + if (t < dist) { + dist = t; + } + } + return dist; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) DistFromOutside function + +Double_t O2Tessellated::DistFromOutside_Loop(const Double_t* point, const Double_t* dir) const +{ + Vertex_t p(point[0], point[1], point[2]); + Vertex_t d(dir[0], dir[1], dir[2]); + + double dist = Big(); + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + const auto& n = fOutwardNormals[i]; + + // Only exiting surfaces are relevant (from outside, the dot product must be negative) + if (n.Dot(d) > 0.0) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, d, v0, v1, v2, 0.); + + if (t < dist) { + dist = t; + } + } + return dist; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) Contains + +bool O2Tessellated::Contains_Loop(const Double_t* point) const +{ + // Fixed ray direction + const Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757}; + + Vertex_t p(point[0], point[1], point[2]); + + int crossings = 0; + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, test_dir, v0, v1, v2, 0.); + if (t != std::numeric_limits::infinity()) { + ++crossings; + } + } + return (crossings & 1); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Custom streamer which performs Closing on read. +/// Recalculation of BVH and normals is fast + +void O2Tessellated::Streamer(TBuffer& b) +{ + if (b.IsReading()) { + b.ReadClassBuffer(O2Tessellated::Class(), this); + CloseShape(false); // close shape but do not re-perform checks + } else { + b.WriteClassBuffer(O2Tessellated::Class(), this); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Calculate the normals + +void O2Tessellated::CalculateNormals() +{ + fOutwardNormals.clear(); + for (auto& facet : fFacets) { + auto& v1 = fVertices[facet[0]]; + auto& v2 = fVertices[facet[1]]; + auto& v3 = fVertices[facet[2]]; + using Vec3d = Vec3f; + auto norm = triangleNormal(Vec3d{v1[0], v1[1], v1[2]}, Vec3d{v2[0], v2[1], v2[2]}, Vec3d{v3[0], v3[1], v3[2]}); + fOutwardNormals.emplace_back(Vertex_t{norm.x, norm.y, norm.z}); + } +} + +// NOLINTEND diff --git a/Detectors/Base/src/Propagator.cxx b/Detectors/Base/src/Propagator.cxx index cdc301a47cb6b..a465bbc312c77 100644 --- a/Detectors/Base/src/Propagator.cxx +++ b/Detectors/Base/src/Propagator.cxx @@ -15,6 +15,7 @@ #include "GPUCommonMath.h" #include "GPUTPCGMPolynomialField.h" #include "MathUtils/Utils.h" +#include "ReconstructionDataFormats/HelixHelper.h" #include "ReconstructionDataFormats/Vertex.h" using namespace o2::base; @@ -62,9 +63,9 @@ void PropagatorImpl::updateField() } const value_type xyz[3] = {0.}; if (mFieldFast) { - mFieldFast->GetBz(xyz, mBz); + mFieldFast->GetBz(xyz, mNominalBz); } else { - mBz = mField->GetBz(xyz[0], xyz[1], xyz[2]); + mNominalBz = mField->GetBz(xyz[0], xyz[1], xyz[2]); } } @@ -83,7 +84,6 @@ int PropagatorImpl::initFieldFromGRP(const std::string grpFileName, boo if (verbose) { grp->print(); } - return initFieldFromGRP(grp); } @@ -91,27 +91,7 @@ int PropagatorImpl::initFieldFromGRP(const std::string grpFileName, boo template int PropagatorImpl::initFieldFromGRP(const o2::parameters::GRPObject* grp, bool verbose) { - /// init mag field from GRP data and attach it to TGeoGlobalMagField - - if (TGeoGlobalMagField::Instance()->IsLocked()) { - if (TGeoGlobalMagField::Instance()->GetField()->TestBit(o2::field::MagneticField::kOverrideGRP)) { - LOG(warning) << "ExpertMode!!! GRP information will be ignored"; - LOG(warning) << "ExpertMode!!! Running with the externally locked B field"; - return 0; - } else { - LOG(info) << "Destroying existing B field instance"; - delete TGeoGlobalMagField::Instance(); - } - } - auto fld = o2::field::MagneticField::createFieldMap(grp->getL3Current(), grp->getDipoleCurrent(), o2::field::MagneticField::kConvLHC, grp->getFieldUniformity()); - TGeoGlobalMagField::Instance()->SetField(fld); - TGeoGlobalMagField::Instance()->Lock(); - if (verbose) { - LOG(info) << "Running with the B field constructed out of GRP"; - LOG(info) << "Access field via TGeoGlobalMagField::Instance()->Field(xyz,bxyz) or via"; - LOG(info) << "auto o2field = static_cast( TGeoGlobalMagField::Instance()->GetField() )"; - } - return 0; + return initFieldFromGRP(grp->getL3Current(), grp->getDipoleCurrent(), grp->getFieldUniformity(), verbose); } //____________________________________________________________ @@ -119,25 +99,53 @@ template int PropagatorImpl::initFieldFromGRP(const o2::parameters::GRPMagField* grp, bool verbose) { /// init mag field from GRP data and attach it to TGeoGlobalMagField + return initFieldFromGRP(grp->getL3Current(), grp->getDipoleCurrent(), grp->getFieldUniformity(), verbose); +} - if (TGeoGlobalMagField::Instance()->IsLocked()) { - if (TGeoGlobalMagField::Instance()->GetField()->TestBit(o2::field::MagneticField::kOverrideGRP)) { - LOG(warning) << "ExpertMode!!! GRP information will be ignored"; - LOG(warning) << "ExpertMode!!! Running with the externally locked B field"; - return 0; +//____________________________________________________________ +template +int PropagatorImpl::initFieldFromGRP(float currL3, float currDip, bool uniform, bool verbose) +{ + /// init mag field from GRP data and attach it to TGeoGlobalMagField or rescale already updated field + auto fldGlo = static_cast(TGeoGlobalMagField::Instance()->GetField()); + if (fldGlo) { // global field object was already initialized, reuse it if it is locked (as it normally should be) + float _currL3(currL3), _currDip(currDip); + auto newFieldType = fldGlo->getFieldMapScale(_currL3, _currDip, uniform); + bool sameFieldType = newFieldType == fldGlo->getMapType(); + if (!sameFieldType) { + LOGP(warn, "Existing B-field type {} cannot be rescaled to type {} requested by the GRP", int(fldGlo->getMapType()), int(newFieldType)); + } + if (TGeoGlobalMagField::Instance()->IsLocked() && sameFieldType) { + if (Instance()->mField && Instance()->mField != fldGlo) { // just make sure that cached field is the same as the global one + std::string name{"PropagatorF"}; + if constexpr (std::is_same_v) { + std::string name{"PropagatorD"}; + } + LOGP(fatal, "Magnetic field pointer cached in the {} instance differs from the gloabal field pointer", name); + } + if (verbose) { + LOGP(info, "Rescaling magnetic field to currents L3: {}, Dipole: {}, UniformityFlag: {}", currL3, currDip, uniform); + } + fldGlo->rescaleField(currL3, currDip, uniform); } else { - LOG(info) << "Destroying existing B field instance"; + LOGP(warn, "Destroying existing B field instance. This may invalidate field pointer cached in other objects"); delete TGeoGlobalMagField::Instance(); + Instance()->mField = nullptr; + Instance()->mFieldFast = nullptr; + fldGlo = nullptr; } } - auto fld = o2::field::MagneticField::createFieldMap(grp->getL3Current(), grp->getDipoleCurrent(), o2::field::MagneticField::kConvLHC, grp->getFieldUniformity()); - TGeoGlobalMagField::Instance()->SetField(fld); - TGeoGlobalMagField::Instance()->Lock(); - if (verbose) { - LOG(info) << "Running with the B field constructed out of GRP"; - LOG(info) << "Access field via TGeoGlobalMagField::Instance()->Field(xyz,bxyz) or via"; - LOG(info) << "auto o2field = static_cast( TGeoGlobalMagField::Instance()->GetField() )"; + if (!fldGlo) { + fldGlo = o2::field::MagneticField::createFieldMap(currL3, currDip, o2::field::MagneticField::kConvLHC, uniform); + TGeoGlobalMagField::Instance()->SetField(fldGlo); + TGeoGlobalMagField::Instance()->Lock(); + if (verbose) { + LOG(info) << "Running with the B field constructed out of GRP"; + LOG(info) << "Access field via TGeoGlobalMagField::Instance()->Field(xyz,bxyz) or via"; + LOG(info) << "auto o2field = static_cast( TGeoGlobalMagField::Instance()->GetField() )"; + } } + Instance()->updateField(); return 0; } @@ -170,7 +178,7 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackParCov_t& track, va signCorr = -dir; // sign of eloss correction is not imposed } - gpu::gpustd::array b{}; + std::array b{}; while (math_utils::detail::abs(dx) > Epsilon) { auto step = math_utils::detail::min(math_utils::detail::abs(dx), maxStep); if (dir < 0) { @@ -180,28 +188,105 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackParCov_t& track, va auto xyz0 = track.getXYZGlo(); getFieldXYZ(xyz0, &b[0]); + auto correct = [&track, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = track.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForMaterial(mb.meanX2X0, mb.getXRho(signCorr))) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, track.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = track.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); + } + return res; + }; + if (!track.propagateTo(x, b)) { return false; } if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); return false; } - if (matCorr != MatCorrType::USEMatCorrNONE) { - auto xyz1 = track.getXYZGlo(); - auto mb = getMatBudget(matCorr, xyz0, xyz1); - if (!track.correctForMaterial(mb.meanX2X0, mb.getXRho(signCorr))) { - return false; - } + if (!correct()) { + return false; + } + dx = xToGo - track.getX(); + } + track.setX(xToGo); + return true; +} + +//_______________________________________________________________________ +template +GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackParCov_t& track, TrackPar_t& linRef, value_type xToGo, value_type maxSnp, value_type maxStep, + PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + //---------------------------------------------------------------- + // + // Propagates the track to the plane X=xk (cm), using linRef as a Kalman linearisation point. + // taking into account all the three components of the magnetic field + // and correcting for the crossed material. + // + // maxStep - maximal step for propagation + // tofInfo - optional container for track length and PID-dependent TOF integration + // + // matCorr - material correction type, it is up to the user to make sure the pointer is attached (if LUT is requested) + //---------------------------------------------------------------- + auto dx = xToGo - track.getX(); + int dir = dx > 0.f ? 1 : -1; + if (!signCorr) { + signCorr = -dir; // sign of eloss correction is not imposed + } + + std::array b{}; + while (math_utils::detail::abs(dx) > Epsilon) { + auto step = math_utils::detail::min(math_utils::detail::abs(dx), maxStep); + if (dir < 0) { + step = -step; + } + auto x = track.getX() + step; + auto xyz0 = linRef.getXYZGlo(); + getFieldXYZ(xyz0, &b[0]); - if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length - tofInfo->addX2X0(mb.meanX2X0); - tofInfo->addXRho(mb.getXRho(signCorr)); + auto correct = [&track, &linRef, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = linRef.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForMaterial(linRef, mb.meanX2X0, mb.getXRho(signCorr))) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, linRef.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = linRef.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), linRef.getQ2P2()); } - } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght - auto xyz1 = track.getXYZGlo(); - math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); - tofInfo->addStep(stepV.R(), track.getP2Inv()); + return res; + }; + + if (!track.propagateTo(x, linRef, b)) { + return false; + } + if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); + return false; + } + if (!correct()) { + return false; } dx = xToGo - track.getX(); } @@ -231,7 +316,7 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackPar_t& track, value signCorr = -dir; // sign of eloss correction is not imposed } - gpu::gpustd::array b{}; + std::array b{}; while (math_utils::detail::abs(dx) > Epsilon) { auto step = math_utils::detail::min(math_utils::detail::abs(dx), maxStep); if (dir < 0) { @@ -241,27 +326,36 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackPar_t& track, value auto xyz0 = track.getXYZGlo(); getFieldXYZ(xyz0, &b[0]); + auto correct = [&track, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = track.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForELoss(((signCorr < 0) ? -mb.length : mb.length) * mb.meanRho)) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, track.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = track.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); + } + return res; + }; + if (!track.propagateParamTo(x, b)) { return false; } if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); return false; } - if (matCorr != MatCorrType::USEMatCorrNONE) { - auto xyz1 = track.getXYZGlo(); - auto mb = getMatBudget(matCorr, xyz0, xyz1); - if (!track.correctForELoss(((signCorr < 0) ? -mb.length : mb.length) * mb.meanRho)) { - return false; - } - if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length - tofInfo->addX2X0(mb.meanX2X0); - tofInfo->addXRho(mb.getXRho(signCorr)); - } - } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght - auto xyz1 = track.getXYZGlo(); - math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); - tofInfo->addStep(stepV.R(), track.getP2Inv()); + if (!correct()) { + return false; } dx = xToGo - track.getX(); } @@ -277,8 +371,7 @@ GPUd() bool PropagatorImpl::propagateToX(TrackParCov_t& track, value_ty //---------------------------------------------------------------- // // Propagates the track to the plane X=xk (cm) - // taking into account all the three components of the magnetic field - // and correcting for the crossed material. + // Use bz only and correct for the crossed material. // // maxStep - maximal step for propagation // tofInfo - optional container for track length and PID-dependent TOF integration @@ -298,30 +391,101 @@ GPUd() bool PropagatorImpl::propagateToX(TrackParCov_t& track, value_ty } auto x = track.getX() + step; auto xyz0 = track.getXYZGlo(); - + auto correct = [&track, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = track.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForMaterial(mb.meanX2X0, mb.getXRho(signCorr))) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, track.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = track.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); + } + return res; + }; if (!track.propagateTo(x, bZ)) { return false; } if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); return false; } - if (matCorr != MatCorrType::USEMatCorrNONE) { - auto xyz1 = track.getXYZGlo(); - auto mb = getMatBudget(matCorr, xyz0, xyz1); - // - if (!track.correctForMaterial(mb.meanX2X0, mb.getXRho(signCorr))) { - return false; - } + if (!correct()) { + return false; + } + dx = xToGo - track.getX(); + } + track.setX(xToGo); + return true; +} + +//_______________________________________________________________________ +template +GPUd() bool PropagatorImpl::propagateToX(TrackParCov_t& track, TrackPar_t& linRef, value_type xToGo, value_type bZ, value_type maxSnp, value_type maxStep, + PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + //---------------------------------------------------------------- + // + // Propagates the track to the plane X=xk (cm), using linRef as a Kalman linearisation point. + // Use bz only and correct for the crossed material if requested. + // + // maxStep - maximal step for propagation + // tofInfo - optional container for track length and PID-dependent TOF integration + // + // matCorr - material correction type, it is up to the user to make sure the pointer is attached (if LUT is requested) + //---------------------------------------------------------------- + auto dx = xToGo - track.getX(); + int dir = dx > 0.f ? 1 : -1; + if (!signCorr) { + signCorr = -dir; // sign of eloss correction is not imposed + } + + while (math_utils::detail::abs(dx) > Epsilon) { + auto step = math_utils::detail::min(math_utils::detail::abs(dx), maxStep); + if (dir < 0) { + step = -step; + } + auto x = track.getX() + step; + auto xyz0 = linRef.getXYZGlo(); - if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length - tofInfo->addX2X0(mb.meanX2X0); - tofInfo->addXRho(mb.getXRho(signCorr)); + auto correct = [&track, &linRef, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = linRef.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForMaterial(linRef, mb.meanX2X0, mb.getXRho(signCorr))) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, linRef.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = linRef.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), linRef.getQ2P2()); } - } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght - auto xyz1 = track.getXYZGlo(); - math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); - tofInfo->addStep(stepV.R(), track.getP2Inv()); + return res; + }; + + if (!track.propagateTo(x, linRef, bZ)) { // linRef also updated + return false; + } + if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); + return false; + } + if (!correct()) { + return false; } dx = xToGo - track.getX(); } @@ -359,34 +523,153 @@ GPUd() bool PropagatorImpl::propagateToX(TrackPar_t& track, value_type auto x = track.getX() + step; auto xyz0 = track.getXYZGlo(); + auto correct = [&track, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = track.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForELoss(mb.getXRho(signCorr))) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, track.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = track.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); + } + return res; + }; + if (!track.propagateParamTo(x, bZ)) { return false; } if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); + return false; + } + if (!correct()) { + return false; + } + dx = xToGo - track.getX(); + } + track.setX(xToGo); + return true; +} + +//_______________________________________________________________________ +template +template +GPUd() bool PropagatorImpl::propagateToR(track_T& track, value_type r, bool bzOnly, value_type maxSnp, value_type maxStep, + MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + const value_T MaxPhiLoc = math_utils::detail::asin(maxSnp), MaxPhiLocSafe = 0.95 * MaxPhiLoc; + auto bz = getNominalBz(); + if (math_utils::detail::abs(bz) > constants::math::Almost0) { + o2::track::TrackAuxPar traux(track, bz); + o2::track::TrackAuxPar crad; + value_type r0 = math_utils::detail::sqrt(track.getX() * track.getX() + track.getY() * track.getY()); + value_type dr = (r - r0); + value_type rTmp = r - (math_utils::detail::abs(dr) > 1. ? (dr > 0 ? 0.5 : -0.5) : 0.5 * dr); // 1st propagate a few mm short of the targer R + crad.rC = rTmp; + crad.c = crad.cc = 1.f; + crad.s = crad.ss = crad.cs = 0.f; + o2::track::CrossInfo cross; + cross.circlesCrossInfo(crad, traux, 0.); + if (cross.nDCA < 1) { return false; } - if (matCorr != MatCorrType::USEMatCorrNONE) { - auto xyz1 = track.getXYZGlo(); - auto mb = getMatBudget(matCorr, xyz0, xyz1); - // - if (!track.correctForELoss(mb.getXRho(signCorr))) { + double phiCross[2] = {}, dphi[2] = {}; + auto curv = track.getCurvature(bz); + bool clockwise = curv < 0; // q+ in B+ or q- in B- goes clockwise + auto phiLoc = math_utils::detail::asin(track.getSnp()); + auto phi0 = phiLoc + track.getAlpha(); + o2::math_utils::detail::bringTo02Pi(phi0); + for (int i = 0; i < cross.nDCA; i++) { + // track pT direction angle at crossing points: + // == angle of the tangential to track circle at the crossing point X,Y + // == normal to the radial vector from the track circle center {X-cX, Y-cY} + // i.e. the angle of the vector {Y-cY, -(X-cx)} + auto normX = double(cross.yDCA[i]) - double(traux.yC), normY = -(double(cross.xDCA[i]) - double(traux.xC)); + if (!clockwise) { + normX = -normX; + normY = -normY; + } + phiCross[i] = math_utils::detail::atan2(normY, normX); + o2::math_utils::detail::bringTo02Pi(phiCross[i]); + dphi[i] = phiCross[i] - phi0; + if (dphi[i] > o2::constants::math::PI) { + dphi[i] -= o2::constants::math::TwoPI; + } else if (dphi[i] < -o2::constants::math::PI) { + dphi[i] += o2::constants::math::TwoPI; + } + } + int sel = cross.nDCA == 1 ? 0 : (clockwise ? (dphi[0] < dphi[1] ? 0 : 1) : (dphi[1] < dphi[0] ? 0 : 1)); + auto deltaPhi = dphi[sel]; + + while (1) { + auto phiLocFin = phiLoc + deltaPhi; + // case1 + if (math_utils::detail::abs(phiLocFin) < MaxPhiLocSafe) { // just 1 step propagation + auto deltaX = (math_utils::detail::sin(phiLocFin) - track.getSnp()) / track.getCurvature(bz); + if (!propagateTo(track, track.getX() + deltaX, bzOnly, maxSnp, maxStep, matCorr, tofInfo, signCorr)) { + return false; + } + break; + } + if (math_utils::detail::abs(deltaPhi) < (2 * MaxPhiLocSafe)) { // still can go in 1 step with one extra rotation + auto rot = phiLoc + 0.5 * deltaPhi; + if (!track.rotate(track.getAlpha() + rot)) { + return false; + } + phiLoc -= rot; + continue; // should be ok for the case 1 now. + } + + auto rot = phiLoc + (deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe); + if (!track.rotate(track.getAlpha() + rot)) { return false; } + phiLoc -= rot; // = +- MaxPhiLocSafe - if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length - tofInfo->addX2X0(mb.meanX2X0); - tofInfo->addXRho(mb.getXRho(signCorr)); + // propagate to phiLoc = +-MaxPhiLocSafe + auto tgtPhiLoc = deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe; + auto deltaX = (math_utils::detail::sin(tgtPhiLoc) - track.getSnp()) / track.getCurvature(bz); + if (!propagateTo(track, track.getX() + deltaX, bzOnly, maxSnp, maxStep, matCorr, tofInfo, signCorr)) { + return false; } - } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght - auto xyz1 = track.getXYZGlo(); - math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); - tofInfo->addStep(stepV.R(), track.getP2Inv()); + deltaPhi -= tgtPhiLoc - phiLoc; + phiLoc = deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe; + continue; // should be of for the case 1 now. } - dx = xToGo - track.getX(); + bz = getBz(math_utils::Point3D{value_type(cross.xDCA[sel]), value_type(cross.yDCA[sel]), value_type(track.getZ())}); } - track.setX(xToGo); - return true; + // do final step till target R, also covers Bz = 0; + value_type xfin; + if (!track.getXatLabR(r, xfin, bz)) { + return false; + } + return propagateToX(track, xfin, bzOnly, maxSnp, maxStep, matCorr, tofInfo, signCorr); +} + +template +GPUd() bool PropagatorImpl::propagateToAlphaX(TrackParCov_t& track, TrackPar_t* linRef, value_type alpha, value_type x, bool bzOnly, value_type maxSnp, value_type maxStep, int minSteps, + MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + // propagate to alpha,X, if needed in a few steps + auto snp = track.getSnpAt(alpha, x, getNominalBz()); + // apply safety factor 0.9 for crude rotation estimate + if (math_utils::detail::abs(snp) < maxSnp * 0.9 && (linRef ? track.rotate(alpha, *linRef, getNominalBz()) : track.rotate(alpha))) { + auto dx = math_utils::detail::abs(x - track.getX()); + if (dx < Epsilon) { + return true; + } + return propagateTo(track, linRef, x, bzOnly, maxSnp, math_utils::detail::min(dx / minSteps, maxStep), matCorr, tofInfo, signCorr); + } + return false; } //_______________________________________________________________________ @@ -439,6 +722,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const o2::dataformats::Verte // Estimate the impact parameter neglecting the track curvature value_type d = math_utils::detail::abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } value_type crv = track.getCurvature(bZ); @@ -459,6 +746,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const o2::dataformats::Verte #elif !defined(GPUCA_NO_FMT) LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx; #endif + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } track = tmpT; @@ -488,9 +779,13 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const o2::dataformats: // Estimate the impact parameter neglecting the track curvature value_type d = math_utils::detail::abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } - value_type crv = track.getCurvature(mBz); + value_type crv = track.getCurvature(mNominalBz); value_type tgfv = -(crv * x - snp) / (crv * y + csp); sn = tgfv / math_utils::detail::sqrt(1.f + tgfv * tgfv); cs = math_utils::detail::sqrt((1. - sn) * (1. + sn)); @@ -508,6 +803,10 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const o2::dataformats: #elif !defined(GPUCA_NO_FMT) LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx; #endif + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } track = tmpT; @@ -524,7 +823,7 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const o2::dataformats: template GPUd() bool PropagatorImpl::propagateToDCA(const math_utils::Point3D& vtx, TrackPar_t& track, value_type bZ, value_type maxStep, PropagatorImpl::MatCorrType matCorr, - gpu::gpustd::array* dca, track::TrackLTIntegral* tofInfo, + std::array* dca, track::TrackLTIntegral* tofInfo, int signCorr, value_type maxD) const { // propagate track to DCA to the vertex @@ -537,6 +836,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const math_utils::Point3D(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } value_type crv = track.getCurvature(bZ); @@ -558,6 +861,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const math_utils::Point3D::propagateToDCA(const math_utils::Point3D GPUd() bool PropagatorImpl::propagateToDCABxByBz(const math_utils::Point3D& vtx, TrackPar_t& track, value_type maxStep, PropagatorImpl::MatCorrType matCorr, - gpu::gpustd::array* dca, track::TrackLTIntegral* tofInfo, + std::array* dca, track::TrackLTIntegral* tofInfo, int signCorr, value_type maxD) const { // propagate track to DCA to the vertex @@ -585,9 +892,13 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const math_utils::Poin // Estimate the impact parameter neglecting the track curvature value_type d = math_utils::detail::abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } - value_type crv = track.getCurvature(mBz); + value_type crv = track.getCurvature(mNominalBz); value_type tgfv = -(crv * x - snp) / (crv * y + csp); sn = tgfv / math_utils::detail::sqrt(1.f + tgfv * tgfv); cs = math_utils::detail::sqrt((1. - sn) * (1. + sn)); @@ -606,6 +917,10 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const math_utils::Poin #else LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z(); #endif + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } track = tmpT; @@ -618,7 +933,28 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const math_utils::Poin //____________________________________________________________ template -GPUd() void PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegral& lt, const o2::track::TrackParametrization& trc) const +GPUd() float PropagatorImpl::estimateLTIncrement(const o2::track::TrackParametrization& trc, + const o2::math_utils::Point3D& posStart, + const o2::math_utils::Point3D& posEnd) const +{ + // estimate helical step increment between 2 point + float dX = posEnd.X() - posStart.X(), dY = posEnd.Y() - posStart.Y(), dZ = posEnd.Z() - posStart.Z(), d2XY = dX * dX + dY * dY; + if (getNominalBz() != 0) { // circular arc = 2*R*asin(dXY/2R) + float b[3]; + o2::math_utils::Point3D posAv(0.5 * (posEnd.X() + posStart.X()), 0.5 * (posEnd.Y() + posStart.Y()), 0.5 * (posEnd.Z() + posStart.Z())); + getFieldXYZ(posAv, b); + float curvH = math_utils::detail::abs(0.5f * trc.getCurvature(b[2])), asArg = curvH * math_utils::detail::sqrt(d2XY); + if (curvH > 0.f) { + d2XY = asArg < 1.f ? math_utils::detail::asin(asArg) / curvH : o2::constants::math::PIHalf / curvH; + d2XY *= d2XY; + } + } + return math_utils::detail::sqrt(d2XY + dZ * dZ); +} + +//____________________________________________________________ +template +GPUd() value_T PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegral& lt, const o2::track::TrackParametrization& trc) const { value_T xdca = 0., ydca = 0., length = 0.; // , zdca = 0. // zdca might be used in future o2::math_utils::CircleXY c; @@ -637,7 +973,7 @@ GPUd() void PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegral& return math_utils::detail::abs(trc.getY() * math_utils::detail::sqrt(1. + trc.getTgl() * trc.getTgl())); // distance from the current point to DCA } }; - trc.getCircleParamsLoc(mBz, c); + trc.getCircleParamsLoc(mNominalBz, c); if (c.rC != 0.) { // helix auto distC = math_utils::detail::sqrt(c.getCenterD2()); // distance from the circle center to origin if (distC > 1.e-3) { @@ -648,7 +984,7 @@ GPUd() void PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegral& auto angcos = (v0x * v1x + v0y * v1y) / (c.rC * c.rC); if (math_utils::detail::abs(angcos) < 1.f) { auto ang = math_utils::detail::acos(angcos); - if ((trc.getSign() > 0.f) == (mBz > 0.f)) { + if ((trc.getSign() > 0.f) == (mNominalBz > 0.f)) { ang = -ang; // we need signeg angle c.rC = -c.rC; // we need signed curvature for zdca } @@ -665,8 +1001,10 @@ GPUd() void PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegral& length = straigh_line_approx(); } // since we assume the track or its parent comes from the beam-line or decay, add XY(?) distance to it - length += math_utils::detail::sqrt(xdca * xdca + ydca * ydca); - lt.addStep(length, trc.getP2Inv()); + value_T dcaT = math_utils::detail::sqrt(xdca * xdca + ydca * ydca); + length += dcaT; + lt.addStep(length, trc.getQ2P2()); + return dcaT; } //____________________________________________________________ @@ -720,6 +1058,35 @@ GPUd() void PropagatorImpl::getFieldXYZImpl(const math_utils::Point3D +template +GPUd() T PropagatorImpl::getBzImpl(const math_utils::Point3D xyz) const +{ + T bz = 0; + if (mGPUField) { +#if defined(GPUCA_GPUCODE_DEVICE) && defined(GPUCA_HAS_GLOBAL_SYMBOL_CONSTANT_MEM) + const auto* f = &GPUCA_CONSMEM.param.polynomialField; // Access directly from constant memory on GPU (copied here to avoid complicated header dependencies) +#else + const auto* f = mGPUField; +#endif + constexpr value_type kCLight1 = 1. / o2::gpu::gpu_common_constants::kCLight; + bz = f->GetFieldBz(xyz.X(), xyz.Y(), xyz.Z()) * kCLight1; + } else { +#ifndef GPUCA_GPUCODE + if (mFieldFast) { + mFieldFast->GetBz(xyz, bz); // Must not call the host-only function in GPU compilation + } else { +#ifdef GPUCA_STANDALONE + LOG(fatal) << "Normal field cannot be used in standalone benchmark"; +#else + bz = mField->GetBz(xyz.X(), xyz.Y(), xyz.Z()); +#endif + } +#endif + } + return bz; +} + template GPUd() void PropagatorImpl::getFieldXYZ(const math_utils::Point3D xyz, float* bxyz) const { @@ -732,18 +1099,30 @@ GPUd() void PropagatorImpl::getFieldXYZ(const math_utils::Point3D(xyz, bxyz); } +template +GPUd() float PropagatorImpl::getBz(const math_utils::Point3D xyz) const +{ + return getBzImpl(xyz); +} + +template +GPUd() double PropagatorImpl::getBz(const math_utils::Point3D xyz) const +{ + return getBzImpl(xyz); +} + namespace o2::base { +#if !defined(GPUCA_GPUCODE) || defined(GPUCA_GPUCODE_DEVICE) // FIXME: DR: WORKAROUND to avoid CUDA bug creating host symbols for device code. template class PropagatorImpl; -#ifndef GPUCA_GPUCODE_DEVICE -template class PropagatorImpl; +template bool GPUdni() PropagatorImpl::propagateToAlphaX::TrackPar_t>(PropagatorImpl::TrackPar_t&, float, float, bool, float, float, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; +template bool GPUdni() PropagatorImpl::propagateToAlphaX::TrackParCov_t>(PropagatorImpl::TrackParCov_t&, float, float, bool, float, float, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; +template bool GPUdni() PropagatorImpl::propagateToR::TrackPar_t>(PropagatorImpl::TrackPar_t&, float, bool, float, float, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; +template bool GPUdni() PropagatorImpl::propagateToR::TrackParCov_t>(PropagatorImpl::TrackParCov_t&, float, bool, float, float, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; #endif -#ifndef __HIPCC__ // TODO: Fixme: must prevent HIP from compiling this, should file bug report -template bool GPUd() PropagatorImpl::propagateToAlphaX::TrackPar_t>(PropagatorImpl::TrackPar_t&, float, float, bool, float, float, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; -template bool GPUd() PropagatorImpl::propagateToAlphaX::TrackParCov_t>(PropagatorImpl::TrackParCov_t&, float, float, bool, float, float, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; -#ifndef GPUCA_GPUCODE_DEVICE +#ifndef GPUCA_GPUCODE +template class PropagatorImpl; template bool PropagatorImpl::propagateToAlphaX::TrackPar_t>(PropagatorImpl::TrackPar_t&, double, double, bool, double, double, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; template bool PropagatorImpl::propagateToAlphaX::TrackParCov_t>(PropagatorImpl::TrackParCov_t&, double, double, bool, double, double, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; #endif -#endif } // namespace o2::base diff --git a/Detectors/Base/src/SimFieldUtils.cxx b/Detectors/Base/src/SimFieldUtils.cxx index 9673e39bf1b07..ba828c0c2ac25 100644 --- a/Detectors/Base/src/SimFieldUtils.cxx +++ b/Detectors/Base/src/SimFieldUtils.cxx @@ -33,7 +33,7 @@ FairField* const SimFieldUtils::createMagField() auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); auto grpmagfield = ccdb.get("GLO/Config/GRPMagField"); // TODO: clarify if we need to pass other params such as beam energy/type etc. - field = o2::field::MagneticField::createFieldMap(grpmagfield->getL3Current(), grpmagfield->getDipoleCurrent(), grpmagfield->getFieldUniformity()); + field = o2::field::MagneticField::createFieldMap(grpmagfield->getL3Current(), grpmagfield->getDipoleCurrent(), o2::field::MagneticField::kConvLHC, grpmagfield->getFieldUniformity()); } // b) using the given values on the command line else { diff --git a/Detectors/Base/src/Stack.cxx b/Detectors/Base/src/Stack.cxx index 2a39b9574793d..de69c866e7b82 100644 --- a/Detectors/Base/src/Stack.cxx +++ b/Detectors/Base/src/Stack.cxx @@ -15,6 +15,7 @@ #include "DetectorsBase/Stack.h" #include "DetectorsBase/Detector.h" +#include #include "DetectorsCommonDataFormats/DetID.h" #include "SimulationDataFormat/MCTrack.h" @@ -82,7 +83,6 @@ Stack::Stack(Int_t size) } auto& param = o2::sim::StackParam::Instance(); - LOG(info) << param; TransportFcn transportPrimary; if (param.transportPrimary.compare("none") == 0) { transportPrimary = [](const TParticle& p, const std::vector& particles) { @@ -362,6 +362,7 @@ TParticle* Stack::PopNextTrack(Int_t& iTrack) // LOG(info) << "SEEDING NEW TRACK USING HASH" << hash; // init seed per track gRandom->SetSeed(hash); + o2::base::VMCSeederService::instance().setSeed(); // NOTE: THE BETTER PLACE WOULD BE IN PRETRACK HOOK BUT THIS DOES NOT SEEM TO WORK // WORKS ONLY WITH G3 SINCE G4 DOES NOT CALL THIS FUNCTION } // .trackSeed ? @@ -636,14 +637,17 @@ void Stack::Print(Option_t* option) const void Stack::addHit(int iDet) { + // translate detector ID to bit id + const auto bitID = o2::base::Detector::getDetId2HitBitIndex()[iDet]; + if (mIndexOfCurrentTrack < mNumberOfPrimaryParticles) { auto& part = mTracks->at(mIndexOfCurrentTrack); - part.setHit(iDet); + part.setHit(bitID); } else { Int_t iTrack = mTrackIDtoParticlesEntry[mIndexOfCurrentTrack]; auto& part = mParticles[iTrack]; - part.setHit(iDet); + part.setHit(bitID); } mCurrentParticle.SetBit(ParticleStatus::kHasHits, 1); mHitCounter++; @@ -652,7 +656,9 @@ void Stack::addHit(int iDet, Int_t iTrack) { mHitCounter++; auto& part = mParticles[iTrack]; - part.setHit(iDet); + // fetch the bit encoding for hits + const auto bitID = o2::base::Detector::getDetId2HitBitIndex()[iDet]; + part.setHit(bitID); mCurrentParticle.SetBit(ParticleStatus::kHasHits, 1); } diff --git a/Detectors/Base/src/TFIDInfoHelper.cxx b/Detectors/Base/src/TFIDInfoHelper.cxx index f0bd8c0f15a30..6eac1cc18c81a 100644 --- a/Detectors/Base/src/TFIDInfoHelper.cxx +++ b/Detectors/Base/src/TFIDInfoHelper.cxx @@ -23,15 +23,9 @@ void o2::base::TFIDInfoHelper::fillTFIDInfo(ProcessingContext& pc, o2::dataforma { const auto& tinfo = pc.services().get(); static int errCount = 0; - if (tinfo.firstTForbit == -1U || tinfo.creation == -1) { - if (errCount++ < 5) { - LOGP(warn, "Ignoring dummy input with orbit {} and creation time {} in fillTFIDInfo", tinfo.firstTForbit, tinfo.creation); - } - return; + ti.fill(tinfo.firstTForbit, tinfo.tfCounter, tinfo.runNumber, tinfo.timeslice, tinfo.creation); + if (ti.discard && errCount++ < 5) { + LOGP(warn, "Bad input with orbit {}, TFcounter {} and creation time {} in fillTFIDInfo", tinfo.firstTForbit, tinfo.tfCounter, tinfo.creation); } - ti.firstTForbit = tinfo.firstTForbit; - ti.tfCounter = tinfo.tfCounter; - ti.runNumber = tinfo.runNumber; - ti.startTime = tinfo.timeslice; - ti.creation = tinfo.creation; + return; } diff --git a/Detectors/Base/src/TGeoGeometryUtils.cxx b/Detectors/Base/src/TGeoGeometryUtils.cxx new file mode 100644 index 0000000000000..6f06eff17a6d7 --- /dev/null +++ b/Detectors/Base/src/TGeoGeometryUtils.cxx @@ -0,0 +1,144 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TGeoGeometryUtils.cxx +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo + +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace base +{ + +namespace +{ +// some helpers to interpret TGeo TBuffer3D output +// and convert it to surface triangles (reengineered from TGeo code) + +std::vector BuildVertexLoop(const TBuffer3D& buf, + const std::vector& segs) +{ + // adjacency list + std::unordered_map> adj; + + for (int s : segs) { + int a = buf.fSegs[3 * s + 1]; + int b = buf.fSegs[3 * s + 2]; + adj[a].push_back(b); + adj[b].push_back(a); + } + + // start from any vertex + int start = adj.begin()->first; + int prev = -1; + int curr = start; + + std::vector loop; + + while (true) { + loop.push_back(curr); + + const auto& nbrs = adj[curr]; + int next = -1; + + for (int n : nbrs) { + if (n != prev) { + next = n; + break; + } + } + + if (next == -1 || next == start) { + break; + } + + prev = curr; + curr = next; + } + return loop; +} + +std::vector> ExtractPolygons(const TBuffer3D& buf) +{ + std::vector> polys; + Int_t idx = 0; + + for (Int_t ip = 0; ip < buf.NbPols(); ++ip) { + + idx++; // color + Int_t nseg = buf.fPols[idx++]; + + std::vector segs(nseg); + for (Int_t i = 0; i < nseg; ++i) { + segs[i] = buf.fPols[idx++]; + } + + auto verts = BuildVertexLoop(buf, segs); + if (verts.size() >= 3) { + polys.push_back(std::move(verts)); + } + } + + return polys; +} + +std::vector> + Triangulate(const std::vector>& polys) +{ + std::vector> tris; + for (const auto& poly : polys) { + int nv = poly.size(); + if (nv < 3) { + continue; + } + + int v0 = poly[0]; + for (int i = 1; i < nv - 1; ++i) { + tris.push_back({{v0, poly[i], poly[i + 1]}}); + } + } + return tris; +} + +TGeoTessellated* MakeTessellated(const TBuffer3D& buf) +{ + auto polys = ExtractPolygons(buf); + auto tris = Triangulate(polys); + int i = 0; + auto* tess = new TGeoTessellated("tess"); + const Double_t* p = buf.fPnts; + for (auto& t : tris) { + tess->AddFacet( + TGeoTessellated::Vertex_t{p[3 * t[0]], p[3 * t[0] + 1], p[3 * t[0] + 2]}, + TGeoTessellated::Vertex_t{p[3 * t[1]], p[3 * t[1] + 1], p[3 * t[1] + 2]}, + TGeoTessellated::Vertex_t{p[3 * t[2]], p[3 * t[2] + 1], p[3 * t[2] + 2]}); + } + tess->CloseShape(); + return tess; +} +} // end anonymous namespace + +///< Transform any (primitive) TGeoShape to a TGeoTessellated +TGeoTessellated* TGeoGeometryUtils::TGeoShapeToTGeoTessellated(TGeoShape const* shape) +{ + auto& buf = shape->GetBuffer3D(TBuffer3D::kRawSizes | TBuffer3D::kRaw | TBuffer3D::kCore, false); + auto tes = MakeTessellated(buf); + return tes; +} + +} // namespace base +} // namespace o2 diff --git a/Detectors/Base/src/VMCSeederService.cxx b/Detectors/Base/src/VMCSeederService.cxx new file mode 100644 index 0000000000000..8fc36d9074fab --- /dev/null +++ b/Detectors/Base/src/VMCSeederService.cxx @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DetectorsBase/VMCSeederService.h" +#include "TVirtualMC.h" +#include // for FairLogger +#include // for ROOT JIT helpers + +using namespace o2::base; + +void VMCSeederService::initSeederFunction(TVirtualMC const* vmc) +{ + if (strcmp(vmc->GetName(), "TGeant3TGeo") == 0) { + // Geant3 doesn't need anything special in our context + mSeederFcn = []() {}; + } else if (strcmp(vmc->GetName(), "TGeant4") == 0) { + // dynamically get access to the Geant4_VMC seeding function (without this function linking against Geant4) + std::string G4func("std::function G4func() { gSystem->Load(\"libgeant4vmc\"); return [](){ ((TGeant4*)TVirtualMC::GetMC())->SetRandomSeed(); };}"); + mSeederFcn = o2::conf::JITAndEvalFunction(G4func, "G4func()", "std::function", "VMCSEEDERFUNC123"); + } else { + LOG(warn) << "Unknown VMC engine or unimplemented VMC seeding function"; + mSeederFcn = []() {}; + } +} + +// constructor +VMCSeederService::VMCSeederService() +{ + auto vmc = TVirtualMC::GetMC(); + if (vmc) { + LOG(info) << "Seeder initializing for " << vmc->GetName(); + initSeederFunction(vmc); + } else { + LOG(fatal) << " Seeder could not be initialized (no VMC instance found)"; + } +} + +void VMCSeederService::setSeed() const +{ + // Make sure gRandom is sufficiently well initialized + // by calling Rndm() once. + // This is ok since in any case gRandom->SetSeed(seed); gRandom->GetSeed() != seed; + gRandom->Rndm(); + mSeederFcn(); + ++mSeedCount; +} diff --git a/Detectors/Base/src/bvh2_extra_kernels.h b/Detectors/Base/src/bvh2_extra_kernels.h new file mode 100644 index 0000000000000..70e43202a53c4 --- /dev/null +++ b/Detectors/Base/src/bvh2_extra_kernels.h @@ -0,0 +1,79 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel 2026 + +#ifndef ROOT_GEOM_BVH2_EXTRA + +namespace bvh::v2::extra +{ + +// reusable geometry kernels used in multiple places +// for interaction with BVH2 structures + +// determines if a point is inside the bounding box +template +bool contains(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + auto min = box.min; + auto max = box.max; + return (p[0] >= min[0] && p[0] <= max[0]) && (p[1] >= min[1] && p[1] <= max[1]) && + (p[2] >= min[2] && p[2] <= max[2]); +} + +// determines the largest squared distance of point to any of the bounding box corners +template +auto RmaxSqToNode(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + // construct the 8 corners to get the maximal distance + const auto minCorner = box.min; + const auto maxCorner = box.max; + using Vec3 = bvh::v2::Vec; + // these are the corners of the bounding box + const std::array, 8> corners{ + Vec3{minCorner[0], minCorner[1], minCorner[2]}, Vec3{minCorner[0], minCorner[1], maxCorner[2]}, + Vec3{minCorner[0], maxCorner[1], minCorner[2]}, Vec3{minCorner[0], maxCorner[1], maxCorner[2]}, + Vec3{maxCorner[0], minCorner[1], minCorner[2]}, Vec3{maxCorner[0], minCorner[1], maxCorner[2]}, + Vec3{maxCorner[0], maxCorner[1], minCorner[2]}, Vec3{maxCorner[0], maxCorner[1], maxCorner[2]}}; + + T Rmax_sq{0}; + for (const auto& corner : corners) { + float R_sq = 0.; + const auto dx = corner[0] - p[0]; + R_sq += dx * dx; + const auto dy = corner[1] - p[1]; + R_sq += dy * dy; + const auto dz = corner[2] - p[2]; + R_sq += dz * dz; + Rmax_sq = std::max(Rmax_sq, R_sq); + } + return Rmax_sq; +}; + +// determines the minimum squared distance of point to a bounding box ("safey square") +template +auto SafetySqToNode(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + T sqDist{0.0}; + for (int i = 0; i < 3; i++) { + T v = p[i]; + if (v < box.min[i]) { + sqDist += (box.min[i] - v) * (box.min[i] - v); + } else if (v > box.max[i]) { + sqDist += (v - box.max[i]) * (v - box.max[i]); + } + } + return sqDist; +}; + +} // namespace bvh::v2::extra + +#endif \ No newline at end of file diff --git a/Detectors/Base/src/bvh2_third_party.h b/Detectors/Base/src/bvh2_third_party.h new file mode 100644 index 0000000000000..5cf7772269642 --- /dev/null +++ b/Detectors/Base/src/bvh2_third_party.h @@ -0,0 +1,49 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel 2026 + +#ifndef ROOT_GEOM_BVH2_THIRD_PARTY + +// A single entry header into third-party BVH2 installed in ROOT +// Good place to manage compiler warnings etc. + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wpsabi" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wpsabi" +#pragma GCC diagnostic ignored "-Wall" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wattributes" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 5051) +#endif + +#include +#include +#include +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#endif \ No newline at end of file diff --git a/Detectors/Base/test/README.md b/Detectors/Base/test/README.md index a545d6091846d..ca7fea02949ba 100644 --- a/Detectors/Base/test/README.md +++ b/Detectors/Base/test/README.md @@ -13,6 +13,25 @@ root -b -q O2/Detectors/Base/test/buildMatBudLUT.C+ The generation is quite time consuming (may take ~30 min). +It can be filled in parallel, one `TGeoNavigator` per thread, by passing a thread count as the +7th argument of `buildMatBudLUT` or by setting the environment variable: +``` +export NTHREADS_MATBUD=16 +``` +The result does not depend on the number of threads. Scaling beyond a few threads needs +ROOT >= v6-36-10-alice3, which removes the per-query thread-id lookup and the false sharing +between the per-thread scratch buffers of TGeo shapes; with older ROOT the parallel path is +still correct, just slower. + +An alternative VecGeom geometry backend can be selected via the 8th argument (`"ROOT"` +or `"VECGEOM"`), e.g. +``` +root -b -q 'O2/Detectors/Base/test/buildMatBudLUT.C(60, -1, "matbud.root", "o2sim", "", 16, "VECGEOM")' +``` +This requires O2 to have been built against the optional `TGeo2VecGeom` package +(`o2::base::GeometryManager::isVecGeomAvailable()`); it is otherwise a build-time no-op that +does not affect the default ROOT/TGeo path in any way. + The optimized LUT will be stored in the matbud.root file. Load it as: @@ -30,3 +49,5 @@ auto mb = mbl.getMatBudget(xyz0[0],xyz0[1],xyz0[2], xyz1[0],xyz1[1],xyz1[2]); std::cout << "= " << mb.meanRho << " = " << mb.meanX2X0 << "\n"; ``` + +Macro `extractLUTLayers.C` can be used to extract layers covering certain radius range to obtain more compact LUT. diff --git a/Detectors/Base/test/buildMatBudLUT.C b/Detectors/Base/test/buildMatBudLUT.C index fe6b6b9fd7fd4..2b371b90effa3 100644 --- a/Detectors/Base/test/buildMatBudLUT.C +++ b/Detectors/Base/test/buildMatBudLUT.C @@ -23,13 +23,18 @@ #include #endif -#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version +using MatbudGeomBackend = o2::base::MatbudGeomBackend; o2::base::MatLayerCylSet mbLUT; bool testMBLUT(const std::string& lutFile = "matbud.root"); +MatbudGeomBackend parseBackend(const std::string& s); -bool buildMatBudLUT(int nTst = 30, int maxLr = -1, const std::string& outFile = "matbud.root", const std::string& geomName = ""); +/// Build the material budget LUT. nThreads < 0 takes the thread count from NTHREADS_MATBUD. +/// geomBackend is "ROOT" (default) or "VECGEOM" (requires O2 built against TGeo2VecGeom). +bool buildMatBudLUT(int nTst = 60, int maxLr = -1, const std::string& outFile = "matbud.root", + const std::string& geomNamePrefix = "o2sim", const std::string& opts = "", + int nThreads = -1, const std::string& geomBackend = "ROOT"); struct LrData { float rMin = 0.f; @@ -44,14 +49,19 @@ struct LrData { std::vector lrData; void configLayers(); -bool buildMatBudLUT(int nTst, int maxLr, const std::string& outFile, const std::string& geomNameInput) +bool buildMatBudLUT(int nTst, int maxLr, const std::string& outFile, const std::string& geomNamePrefix, + const std::string& opts, int nThreads, const std::string& geomBackend) { - auto geomName = o2::base::NameConf::getGeomFileName(geomNameInput); + MatbudGeomBackend backend = parseBackend(geomBackend); + auto geomName = o2::base::NameConf::getGeomFileName(geomNamePrefix); if (gSystem->AccessPathName(geomName.c_str())) { // if needed, create geometry - std::cout << geomName << " does not exist. Will create it\n"; - gSystem->Exec("$O2_ROOT/bin/o2-sim -n 0"); + std::cout << geomName << " does not exist. Will create it on the fly\n"; + std::stringstream str; + // constructing an **unaligned** geom (Geant3 used since faster initialization) --> can be avoided by passing an existing geometry + str << "${O2_ROOT}/bin/o2-sim-serial -n 0 -e TGeant3 --configKeyValues \"" << opts << "\" --field 0 -o " << geomNamePrefix; + gSystem->Exec(str.str().c_str()); } - o2::base::GeometryManager::loadGeometry(geomNameInput); + o2::base::GeometryManager::loadGeometry(geomNamePrefix); configLayers(); if (maxLr < 1) { @@ -66,7 +76,7 @@ bool buildMatBudLUT(int nTst, int maxLr, const std::string& outFile, const std:: } TStopwatch sw; - mbLUT.populateFromTGeo(nTst); + mbLUT.populateFromTGeo(nTst, nThreads, backend); mbLUT.optimizePhiSlices(); // move to populateFromTGeo mbLUT.flatten(); // move to populateFromTGeo @@ -187,11 +197,12 @@ void configLayers() o2::itsmft::ChipMappingITS mp; int nStave = 0; - // rMin rMax zHalf - lrData.emplace_back(LrData(0.0f, 1.8f, 30.f)); + // rMin rMax zHalf + lrData.emplace_back(LrData(0.0f, 1.8f, 50.f)); // beam pipe - lrData.emplace_back(LrData(lrData.back().rMax, 2.0f, 30.f)); + lrData.emplace_back(LrData(lrData.back().rMax, 1.92f, 50.f)); + lrData.emplace_back(LrData(lrData.back().rMax, 2.2f, 50.f)); // ITS Inner Barrel drStep = 0.1; @@ -200,7 +211,7 @@ void configLayers() zBin = 0.5; do { lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 5.2 + kToler); + } while (lrData.back().rMax < 5 - kToler); // air space between Inner and Middle Barrels zSpanH = 40.; @@ -213,19 +224,19 @@ void configLayers() nStave = mp.getNStavesOnLr(3); // Lr 3 zSpanH = 55.; zBin = 0.5; - drStep = 0.2; + drStep = 0.3; do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 20.5 + kToler); + } while (lrData.back().rMax < 21.4 - kToler); drStep = 0.5; do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 24. + kToler); + } while (lrData.back().rMax < 23.4 - kToler); nStave = mp.getNStavesOnLr(3); // Lr 4 drStep = 0.2; @@ -233,19 +244,21 @@ void configLayers() auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 25.6 + kToler); + } while (lrData.back().rMax < 26.2 - kToler); drStep = 0.5; do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 29. + kToler); + } while (lrData.back().rMax < 29. - kToler); //=================================================================================== // air space between Middle and Outer Barrels zSpanH = 80.f; - lrData.emplace_back(LrData(lrData.back().rMax, 33.5, zSpanH)); + zBin = 10.; + rphiBin = lrData.back().rMax * TMath::Pi() * 2 / 18; + lrData.emplace_back(LrData(lrData.back().rMax, 33.5, zSpanH, zBin, rphiBin)); //=================================================================================== // ITS Outer barrel @@ -255,59 +268,71 @@ void configLayers() zBin = 1.; do { auto rmean = lrData.back().rMax + drStep / 2; - rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); + rphiBin = rmean * TMath::Pi() * 2 / (nStave * 15); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 36. + kToler); + } while (lrData.back().rMax < 36. - kToler); drStep = 1.; do { auto rmean = lrData.back().rMax + drStep / 2; - rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); + rphiBin = rmean * TMath::Pi() * 2 / (nStave * 15); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 38.5 + kToler); + } while (lrData.back().rMax < 38.5 - kToler); nStave = mp.getNStavesOnLr(6); // Lr 6 drStep = 0.25; do { auto rmean = lrData.back().rMax + drStep / 2; - rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); + rphiBin = rmean * TMath::Pi() * 2 / (nStave * 15); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 41. + kToler); + } while (lrData.back().rMax < 41. - kToler); drStep = 1.; do { auto rmean = lrData.back().rMax + drStep / 2; - rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); + rphiBin = rmean * TMath::Pi() * 2 / (nStave * 15); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 44. + kToler); + } while (lrData.back().rMax < 44. - kToler); //=================================================================================== zSpanH = 100.f; zBin = 5.; - lrData.emplace_back(LrData(lrData.back().rMax, 47., zSpanH, zBin)); + lrData.emplace_back(LrData(lrData.back().rMax, 44.8, zSpanH, zBin)); + lrData.emplace_back(LrData(lrData.back().rMax, 46.2, zSpanH, zBin)); + lrData.emplace_back(LrData(lrData.back().rMax, 47.0, zSpanH, zBin)); drStep = 2.; zBin = 5.; rphiBin = 2.; do { lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 53. + kToler); + } while (lrData.back().rMax < 55. - kToler); zSpanH = 120.f; - lrData.emplace_back(LrData(lrData.back().rMax, 56.5, zSpanH)); + zBin = 10.; + rphiBin = lrData.back().rMax * TMath::Pi() * 2 / 18; + lrData.emplace_back(LrData(lrData.back().rMax, 56.5, zSpanH, zBin, rphiBin)); + + //------------------------------------ + zBin = 1.; + rphiBin = lrData.back().rMax * TMath::Pi() * 2 / 18; + lrData.emplace_back(LrData(lrData.back().rMax, 60.5, zSpanH, zBin, rphiBin)); + rphiBin = lrData.back().rMax * TMath::Pi() * 2 / 18; + lrData.emplace_back(LrData(lrData.back().rMax, 61.5, zSpanH, zBin, rphiBin)); zSpanH = 150.f; - drStep = 4.; - zBin = 15.; - rphiBin = 10; + drStep = 2; + zBin = 1.; do { + auto rmean = lrData.back().rMax + drStep / 2; + rphiBin = rmean * TMath::Pi() * 2 / (NSect * 2); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 68.5 + kToler); + } while (lrData.back().rMax < 68.5 - kToler); zSpanH = 250.f; - zBin = 25.; - rphiBin = 5; + zBin = 1.; + rphiBin = 2.5; { auto rmean = (lrData.back().rMax + 76) / 2.; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 2); @@ -320,14 +345,14 @@ void configLayers() { auto rmean = (lrData.back().rMax + 78.5) / 2; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); - lrData.emplace_back(LrData(lrData.back().rMax, 78.5, zSpanH, zBin, rphiBin)); + lrData.emplace_back(LrData(lrData.back().rMax, 78.8, zSpanH, zBin, rphiBin)); } // zSpanH = 250.f; zBin = 2; { auto rmean = (lrData.back().rMax + 78.5) / 2; - rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); + rphiBin = rmean * TMath::Pi() * 2 / (NSect * 24); lrData.emplace_back(LrData(lrData.back().rMax, 84.5, zSpanH, zBin, rphiBin)); } @@ -382,4 +407,18 @@ void configLayers() } while (lrData.back().rMax < 500); } -#endif //!_COMPILED_ON_GPU_ +//_______________________________________________________________________ +MatbudGeomBackend parseBackend(const std::string& s) +{ + if (s == "ROOT") { + return MatbudGeomBackend::ROOT; + } + if (s == "VECGEOM") { + if (!o2::base::GeometryManager::isVecGeomAvailable()) { + LOG(fatal) << "geomBackend=VECGEOM requested but O2 was built without VecGeom support (TGeo2VecGeom not found at configure time)"; + } + return MatbudGeomBackend::VECGEOM; + } + LOG(fatal) << "Unknown geomBackend '" << s << "', expected ROOT or VECGEOM"; + return MatbudGeomBackend::ROOT; +} diff --git a/Detectors/Base/test/compareMatBudLUT.C b/Detectors/Base/test/compareMatBudLUT.C new file mode 100644 index 0000000000000..a58695d05d22c --- /dev/null +++ b/Detectors/Base/test/compareMatBudLUT.C @@ -0,0 +1,153 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file compareMatBudLUT.C +/// \brief Compare two material budget LUTs cell by cell +/// +/// Used to check that filling the LUT in parallel gives the same result as filling it serially, +/// or that the VecGeom and ROOT geometry backends agree within a given tolerance: +/// +/// root -b -q 'compareMatBudLUT.C("matbud_serial.root","matbud_parallel.root")' +/// root -b -q 'compareMatBudLUT.C("matbud_ROOT.root","matbud_VECGEOM.root", 0.01, 20, "sweep.csv")' + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "DetectorsBase/MatLayerCylSet.h" +#include "GPUCommonLogger.h" +#include +#include +#include +#include +#include +#endif + +namespace +{ +struct CellDiff { + int layer, iz, ip; + float rhoA, rhoB, x2x0A, x2x0B; + double rRho, rX; + double score() const { return std::max(rRho, rX); } +}; + +struct LayerStat { + float rMin = 0.f, rMax = 0.f; + size_t nBad = 0; + double maxRelRho = 0., maxRelX2X0 = 0.; +}; +} // namespace + +/// Returns true if the two LUTs agree everywhere within tol (relative). +/// nWorst: number of worst-offending cells to print, ranked by max(relRho, relX2X0) over the +/// whole comparison, not just the first ones found in scan order. +/// csvSummary: if non-empty, append one summary row to this CSV file (header written once). +bool compareMatBudLUT(const std::string& fileA = "matbud_serial.root", + const std::string& fileB = "matbud_parallel.root", + float tol = 0.f, + int nWorst = 10, + const std::string& csvSummary = "") +{ + auto* lutA = o2::base::MatLayerCylSet::loadFromFile(fileA); + auto* lutB = o2::base::MatLayerCylSet::loadFromFile(fileB); + if (!lutA) { + LOG(error) << "Failed to load LUT from " << fileA; + return false; + } + if (!lutB) { + LOG(error) << "Failed to load LUT from " << fileB; + return false; + } + + if (lutA->getNLayers() != lutB->getNLayers()) { + LOG(error) << "Layer count differs: " << lutA->getNLayers() << " vs " << lutB->getNLayers(); + return false; + } + + size_t nCells = 0, nBad = 0; + double maxRelRho = 0., maxRelX2X0 = 0.; + std::vector layerStats(lutA->getNLayers()); + std::vector allCells; + + for (int il = 0; il < lutA->getNLayers(); il++) { + const auto& la = lutA->getLayer(il); + const auto& lb = lutB->getLayer(il); + if (la.getNZBins() != lb.getNZBins() || la.getNPhiBins() != lb.getNPhiBins()) { + LOG(error) << "Layer " << il << " segmentation differs: " + << la.getNZBins() << "x" << la.getNPhiBins() << " vs " + << lb.getNZBins() << "x" << lb.getNPhiBins(); + return false; + } + auto& ls = layerStats[il]; + ls.rMin = la.getRMin(); + ls.rMax = la.getRMax(); + + for (int iz = 0; iz < la.getNZBins(); iz++) { + for (int ip = 0; ip < la.getNPhiBins(); ip++) { + const auto& ca = la.getCellPhiBin(ip, iz); + const auto& cb = lb.getCellPhiBin(ip, iz); + nCells++; + + auto rel = [](float a, float b) { + const float den = std::max(std::abs(a), std::abs(b)); + return den > 0.f ? std::abs(a - b) / den : 0.f; + }; + const double rRho = rel(ca.meanRho, cb.meanRho); + const double rX = rel(ca.meanX2X0, cb.meanX2X0); + maxRelRho = std::max(maxRelRho, rRho); + maxRelX2X0 = std::max(maxRelX2X0, rX); + ls.maxRelRho = std::max(ls.maxRelRho, rRho); + ls.maxRelX2X0 = std::max(ls.maxRelX2X0, rX); + + if (rRho > tol || rX > tol) { + ls.nBad++; + nBad++; + } + allCells.push_back({il, iz, ip, ca.meanRho, cb.meanRho, ca.meanX2X0, cb.meanX2X0, rRho, rX}); + } + } + } + + const int nw = std::min(nWorst, (int)allCells.size()); + std::partial_sort(allCells.begin(), allCells.begin() + nw, allCells.end(), + [](const CellDiff& a, const CellDiff& b) { return a.score() > b.score(); }); + printf("--- %d worst cells (by max relative deviation) ---\n", nw); + for (int i = 0; i < nw; i++) { + const auto& c = allCells[i]; + printf("Lr %3d iz %4d ip %4d : rho %.9g vs %.9g (rel %.3g) | x2x0 %.9g vs %.9g (rel %.3g)\n", + c.layer, c.iz, c.ip, c.rhoA, c.rhoB, c.rRho, c.x2x0A, c.x2x0B, c.rX); + } + + printf("--- per-layer summary (%d layers) ---\n", lutA->getNLayers()); + for (int il = 0; il < lutA->getNLayers(); il++) { + const auto& ls = layerStats[il]; + printf("Lr %3d %8.3fgetNLayers()); + printf("Max relative difference: meanRho %.3g, meanX2X0 %.3g (tolerance %.3g)\n", maxRelRho, maxRelX2X0, tol); + + if (!csvSummary.empty()) { + const bool writeHeader = gSystem->AccessPathName(csvSummary.c_str()); // true if it does NOT exist + std::ofstream csv(csvSummary, std::ios::app); + if (writeHeader) { + csv << "fileA,fileB,nLayers,nCells,nBad,maxRelRho,maxRelX2X0,tol\n"; + } + csv << fileA << "," << fileB << "," << lutA->getNLayers() << "," << nCells << "," << nBad << "," + << maxRelRho << "," << maxRelX2X0 << "," << tol << "\n"; + } + + if (nBad) { + LOG(error) << nBad << " cells differ beyond tolerance"; + return false; + } + LOG(info) << "LUTs agree"; + return true; +} diff --git a/Detectors/Base/test/extractLUTLayers.C b/Detectors/Base/test/extractLUTLayers.C new file mode 100644 index 0000000000000..113a12e6a07a5 --- /dev/null +++ b/Detectors/Base/test/extractLUTLayers.C @@ -0,0 +1,55 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "DetectorsBase/MatLayerCylSet.h" +#include "Framework/Logger.h" +#include "CCDB/BasicCCDBManager.h" +#include +#endif + +// Macro to extract layers covering selected radial range into the separate LUT file. + +o2::base::MatLayerCylSet* extractLUTLayers(const o2::base::MatLayerCylSet* src, const std::string& outName, float rmin = 0., float rmax = 84., float toler = 1e-6) +{ + auto* cp = src->extractCopy(rmin, rmax, toler); + if (!cp) { + LOGP(error, "failed to extract layers for {} < r < {}", rmin, rmax); + } + if (outName.size()) { + cp->writeToFile(outName); + } + return cp; +} + +void extractLUTLayers(const std::string& fname, float rmin = 0., float rmax = 84., float toler = 1e-6) +{ + const auto src = o2::base::MatLayerCylSet::loadFromFile(fname); + if (!src) { + LOGP(error, "failed to open source LUT from {}", fname); + return; + } + auto fnameOut = std::regex_replace(fname, std::regex(R"(.root)"), fmt::format("_r{}_{}.root", rmin, rmax)); + auto cp = extractLUTLayers(src, fnameOut, rmin, rmax, toler); +} + +void extractLUTLayers(long timestamp, float rmin = 0., float rmax = 84., float toler = 1e-6) +{ + auto& mg = o2::ccdb::BasicCCDBManager::instance(); + mg.setTimestamp(timestamp); + const auto src = o2::base::MatLayerCylSet::rectifyPtrFromFile(mg.get("GLO/Param/MatLUT")); + if (!src) { + LOGP(error, "failed to open load LUT from CCDB for timestamp {}", timestamp); + return; + } + auto fnameOut = fmt::format("matbudLUT_ts{}_r{}_{}.root", timestamp, rmin, rmax); + auto cp = extractLUTLayers(src, fnameOut, rmin, rmax, toler); +} diff --git a/Detectors/Base/test/rescaleLUT.C b/Detectors/Base/test/rescaleLUT.C new file mode 100644 index 0000000000000..9e25c796e43d1 --- /dev/null +++ b/Detectors/Base/test/rescaleLUT.C @@ -0,0 +1,82 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "DetectorsBase/MatLayerCylSet.h" +#include "Framework/Logger.h" +#include "CCDB/BasicCCDBManager.h" +#include +#endif + +// Macro to extract layers covering selected radial range into the separate LUT file. + +void rescaleLUT(o2::base::MatLayerCylSet* src, const std::string& outName) +{ + struct RescRange { + float rMin, rMax, factor; + }; + std::vector task = { + // put here radial ranges in increasing order with corresponding factors to rescale + {0.1f, 6.f, 1.05}, // e.g. rescale layers covering 0.1 rmax) { + LOGP(error, "rMax={:.2f} of range {} is larger then rMin={:.2f} of range {}, must be in increasing order", rmin, il - 1, rmax, il); + return; + } + o2::base::Ray ray(std::max(src->getRMin(), rmin), 0., 0., std::min(src->getRMax(), rmax), 0., 0.); + if (!src->getLayersRange(ray, lmin, lmax)) { + LOGP(error, "No layers found for {:.2f} < r < {:.2f}", rmin, rmax); + return; + } + if (lmin == lmax) { + LOGP(error, "rMax={:.2f} of range {} and rMin={:.2f} of range {}, correspond to the same slice {} with {:.2f}getLayer(lmin).getRMin(), src->getLayer(lmin).getRMax()); + return; + } + } + + for (size_t il = 0; il < task.size(); il++) { + src->scaleLayersByR(task[il].rMin, task[il].rMax, task[il].factor); + } + if (outName.size()) { + src->writeToFile(outName); + } +} + +void rescaleLUT(const std::string& fname) +{ + auto src = o2::base::MatLayerCylSet::loadFromFile(fname); + if (!src) { + LOGP(error, "failed to open source LUT from {}", fname); + return; + } + auto fnameOut = std::regex_replace(fname, std::regex(R"(.root)"), "_rescaled.root"); + rescaleLUT(src, fnameOut); +} + +void rescaleLUT(long timestamp = -1) +{ + auto& mg = o2::ccdb::BasicCCDBManager::instance(); + mg.setTimestamp(timestamp); + auto src = o2::base::MatLayerCylSet::rectifyPtrFromFile(mg.get("GLO/Param/MatLUT")); + if (!src) { + LOGP(error, "failed to open load LUT from CCDB for timestamp {}", timestamp); + return; + } + auto fnameOut = fmt::format("matbudLUT_ts{}_rescaled.root", timestamp); + rescaleLUT(src, fnameOut); +} diff --git a/Detectors/Base/test/testDCAFitter.cxx b/Detectors/Base/test/testDCAFitter.cxx deleted file mode 100644 index 60e0f08f4a5a1..0000000000000 --- a/Detectors/Base/test/testDCAFitter.cxx +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#define BOOST_TEST_MODULE Test DCAFitter class -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include - -#include "DetectorsBase/DCAFitter.h" -#include - -namespace o2 -{ -namespace base -{ - -void checkResults(const DCAFitter& fitter, const std::array xyz) -{ - int nCand = fitter.getNCandidates(); - BOOST_CHECK(nCand > 0); - for (int ic = 0; ic < nCand; ic++) { - const auto& vtx = fitter.getPCACandidate(ic); - float dx = vtx.x - xyz[0], dy = vtx.y - xyz[1], dz = vtx.z - xyz[2]; - float dst = TMath::Sqrt(dx * dx + dy * dy + dz * dz); - - const auto &trc0 = fitter.getTrack0(ic), &trc1 = fitter.getTrack1(ic); // track parameters at V0 - printf("Candidate %d: DCA:%+e Vtx: %+e %+e %+e [Diff to true: %+e %+e %+e -> %+e]\n", - ic, fitter.getChi2AtPCACandidate(ic), vtx.x, vtx.y, vtx.z, dx, dy, dz, dst); - printf("Track X-parameters at PCA: %+e %+e\n", trc0.getX(), trc1.getX()); - trc0.print(); - trc1.print(); - BOOST_CHECK(dst < 5e-2); - } -} - -BOOST_AUTO_TEST_CASE(PairDCAFitter) -{ - double bz = 5.0; - - // create V0 - std::array cv = {1e-6, 0, 1e-6, 0, 0, 1e-6, 0, 0, 0, 1e-6, 0, 0, 0, 0, 1e-5}; - std::array pr = {0., 0., -0.2, 0.6, 1.}; - DCAFitter::Track t0(0., 0., pr, cv); - t0.propagateTo(10, bz); - - std::array xyz; - t0.getXYZGlo(xyz); - printf("true vertex : %+e %+e %+e\n", xyz[0], xyz[1], xyz[2]); - - DCAFitter::Track tA(t0), tB(t0); - tB.setParam(-tA.getParam(4), 4); - tB.setParam(-0.5 * tA.getParam(3), 3); - tB.setParam(-0.2 * tA.getParam(2), 2); - - tA.rotate(-0.3); - tB.rotate(0.3); - - printf("True track params at PCA:\n"); - tA.print(); - tB.print(); - - tA.propagateTo(tA.getX() + 5., bz); - tB.propagateTo(tB.getX() + 8., bz); - - DCAFitter df(bz, 10.); - - // check with abs DCA minimization - { - df.setUseAbsDCA(true); - // we may supply track directly to the fitter (they are not modified) - int nCand = df.process(tA, tB); - printf("\n\nTesting with abs DCA minimization: %d candidates found\n", nCand); - // we can have up to 2 candidates - checkResults(df, xyz); - } - - // check with weighted DCA minimization - { - df.setUseAbsDCA(false); - // we may supply track directly to the fitter (they are not modified) - DCAFitter::TrcAuxPar trcAAux(tA, bz), trcBAux(tB, bz); - int nCand = df.process(tA, trcAAux, tB, trcBAux); - printf("\n\nTesting with abs DCA minimization: %d candidates found\n", nCand); - // we can have up to 2 candidates - checkResults(df, xyz); - } - - { - // direct minimization w/o preliminary propagation to XY crossing - df.setUseAbsDCA(true); - int nCand = df.processAsIs(tA, tB); - printf("\n\nTesting with abs DCA minimization w/o taking to XY crossing: %d candidates found\n", nCand); - checkResults(df, xyz); - } - - { - // direct minimization w/o preliminary propagation to XY crossing (errors unreliable) - df.setUseAbsDCA(false); - int nCand = df.processAsIs(tA, tB); - printf("\n\nTesting with weighted DCA minimization w/o taking to XY crossing: %d candidates found\n", nCand); - checkResults(df, xyz); - } -} - -} // namespace base -} // namespace o2 diff --git a/Detectors/Base/test/testMatBudLUT.cxx b/Detectors/Base/test/testMatBudLUT.cxx index d332422035cd6..33c3498995c90 100644 --- a/Detectors/Base/test/testMatBudLUT.cxx +++ b/Detectors/Base/test/testMatBudLUT.cxx @@ -13,6 +13,7 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK #include +#include #include "buildMatBudLUT.C" @@ -20,11 +21,12 @@ namespace o2 { BOOST_AUTO_TEST_CASE(MatBudLUT) { -#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version - - BOOST_CHECK(buildMatBudLUT(2, 20)); // generate LUT - BOOST_CHECK(testMBLUT()); // test LUT manipulations - -#endif //!GPUCA_ALIGPUCODE + // using process specific geometry names in order + // to avoid race/conditions with other tests accessing geometry + std::string geomPrefix("matBudGeom"); + std::string matBudFile("matbud"); + matBudFile += std::to_string(getpid()) + ".root"; + BOOST_CHECK(buildMatBudLUT(2, 20, matBudFile, geomPrefix + std::to_string(getpid()), "align-geom.mDetectors=none")); // generate LUT + BOOST_CHECK(testMBLUT(matBudFile)); // test LUT manipulations } } // namespace o2 diff --git a/Detectors/Base/test/testStack.cxx b/Detectors/Base/test/testStack.cxx index 150fb9515c5f1..6d5349711e437 100644 --- a/Detectors/Base/test/testStack.cxx +++ b/Detectors/Base/test/testStack.cxx @@ -44,3 +44,47 @@ BOOST_AUTO_TEST_CASE(Stack_test) BOOST_CHECK(inst->getPrimaries().size() == 2); } } + +// convenience wrapper to push a track and return the assigned trackID +static int pushTrack(o2::data::Stack& st, int parentId, TMCProcess proc) +{ + int trackId; + st.PushTrack(1, parentId, 0, 0., 0., 0., 10., 5., 5., 5., 0.1, 0., 0., 0., proc, trackId, 1., 1); + return trackId; +} + +// unit test for the radioactive-decay ancestry query +BOOST_AUTO_TEST_CASE(Stack_isFromRadDecay_test) +{ + o2::data::Stack st; + + // two primaries; note that primaries do not enter mParticles, only secondaries do + const auto prim0 = pushTrack(st, -1, kPPrimary); + const auto prim1 = pushTrack(st, -1, kPPrimary); + + // a radioactive decay product of the second primary, and its descendants. + // this is deliberately the *first* secondary of the primary, so that it lands + // in the first entry of the particle buffer + const auto radDecay = pushTrack(st, prim1, kPRadDecay); + const auto radChild = pushTrack(st, radDecay, kPHadronic); + const auto radGrandChild = pushTrack(st, radChild, kPHadronic); + + // a plain secondary of the second primary: no radioactive decay anywhere in its history + const auto ordinary = pushTrack(st, prim1, kPHadronic); + + // primaries can never come from a radioactive decay + BOOST_CHECK(!st.isFromRadDecay(prim0)); + BOOST_CHECK(!st.isFromRadDecay(prim1)); + + // a secondary whose ancestry ends in a primary must terminate the search with false + BOOST_CHECK(!st.isFromRadDecay(ordinary)); + + // directly and indirectly from a radioactive decay + BOOST_CHECK(st.isFromRadDecay(radDecay)); + BOOST_CHECK(st.isFromRadDecay(radChild)); + BOOST_CHECK(st.isFromRadDecay(radGrandChild)); + + // out-of-range track IDs are rejected rather than looked up + BOOST_CHECK(!st.isFromRadDecay(-1)); + BOOST_CHECK(!st.isFromRadDecay(1000000000)); +} diff --git a/Detectors/CMakeLists.txt b/Detectors/CMakeLists.txt index d96c02ec42625..9143761508998 100644 --- a/Detectors/CMakeLists.txt +++ b/Detectors/CMakeLists.txt @@ -25,6 +25,7 @@ add_subdirectory(TOF) add_subdirectory(ZDC) add_subdirectory(ITSMFT) +add_subdirectory(External) # sensitive external (CAD-derived) detectors; uses ITSMFT hit type add_subdirectory(TRD) add_subdirectory(MUON) @@ -36,7 +37,8 @@ add_subdirectory(FOCAL) add_subdirectory(GlobalTracking) add_subdirectory(GlobalTrackingWorkflow) add_subdirectory(Vertexing) -add_subdirectory(StrangenessTracking) +add_subdirectory(GLOQC) + if(BUILD_ANALYSIS) add_subdirectory(AOD) endif() @@ -47,6 +49,8 @@ add_subdirectory(Calibration) add_subdirectory(DCS) add_subdirectory(Align) +add_subdirectory(ForwardAlign) + if(BUILD_SIMULATION) add_subdirectory(gconfig) diff --git a/Detectors/CPV/base/src/Geometry.cxx b/Detectors/CPV/base/src/Geometry.cxx index b31b57cb19be1..08bb5504e01da 100644 --- a/Detectors/CPV/base/src/Geometry.cxx +++ b/Detectors/CPV/base/src/Geometry.cxx @@ -68,7 +68,8 @@ short Geometry::areNeighbours(unsigned short absId1, unsigned short absId2) short rowdiff = TMath::Abs(relId1[1] - relId2[1]); short coldiff = TMath::Abs(relId1[2] - relId2[2]); - if ((coldiff <= 1) && (rowdiff <= 1)) { // At least common vertex + // if ((coldiff <= 1) && (rowdiff <= 1)) { // At least common vertex + if (coldiff + rowdiff <= 1) { // Common side return 1; } else { if ((relId2[1] > relId1[1]) && (relId2[2] > relId1[2] + 1)) { diff --git a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVBadMapCalibDevice.cxx b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVBadMapCalibDevice.cxx index b1c61d25da9c8..f2a08e280f954 100644 --- a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVBadMapCalibDevice.cxx +++ b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVBadMapCalibDevice.cxx @@ -174,7 +174,7 @@ void CPVBadMapCalibDevice::sendOutput(DataAllocator& output) output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "CPV_BadChanMap", subSpec}, info); } - output.snapshot(o2::framework::Output{"CPV", "BADMAPCHANGE", 0, o2::framework::Lifetime::Timeframe}, mMapDiff); + output.snapshot(o2::framework::Output{"CPV", "BADMAPCHANGE", 0}, mMapDiff); } bool CPVBadMapCalibDevice::differFromCurrent() diff --git a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVGainCalibDevice.cxx b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVGainCalibDevice.cxx index b5e98f00d021f..d7f187e3a88cc 100644 --- a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVGainCalibDevice.cxx +++ b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVGainCalibDevice.cxx @@ -143,7 +143,7 @@ void CPVGainCalibDevice::sendOutput(DataAllocator& output) fout.Close(); } // Anyway send change to QC - output.snapshot(o2::framework::Output{"CPV", "GAINDIFF", 0, o2::framework::Lifetime::Timeframe}, mGainRatio); + output.snapshot(o2::framework::Output{"CPV", "GAINDIFF", 0}, mGainRatio); } void CPVGainCalibDevice::calculateGains() diff --git a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVPedestalCalibDevice.cxx b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVPedestalCalibDevice.cxx index 125e8e1bc8c40..5dd414ab7aeb8 100644 --- a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVPedestalCalibDevice.cxx +++ b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVPedestalCalibDevice.cxx @@ -118,7 +118,7 @@ void CPVPedestalCalibDevice::sendOutput(DataAllocator& output) } // Anyway send change to QC LOG(info) << "[CPVPedestalCalibDevice - run] Writing "; - output.snapshot(o2::framework::Output{"CPV", "PEDDIFF", 0, o2::framework::Lifetime::Timeframe}, mPedDiff); + output.snapshot(o2::framework::Output{"CPV", "PEDDIFF", 0}, mPedDiff); // Write pedestal distributions to calculate bad map std::string filename = mPath + "CPVPedestals.root"; diff --git a/Detectors/CPV/calib/macros/makeBadMapFromPedestalRun.C b/Detectors/CPV/calib/macros/makeBadMapFromPedestalRun.C index 3b5af09190fe7..9d62cf1a13baa 100644 --- a/Detectors/CPV/calib/macros/makeBadMapFromPedestalRun.C +++ b/Detectors/CPV/calib/macros/makeBadMapFromPedestalRun.C @@ -75,7 +75,7 @@ void makeBadMapFromPedestalRun(const char* ccdbURI = "http://ccdb-test.cern.ch:8 } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdbURI); // or http://localhost:8080 for a local installation api.storeAsTFileAny(&badMap, "CPV/Calib/BadChannelMap", metadata, timeStamp, timeStamp + 31536000000); } diff --git a/Detectors/CPV/calib/testWorkflow/GainCalibratorSpec.h b/Detectors/CPV/calib/testWorkflow/GainCalibratorSpec.h index 97c741bb0193d..71833d6ac9843 100644 --- a/Detectors/CPV/calib/testWorkflow/GainCalibratorSpec.h +++ b/Detectors/CPV/calib/testWorkflow/GainCalibratorSpec.h @@ -101,7 +101,7 @@ class CPVGainCalibratorSpec : public o2::framework::Task auto&& digits = pc.inputs().get>("calibdigs"); // fill statistics - LOG(info) << "Processing TF " << tfcounter << " with " << digits.size() << " digits"; + LOG(detail) << "Processing TF " << tfcounter << " with " << digits.size() << " digits"; mCalibrator->process(digits); // fill TimeSlot with digits from 1 event and check slots for finalization // inform about results and send output to ccdb diff --git a/Detectors/CPV/calib/testWorkflow/NoiseCalibratorSpec.h b/Detectors/CPV/calib/testWorkflow/NoiseCalibratorSpec.h index 3473664bfefef..f272bf9deb3d7 100644 --- a/Detectors/CPV/calib/testWorkflow/NoiseCalibratorSpec.h +++ b/Detectors/CPV/calib/testWorkflow/NoiseCalibratorSpec.h @@ -116,7 +116,7 @@ class CPVNoiseCalibratorSpec : public o2::framework::Task auto&& digits = pc.inputs().get>("digits"); auto&& trigrecs = pc.inputs().get>("trigrecs"); - LOG(info) << "Processing TF " << tfcounter << " with " << digits.size() << " digits in " << trigrecs.size() << " trigger records."; + LOG(detail) << "Processing TF " << tfcounter << " with " << digits.size() << " digits in " << trigrecs.size() << " trigger records."; auto& slotTF = mCalibrator->getSlotForTF(tfcounter); for (auto trigrec = trigrecs.begin(); trigrec != trigrecs.end(); trigrec++) { // event loop @@ -134,7 +134,7 @@ class CPVNoiseCalibratorSpec : public o2::framework::Task auto infoVecSize = mCalibrator->getCcdbInfoBadChannelMapVector().size(); auto badMapVecSize = mCalibrator->getBadChannelMapVector().size(); if (infoVecSize > 0) { - LOG(info) << "Created " << infoVecSize << " ccdb infos and " << badMapVecSize << " BadChannelMap objects for TF " << tfcounter; + LOG(detail) << "Created " << infoVecSize << " ccdb infos and " << badMapVecSize << " BadChannelMap objects for TF " << tfcounter; } sendOutput(pc.outputs()); } diff --git a/Detectors/CPV/calib/testWorkflow/PedestalCalibratorSpec.h b/Detectors/CPV/calib/testWorkflow/PedestalCalibratorSpec.h index 16d565e8e1d41..9e452e2fd8886 100644 --- a/Detectors/CPV/calib/testWorkflow/PedestalCalibratorSpec.h +++ b/Detectors/CPV/calib/testWorkflow/PedestalCalibratorSpec.h @@ -81,7 +81,7 @@ class CPVPedestalCalibratorSpec : public o2::framework::Task auto&& digits = pc.inputs().get>("digits"); auto&& trigrecs = pc.inputs().get>("trigrecs"); - LOG(info) << "Processing TF " << tfcounter << " with " << digits.size() << " digits in " << trigrecs.size() << " trigger records."; + LOG(detail) << "Processing TF " << tfcounter << " with " << digits.size() << " digits in " << trigrecs.size() << " trigger records."; auto& slotTF = mCalibrator->getSlotForTF(tfcounter); for (auto trigrec = trigrecs.begin(); trigrec != trigrecs.end(); trigrec++) { // event loop @@ -99,7 +99,7 @@ class CPVPedestalCalibratorSpec : public o2::framework::Task auto infoVecSize = mCalibrator->getCcdbInfoPedestalsVector().size(); auto pedsVecSize = mCalibrator->getPedestalsVector().size(); if (infoVecSize > 0) { - LOG(info) << "Created " << infoVecSize << " ccdb infos and " << pedsVecSize << " pedestal objects for TF " << tfcounter; + LOG(detail) << "Created " << infoVecSize << " ccdb infos and " << pedsVecSize << " pedestal objects for TF " << tfcounter; } sendOutput(pc.outputs()); } diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h index 31f4227742385..4e259c24f44a6 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h @@ -23,7 +23,6 @@ #include "DataFormatsCPV/CTF.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/CTFCoderBase.h" -#include "rANS/rans.h" #include "CPVReconstruction/CTFHelper.h" class TTree; @@ -33,10 +32,10 @@ namespace o2 namespace cpv { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CPV) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CPV, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF @@ -47,7 +46,7 @@ class CTFCoder : public o2::ctf::CTFCoderBase template o2::ctf::CTFIOSize decode(const CTF::base& ec, VTRG& trigVec, VCLUSTER& cluVec); - void createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) final; + void createCoders(const std::vector& bufVec, ctf::CTFCoderBase::OpType op) final; private: template @@ -86,13 +85,13 @@ o2::ctf::CTFIOSize CTFCoder::encode_impl(VEC& buff, const gsl::spansetHeader(helper.createHeader()); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODECPV(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODECPV(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODECPV(helper.begin_bcIncTrig(), helper.end_bcIncTrig(), CTF::BLC_bcIncTrig, 0); iosize += ENCODECPV(helper.begin_orbitIncTrig(), helper.end_orbitIncTrig(), CTF::BLC_orbitIncTrig, 0); @@ -134,12 +132,13 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VCLUSTER auto header = ec.getHeader(); checkDictVersion(static_cast(header)); ec.print(getPrefix(), mVerbosity); - std::vector bcInc, entries, posX, posZ; - std::vector orbitInc; + std::vector bcInc; + std::vector orbitInc; + std::vector entries, posX, posZ; std::vector energy, status; o2::ctf::CTFIOSize iosize; -#define DECODECPV(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODECPV(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODECPV(bcInc, CTF::BLC_bcIncTrig); iosize += DECODECPV(orbitInc, CTF::BLC_orbitIncTrig); diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h index 607d426d11c89..0c86c7ccc7f17 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h @@ -50,7 +50,7 @@ class CTFHelper class _Iter { public: - using difference_type = int64_t; + using difference_type = std::ptrdiff_t; using value_type = T; using pointer = const T*; using reference = const T&; @@ -59,92 +59,86 @@ class CTFHelper _Iter(const gsl::span& data, bool end = false) : mData(data), mIndex(end ? data.size() : 0){}; _Iter() = default; - const I& operator++() + inline I& operator++() noexcept { ++mIndex; - return (I&)(*this); + return static_cast(*this); } - const I& operator--() + inline I operator++(int) { - mIndex--; - return (I&)(*this); + I res = *(static_cast(this)); + ++mIndex; + return res; } - const I operator++(int) + inline I& operator--() noexcept { - auto res = *this; - ++mIndex; - return res; + mIndex--; + return static_cast(*this); } - const I operator--(int) + inline I operator--(int) { - auto res = *this; + I res = *(static_cast(this)); --mIndex; return res; } - const I& operator+=(difference_type i) + I& operator+=(difference_type i) noexcept { mIndex += i; - return (I&)(*this); + return static_cast(*this); } - const I operator+=(difference_type i) const + I operator+(difference_type i) const { - auto tmp = *const_cast(this); - return tmp += i; + I res = *(const_cast(static_cast(this))); + return res += i; } - const I& operator-=(difference_type i) + I& operator-=(difference_type i) noexcept { mIndex -= i; - return (I&)(*this); + return static_cast(*this); } - const I operator-=(difference_type i) const + I operator-(difference_type i) const { - auto tmp = *const_cast(this); - return tmp -= i; + I res = *(const_cast(static_cast(this))); + return res -= i; } - difference_type operator-(const I& other) const { return mIndex - other.mIndex; } - - difference_type operator-(size_t idx) const { return mIndex - idx; } + difference_type operator-(const I& other) const noexcept { return mIndex - other.mIndex; } - const I& operator-(size_t idx) - { - mIndex -= idx; - return (I&)(*this); - } + inline friend I operator+(difference_type i, const I& iter) { return iter + i; }; - bool operator!=(const I& other) const { return mIndex != other.mIndex; } - bool operator==(const I& other) const { return mIndex == other.mIndex; } - bool operator>(const I& other) const { return mIndex > other.mIndex; } - bool operator<(const I& other) const { return mIndex < other.mIndex; } - bool operator>=(const I& other) const { return mIndex >= other.mIndex; } - bool operator<=(const I& other) const { return mIndex <= other.mIndex; } + bool operator!=(const I& other) const noexcept { return mIndex != other.mIndex; } + bool operator==(const I& other) const noexcept { return mIndex == other.mIndex; } + bool operator>(const I& other) const noexcept { return mIndex > other.mIndex; } + bool operator<(const I& other) const noexcept { return mIndex < other.mIndex; } + bool operator>=(const I& other) const noexcept { return mIndex >= other.mIndex; } + bool operator<=(const I& other) const noexcept { return mIndex <= other.mIndex; } protected: gsl::span mData{}; - size_t mIndex = 0; + difference_type mIndex = 0; }; //_______________________________________________ // BC difference wrt previous if in the same orbit, otherwise the abs.value. // For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_bcIncTrig : public _Iter + class Iter_bcIncTrig : public _Iter { public: - using _Iter::_Iter; + using _Iter::_Iter; value_type operator*() const { if (mIndex) { if (mData[mIndex].getBCData().orbit == mData[mIndex - 1].getBCData().orbit) { - return mData[mIndex].getBCData().bc - mData[mIndex - 1].getBCData().bc; + return value_type(mData[mIndex].getBCData().bc - mData[mIndex - 1].getBCData().bc); } else { - return mData[mIndex].getBCData().bc; + return value_type(mData[mIndex].getBCData().bc); } } return 0; @@ -154,9 +148,9 @@ class CTFHelper size_t id = mIndex + i; if (id) { if (mData[id].getBCData().orbit == mData[id - 1].getBCData().orbit) { - return mData[id].getBCData().bc - mData[id - 1].getBCData().bc; + return value_type(mData[id].getBCData().bc - mData[id - 1].getBCData().bc); } else { - return mData[id].getBCData().bc; + return value_type(mData[id].getBCData().bc); } } return 0; @@ -165,15 +159,15 @@ class CTFHelper //_______________________________________________ // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_orbitIncTrig : public _Iter + class Iter_orbitIncTrig : public _Iter { public: - using _Iter::_Iter; - value_type operator*() const { return mIndex ? mData[mIndex].getBCData().orbit - mData[mIndex - 1].getBCData().orbit : 0; } + using _Iter::_Iter; + value_type operator*() const { return value_type(mIndex ? mData[mIndex].getBCData().orbit - mData[mIndex - 1].getBCData().orbit : 0); } value_type operator[](difference_type i) const { size_t id = mIndex + i; - return id ? mData[id].getBCData().orbit - mData[id - 1].getBCData().orbit : 0; + return value_type(id ? mData[id].getBCData().orbit - mData[id - 1].getBCData().orbit : 0); } }; @@ -206,10 +200,10 @@ class CTFHelper }; //_______________________________________________ - class Iter_energy : public _Iter + class Iter_energy : public _Iter { public: - using _Iter::_Iter; + using _Iter::_Iter; value_type operator*() const { return mData[mIndex].getPackedEnergy(); } value_type operator[](difference_type i) const { return mData[mIndex + i].getPackedEnergy(); } }; diff --git a/Detectors/CPV/reconstruction/src/CTFCoder.cxx b/Detectors/CPV/reconstruction/src/CTFCoder.cxx index 7c94e125dc449..b86706dee6729 100644 --- a/Detectors/CPV/reconstruction/src/CTFCoder.cxx +++ b/Detectors/CPV/reconstruction/src/CTFCoder.cxx @@ -41,10 +41,11 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); // just to get types - uint16_t bcInc = 0, entries = 0, cluPosX = 0, cluPosZ = 0; - uint32_t orbitInc = 0; + int16_t bcInc = 0; + int32_t orbitInc = 0; + uint16_t entries = 0, cluPosX = 0, cluPosZ = 0; uint8_t energy = 0, status = 0; -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(bcInc, CTF::BLC_bcIncTrig); MAKECODER(orbitInc, CTF::BLC_orbitIncTrig); diff --git a/Detectors/CPV/workflow/CMakeLists.txt b/Detectors/CPV/workflow/CMakeLists.txt index 04e4b075a7ec4..ba21452393b99 100644 --- a/Detectors/CPV/workflow/CMakeLists.txt +++ b/Detectors/CPV/workflow/CMakeLists.txt @@ -12,6 +12,8 @@ o2_add_library(CPVWorkflow SOURCES src/RecoWorkflow.cxx src/ReaderSpec.cxx + src/DigitReaderSpec.cxx + src/ClusterReaderSpec.cxx src/WriterSpec.cxx src/ClusterizerSpec.cxx src/DigitsPrinterSpec.cxx @@ -41,3 +43,13 @@ o2_add_executable(cluster-writer-workflow COMPONENT_NAME cpv SOURCES src/cluster-writer-workflow.cxx PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) + +o2_add_executable(digit-reader-workflow + COMPONENT_NAME cpv + SOURCES src/digit-reader-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) + +o2_add_executable(cluster-reader-workflow + COMPONENT_NAME cpv + SOURCES src/cluster-reader-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/ClusterReaderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/ClusterReaderSpec.h new file mode 100644 index 0000000000000..ee1c195b0aec5 --- /dev/null +++ b/Detectors/CPV/workflow/include/CPVWorkflow/ClusterReaderSpec.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClusterReaderSpec.h + +#ifndef O2_CPV_CLUSTERREADER +#define O2_CPV_CLUSTERREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "DataFormatsCPV/Cluster.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +namespace o2 +{ +namespace cpv +{ + +class ClusterReader : public o2::framework::Task +{ + public: + ClusterReader(bool useMC = true); + ~ClusterReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(const std::string& filename); + + std::vector mClusters, *mClustersInp = &mClusters; + std::vector mTRs, *mTRsInp = &mTRs; + o2::dataformats::MCTruthContainer mMCTruth, *mMCTruthInp = &mMCTruth; + + o2::header::DataOrigin mOrigin = o2::header::gDataOriginCPV; + + bool mUseMC = true; // use MC truth + + std::unique_ptr mFile; + std::unique_ptr mTree; + std::string mInputFileName = ""; + std::string mClusterTreeName = "o2sim"; + std::string mClusterBranchName = "CPVCluster"; + std::string mTRBranchName = "CPVClusterTrigRec"; + std::string mClusterMCTruthBranchName = "CPVClusterTrueMC"; +}; + +/// create a processor spec +/// read CPV Cluster data from a root file +framework::DataProcessorSpec getCPVClusterReaderSpec(bool useMC = true); + +} // namespace cpv +} // namespace o2 + +#endif /* O2_CPV_CLUSTERREADER */ diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/DigitReaderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/DigitReaderSpec.h new file mode 100644 index 0000000000000..00fce64f89125 --- /dev/null +++ b/Detectors/CPV/workflow/include/CPVWorkflow/DigitReaderSpec.h @@ -0,0 +1,69 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file DigitReaderSpec.h + +#ifndef O2_CPV_DIGITREADER +#define O2_CPV_DIGITREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "DataFormatsCPV/Digit.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +namespace o2 +{ +namespace cpv +{ + +class DigitReader : public o2::framework::Task +{ + public: + DigitReader(bool useMC = true); + ~DigitReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(const std::string& filename); + + std::vector mDigits, *mDigitsInp = &mDigits; + std::vector mTRs, *mTRsInp = &mTRs; + // std::vector mMCTruth, *mMCTruthInp = &mMCTruth; + o2::dataformats::MCTruthContainer mMCTruth, *mMCTruthInp = &mMCTruth; + + o2::header::DataOrigin mOrigin = o2::header::gDataOriginCPV; + + bool mUseMC = true; // use MC truth + + std::unique_ptr mFile; + std::unique_ptr mTree; + std::string mInputFileName = ""; + std::string mDigitTreeName = "o2sim"; + std::string mDigitBranchName = "CPVDigit"; + std::string mTRBranchName = "CPVDigitTrigRecords"; + std::string mDigitMCTruthBranchName = "CPVDigitMCTruth"; +}; + +/// create a processor spec +/// read CPV Digit data from a root file +framework::DataProcessorSpec getCPVDigitReaderSpec(bool useMC = true); + +} // namespace cpv +} // namespace o2 + +#endif /* O2_CPV_DIGITREADER */ diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h index 09de778360d74..7192b1b2f6353 100644 --- a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h +++ b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace cpv class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace cpv } // namespace o2 diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h index 24c229179fe1d..a1851ebb97377 100644 --- a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h +++ b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace cpv class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR = false); + EntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace cpv } // namespace o2 diff --git a/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx b/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx new file mode 100644 index 0000000000000..f9d0817325c36 --- /dev/null +++ b/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx @@ -0,0 +1,100 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClusterReaderSpec.cxx + +#include +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "CPVWorkflow/ClusterReaderSpec.h" +#include "CommonUtils/NameConf.h" + +using namespace o2::framework; +using namespace o2::cpv; + +namespace o2 +{ +namespace cpv +{ + +ClusterReader::ClusterReader(bool useMC) +{ + mUseMC = useMC; +} + +void ClusterReader::init(InitContext& ic) +{ + mInputFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), + ic.options().get("cpv-clusters-infile")); + connectTree(mInputFileName); +} + +void ClusterReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "Pushing " << mClusters.size() << " Clusters in " << mTRs.size() << " TriggerRecords at entry " << ent; + pc.outputs().snapshot(Output{mOrigin, "CLUSTERS", 0}, mClusters); + pc.outputs().snapshot(Output{mOrigin, "CLUSTERTRIGRECS", 0}, mTRs); + if (mUseMC) { + pc.outputs().snapshot(Output{mOrigin, "CLUSTERTRUEMC", 0}, mMCTruth); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void ClusterReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mClusterTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mTRBranchName.c_str())); + + mTree->SetBranchAddress(mTRBranchName.c_str(), &mTRsInp); + mTree->SetBranchAddress(mClusterBranchName.c_str(), &mClustersInp); + if (mUseMC) { + if (mTree->GetBranch(mClusterMCTruthBranchName.c_str())) { + mTree->SetBranchAddress(mClusterMCTruthBranchName.c_str(), &mMCTruthInp); + } else { + LOG(warning) << "MC-truth is missing, message will be empty"; + } + } + LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getCPVClusterReaderSpec(bool useMC) +{ + std::vector outputSpec; + outputSpec.emplace_back("CPV", "CLUSTERS", 0, Lifetime::Timeframe); + outputSpec.emplace_back("CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe); + if (useMC) { + outputSpec.emplace_back("CPV", "CLUSTERTRUEMC", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "cpv-cluster-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask(useMC)}, + Options{ + {"cpv-clusters-infile", VariantType::String, "cpvclusters.root", {"Name of the input Cluster file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}}}; +} + +} // namespace cpv +} // namespace o2 diff --git a/Detectors/CPV/workflow/src/ClusterizerSpec.cxx b/Detectors/CPV/workflow/src/ClusterizerSpec.cxx index 87d0b86a98f5c..e004c3cec8949 100644 --- a/Detectors/CPV/workflow/src/ClusterizerSpec.cxx +++ b/Detectors/CPV/workflow/src/ClusterizerSpec.cxx @@ -49,14 +49,14 @@ void ClusterizerSpec::run(framework::ProcessingContext& ctx) if (!digits.size()) { // nothing to process LOG(info) << "ClusterizerSpec::run() : no digits; moving on"; mOutputClusters.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERS", 0, o2::framework::Lifetime::Timeframe}, mOutputClusters); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERS", 0}, mOutputClusters); mOutputClusterTrigRecs.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRIGRECS", 0, o2::framework::Lifetime::Timeframe}, mOutputClusterTrigRecs); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRIGRECS", 0}, mOutputClusterTrigRecs); mCalibDigits.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CALIBDIGITS", 0, o2::framework::Lifetime::Timeframe}, mCalibDigits); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CALIBDIGITS", 0}, mCalibDigits); if (mPropagateMC) { mOutputTruthCont.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRUEMC", 0, o2::framework::Lifetime::Timeframe}, mOutputTruthCont); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRUEMC", 0}, mOutputTruthCont); } return; } @@ -73,12 +73,12 @@ void ClusterizerSpec::run(framework::ProcessingContext& ctx) LOG(debug) << "CPVClusterizer::run() : Received " << digitsTR.size() << " TR, calling clusterizer ..."; - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERS", 0, o2::framework::Lifetime::Timeframe}, mOutputClusters); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRIGRECS", 0, o2::framework::Lifetime::Timeframe}, mOutputClusterTrigRecs); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERS", 0}, mOutputClusters); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRIGRECS", 0}, mOutputClusterTrigRecs); if (mPropagateMC) { - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRUEMC", 0, o2::framework::Lifetime::Timeframe}, mOutputTruthCont); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRUEMC", 0}, mOutputTruthCont); } - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CALIBDIGITS", 0, o2::framework::Lifetime::Timeframe}, mCalibDigits); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CALIBDIGITS", 0}, mCalibDigits); LOG(info) << "Finished, wrote " << mOutputClusters.size() << " clusters, " << mOutputClusterTrigRecs.size() << "TR and " << mOutputTruthCont.getIndexedSize() << " Labels"; } o2::framework::DataProcessorSpec o2::cpv::reco_workflow::getClusterizerSpec(bool propagateMC) diff --git a/Detectors/CPV/workflow/src/DigitReaderSpec.cxx b/Detectors/CPV/workflow/src/DigitReaderSpec.cxx new file mode 100644 index 0000000000000..20fe497eb5d0c --- /dev/null +++ b/Detectors/CPV/workflow/src/DigitReaderSpec.cxx @@ -0,0 +1,100 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file DigitReaderSpec.cxx + +#include +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "CPVWorkflow/DigitReaderSpec.h" +#include "CommonUtils/NameConf.h" + +using namespace o2::framework; +using namespace o2::cpv; + +namespace o2 +{ +namespace cpv +{ + +DigitReader::DigitReader(bool useMC) +{ + mUseMC = useMC; +} + +void DigitReader::init(InitContext& ic) +{ + mInputFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), + ic.options().get("cpv-digits-infile")); + connectTree(mInputFileName); +} + +void DigitReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "Pushing " << mDigits.size() << " Digits in " << mTRs.size() << " TriggerRecords at entry " << ent; + pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0}, mDigits); + pc.outputs().snapshot(Output{mOrigin, "DIGITTRIGREC", 0}, mTRs); + if (mUseMC) { + pc.outputs().snapshot(Output{mOrigin, "DIGITSMCTR", 0}, mMCTruth); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void DigitReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mDigitTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mTRBranchName.c_str())); + + mTree->SetBranchAddress(mTRBranchName.c_str(), &mTRsInp); + mTree->SetBranchAddress(mDigitBranchName.c_str(), &mDigitsInp); + if (mUseMC) { + if (mTree->GetBranch(mDigitMCTruthBranchName.c_str())) { + mTree->SetBranchAddress(mDigitMCTruthBranchName.c_str(), &mMCTruthInp); + } else { + LOG(warning) << "MC-truth is missing, message will be empty"; + } + } + LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getCPVDigitReaderSpec(bool useMC) +{ + std::vector outputSpec; + outputSpec.emplace_back("CPV", "DIGITS", 0, Lifetime::Timeframe); + outputSpec.emplace_back("CPV", "DIGITTRIGREC", 0, Lifetime::Timeframe); + if (useMC) { + outputSpec.emplace_back("CPV", "DIGITSMCTR", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "cpv-digit-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask(useMC)}, + Options{ + {"cpv-digits-infile", VariantType::String, "cpvdigits.root", {"Name of the input Digit file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}}}; +} + +} // namespace cpv +} // namespace o2 diff --git a/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx b/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx index 25173c7a64555..518a646e23cb9 100644 --- a/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx @@ -25,12 +25,13 @@ namespace o2 namespace cpv { -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); mCTFCoder.setSupportBCShifts(true); + mCTFCoder.setDictBinding("ctfdict_CPV"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -51,8 +52,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc); - auto buff = pc.inputs().get>("ctf"); + mCTFCoder.updateTimeDependentParams(pc, true); + auto buff = pc.inputs().get>("ctf_CPV"); auto& triggers = pc.outputs().make>(OutputRef{"triggers"}); auto& clusters = pc.outputs().make>(OutputRef{"clusters"}); @@ -73,7 +74,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe}, @@ -81,16 +82,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) OutputSpec{{"ctfrep"}, "CPV", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "CPV", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionary")); + inputs.emplace_back("ctf_CPV", "CPV", "CTFDATA", sspec, Lifetime::Timeframe); + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_CPV", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "cpv-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } } // namespace cpv diff --git a/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx b/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx index 8901028e7ac66..54fb1354ad60c 100644 --- a/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx @@ -26,7 +26,7 @@ namespace o2 namespace cpv { -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -48,13 +48,13 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - mCTFCoder.updateTimeDependentParams(pc); + mCTFCoder.updateTimeDependentParams(pc, true); auto triggers = pc.inputs().get>("triggers"); auto clusters = pc.inputs().get>("clusters"); if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{"CPV", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"CPV", "CTFDATA", 0}); auto iosize = mCTFCoder.encode(buffer, triggers, clusters); pc.outputs().snapshot({"ctfrep", 0}, iosize); if (mSelIR) { @@ -70,12 +70,14 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe); inputs.emplace_back("clusters", "CPV", "CLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionary")); + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -84,11 +86,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"CPV", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "CPV", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } } // namespace cpv diff --git a/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx b/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx index 5dcac96160610..766902a2fdc95 100644 --- a/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx +++ b/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx @@ -136,11 +136,11 @@ void RawToDigitConverterSpec::run(framework::ProcessingContext& ctx) contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); } mOutputDigits.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mOutputDigits); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0}, mOutputDigits); mOutputTriggerRecords.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0}, mOutputTriggerRecords); mOutputHWErrors.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe}, mOutputHWErrors); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0}, mOutputHWErrors); return; // empty TF, nothing to process } } @@ -272,11 +272,11 @@ void RawToDigitConverterSpec::run(framework::ProcessingContext& ctx) if (skipTF) { // Send no digits mOutputDigits.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mOutputDigits); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0}, mOutputDigits); mOutputTriggerRecords.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0}, mOutputTriggerRecords); // Send errors - ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe}, mOutputHWErrors); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0}, mOutputHWErrors); return; } } @@ -305,15 +305,15 @@ void RawToDigitConverterSpec::run(framework::ProcessingContext& ctx) digitBuffer.clear(); LOG(info) << "[CPVRawToDigitConverter - run] Sending " << mOutputDigits.size() << " digits in " << mOutputTriggerRecords.size() << "trigger records."; - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mOutputDigits); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe}, mOutputHWErrors); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0}, mOutputDigits); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0}, mOutputTriggerRecords); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0}, mOutputHWErrors); } //_____________________________________________________________________________ o2::framework::DataProcessorSpec o2::cpv::reco_workflow::getRawToDigitConverterSpec(bool askDISTSTF, bool isPedestal, bool useBadChannelMap, bool useGainCalibration) { std::vector inputs; - inputs.emplace_back("RAWDATA", o2::framework::ConcreteDataTypeMatcher{"CPV", "RAWDATA"}, o2::framework::Lifetime::Optional); + inputs.emplace_back("RAWDATA", o2::framework::ConcreteDataTypeMatcher{"CPV", "RAWDATA"}, o2::framework::Lifetime::Timeframe); // receive at least 1 guaranteed input (which will allow to acknowledge the TF) if (askDISTSTF) { inputs.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe); diff --git a/Detectors/CPV/workflow/src/RecoWorkflow.cxx b/Detectors/CPV/workflow/src/RecoWorkflow.cxx index da4ffee8aa6e8..66b5f3dbe588a 100644 --- a/Detectors/CPV/workflow/src/RecoWorkflow.cxx +++ b/Detectors/CPV/workflow/src/RecoWorkflow.cxx @@ -24,6 +24,7 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "DataFormatsCPV/TriggerRecord.h" #include "CPVWorkflow/RecoWorkflow.h" +#include "CPVWorkflow/DigitReaderSpec.h" #include "CPVWorkflow/ClusterizerSpec.h" #include "CPVWorkflow/ReaderSpec.h" #include "CPVWorkflow/WriterSpec.h" @@ -107,7 +108,8 @@ o2::framework::WorkflowSpec getWorkflow(bool disableRootInp, // Digits to .... if (inputType == InputType::Digits) { if (!disableRootInp) { - specs.emplace_back(o2::cpv::getDigitsReaderSpec(propagateMC)); + specs.emplace_back(o2::cpv::getCPVDigitReaderSpec(propagateMC)); + // specs.emplace_back(o2::cpv::getDigitsReaderSpec(propagateMC)); } if (isEnabled(OutputType::Clusters)) { diff --git a/Detectors/CPV/workflow/src/cluster-reader-workflow.cxx b/Detectors/CPV/workflow/src/cluster-reader-workflow.cxx new file mode 100644 index 0000000000000..33f389f15f9da --- /dev/null +++ b/Detectors/CPV/workflow/src/cluster-reader-workflow.cxx @@ -0,0 +1,56 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/ConfigParamSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{ + "with-mc", + o2::framework::VariantType::Bool, + false, + {"propagate MC labels"}}); + workflowOptions.push_back( + ConfigParamSpec{ + "configKeyValues", + VariantType::String, + "", + {"Semicolon separated key=value strings"}}); + o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); +} + +#include "Framework/runDataProcessing.h" +#include "CPVWorkflow/ClusterReaderSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cc) +{ + WorkflowSpec specs; + o2::conf::ConfigurableParam::updateFromString(cc.options().get("configKeyValues")); + auto withMC = cc.options().get("with-mc"); + + specs.emplace_back(o2::cpv::getCPVClusterReaderSpec(withMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(cc, specs); + + return specs; +} diff --git a/Detectors/CPV/workflow/src/digit-reader-workflow.cxx b/Detectors/CPV/workflow/src/digit-reader-workflow.cxx new file mode 100644 index 0000000000000..dc068573b1218 --- /dev/null +++ b/Detectors/CPV/workflow/src/digit-reader-workflow.cxx @@ -0,0 +1,56 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/ConfigParamSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{ + "with-mc", + o2::framework::VariantType::Bool, + false, + {"propagate MC labels"}}); + workflowOptions.push_back( + ConfigParamSpec{ + "configKeyValues", + VariantType::String, + "", + {"Semicolon separated key=value strings"}}); + o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); +} + +#include "Framework/runDataProcessing.h" +#include "CPVWorkflow/DigitReaderSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cc) +{ + WorkflowSpec specs; + o2::conf::ConfigurableParam::updateFromString(cc.options().get("configKeyValues")); + auto withMC = cc.options().get("with-mc"); + + specs.emplace_back(o2::cpv::getCPVDigitReaderSpec(withMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(cc, specs); + + return specs; +} diff --git a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx index d7e79c4cea430..90fedb1d5eb83 100644 --- a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::cpv::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::cpv::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/CTF/README.md b/Detectors/CTF/README.md index 9d5c800be6d18..47ce765de289a 100644 --- a/Detectors/CTF/README.md +++ b/Detectors/CTF/README.md @@ -51,6 +51,22 @@ o2-ctf-reader-workflow --ctf-input input.lst --onlyDet ITS,TPC,TOF --its-entropy See below for the details of `--ctf-dict` option. +One can pause the writing if available disk space is low using a combination of following options: +```bash +--require-free-disk : pause writing operation if available disk space is below this margin in bytes (if > 0) or this fraction of total (if < 0) +``` + +```bash +--wait-for-free-disk : if paused due to the low disk space, recheck after this time (in s) +``` + +```bash +--max-wait-for-free-disk : produce fatal if paused due to the low disk space for more than this amount( in s). +``` + + + + ## CTF reader workflow `o2-ctf-reader-workflow` should be the 1st workflow in the piped chain of CTF processing. @@ -79,11 +95,24 @@ comma-separated list of detectors to read, Overrides skipDet ``` comma-separated list of detectors to skip + +By default an exception will be thrown if detector is requested but missing in the CTF. To enable injection of the empty output in such case one should use option `--allow-missing-detectors`. + +``` +--ctf-data-subspec arg (=0) +``` +allows to alter the `subSpecification` used to send the CTFDATA from the reader to decoders. Non-0 value must be used in case the data extracted by the CTF-reader should be processed and stored in new CTFs (in order to avoid clash of CTFDATA messages of the reader and writer). + ``` --max-tf arg (=-1) ``` max CTFs to process (<= 0 : infinite) +``` +--max-tf-per-file arg (=-1) +``` +max TFs to process from every CTF file (<= 0 : infinite) + ``` --loop arg (=0) ``` @@ -120,6 +149,8 @@ There is a possibility to read remote root files directly, w/o caching them loca 2) provide proper regex to define remote files, e.g. for the example above: `--remote-regex "^root://.+/eos/aliceo2/.+"`. 3) pass an option `--copy-cmd no-copy`. +## Selective TF reading + ``` --select-ctf-ids ``` @@ -127,24 +158,25 @@ This is a `ctf-reader` device local option allowing selective reading of particu Note that the index corresponds not to the entry of the TF in the CTF tree but to the reader own counter incremented throught all input files (e.g. if the 10 CTF files with 20 TFs each are provided for the input and the selection of TFs `0,2,22,66` is provided, the reader will inject to the DPL the TFs at entries 0 and 2 from the 1st CTF file, entry 5 of the second file, entry 6 of the 3d and will finish the job. -For the ITS and MFT entropy decoding one can request either to decompose clusters to digits and send them instead of clusters (via `o2-ctf-reader-workflow` global options `--its-digits` and `--mft-digits` respectively) -or to apply the noise mask to decoded clusters (or decoded digits). If the masking (e.g. via option `--its-entropy-decoder " --mask-noise "`) is requested, user should provide to the entropy decoder the noise mask file (eventually will be loaded from CCDB) and cluster patterns decoding dictionary (if the clusters were encoded with patterns IDs). -For example, ``` -o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --its-entropy-decoder ' --mask-noise' | ... +--ir-frames-files --skip-skimmed-out-tf ``` -will decode ITS and MFT data, decompose on the fly ITS clusters to digits, mask the noisy pixels with the provided masks, recluster remaining ITS digits and send the new clusters out, together with unchanged MFT clusters. +This option (used for skimming) allow to push to DPL only those TFs which overlap with selected BC-ranges provided via input root file (for various formats see `o2::utils::IRFrameSelector::loadIRFrames` method). + ``` -o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --mft-digits --mft-entropy-decoder ' --mask-noise' | ... +--ir-frames-files ``` -will send decompose clusters to digits and send ben out after masking the noise for the MFT, while ITS clusters will be sent as decoded. - -By default an exception will be thrown if detector is requested but missing in the CTF. To enable injection of the empty output in such case one should use option `--allow-missing-detectors`. +This option allows to push to DPL only those TFs which overlap with the ` ` (separators can be any whitespace, comma or semicolon) records provided via text file (assuming that there are some entries for a given run, otherwise the option is ignored). +Multiple ranges per run and multiple runs can be mentioned in a single input file. The range limits can be indicated either as a UNIX timestamp in `ms` or as an orbit number (in the fill the run belongs to). +In case an option ``` ---ctf-data-subspec arg (=0) +--invert-irframe-selection ``` -allows to alter the `subSpecification` used to send the CTFDATA from the reader to decoders. Non-0 value must be used in case the data extracted by the CTF-reader should be processed and stored in new CTFs (in order to avoid clash of CTFDATA messages of the reader and writer). +is provided, the selections above are inverted: TFs matching some of the provided ranges will be discarded, while the rest will be pushed to the DPL + +At the end of the processing the `ctf-writer` will create a local file `ctf_read_ntf.txt` containing only the number of TFs pushed to the DPL. +In case no TF passed the selections above, this file will contain 0. ## Support for externally provided encoding dictionaries @@ -177,3 +209,21 @@ To apply TF rate limiting (make sure that no more than N TFs are in processing) too all workflows (e.g. via ARGS_ALL). The IPCID is the NUMA domain ID (usually 0 on non-EPN workflow). Additionally, one may throttle on the free SHM by providing an option to the reader `--timeframes-shm-limit `. + +Note that by default the reader reads into the memory the CTF data and prepares all output messages but injects them only once the rate-limiter allows that. +With the option `--limit-tf-before-reading` set also the preparation of the data to inject will be conditioned by the green light from the rate-limiter. + + +## Modifying ITS/MFT CTF output + +For the ITS and MFT entropy decoding one can request either to decompose clusters to digits and send them instead of clusters (via `o2-ctf-reader-workflow` global options `--its-digits` and `--mft-digits` respectively) +or to apply the noise mask to decoded clusters (or decoded digits). If the masking (e.g. via option `--its-entropy-decoder " --mask-noise "`) is requested, user should provide to the entropy decoder the noise mask file (eventually will be loaded from CCDB) and cluster patterns decoding dictionary (if the clusters were encoded with patterns IDs). +For example, +``` +o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --its-entropy-decoder ' --mask-noise' | ... +``` +will decode ITS and MFT data, decompose on the fly ITS clusters to digits, mask the noisy pixels with the provided masks, recluster remaining ITS digits and send the new clusters out, together with unchanged MFT clusters. +``` +o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --mft-digits --mft-entropy-decoder ' --mask-noise' | ... +``` +will send decompose clusters to digits and send ben out after masking the noise for the MFT, while ITS clusters will be sent as decoded. diff --git a/Detectors/CTF/test/test_ctf_io_cpv.cxx b/Detectors/CTF/test/test_ctf_io_cpv.cxx index 2648cd40374cb..34a383a6875a0 100644 --- a/Detectors/CTF/test/test_ctf_io_cpv.cxx +++ b/Detectors/CTF/test/test_ctf_io_cpv.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test CPVCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "CPVReconstruction/CTFCoder.h" #include "DataFormatsCPV/CTF.h" @@ -22,11 +28,15 @@ #include #include #include +#include #include using namespace o2::cpv; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector triggers; std::vector clusters; @@ -55,6 +65,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, triggers, clusters); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_ctp.cxx b/Detectors/CTF/test/test_ctf_io_ctp.cxx index 25c93f360baf1..be9cdd1667ba8 100644 --- a/Detectors/CTF/test/test_ctf_io_ctp.cxx +++ b/Detectors/CTF/test/test_ctf_io_ctp.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test CTPCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "CTPReconstruction/CTFCoder.h" #include "DataFormatsCTP/CTF.h" @@ -25,8 +31,11 @@ #include using namespace o2::ctp; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest, *boost::unit_test::enabled()) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector digits; TStopwatch sw; @@ -49,6 +58,7 @@ BOOST_AUTO_TEST_CASE(CTFTest, *boost::unit_test::enabled()) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, digits, lumi); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_emcal.cxx b/Detectors/CTF/test/test_ctf_io_emcal.cxx index fb91254f6847f..4c6f6f8193672 100644 --- a/Detectors/CTF/test/test_ctf_io_emcal.cxx +++ b/Detectors/CTF/test/test_ctf_io_emcal.cxx @@ -12,8 +12,15 @@ #define BOOST_TEST_MODULE Test EMCCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" +#include "DataFormatsEMCAL/Constants.h" #include "EMCALReconstruction/CTFCoder.h" #include "DataFormatsEMCAL/CTF.h" #include "Framework/Logger.h" @@ -24,8 +31,11 @@ #include using namespace o2::emcal; +namespace boost_data = boost::unit_test::data; -BOOST_AUTO_TEST_CASE(CTFTest) +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; + +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector triggers; std::vector cells; @@ -41,8 +51,33 @@ BOOST_AUTO_TEST_CASE(CTFTest) while (tower < 17665) { float timeCell = gRandom->Rndm() * 1500 - 600.; float en = gRandom->Rndm() * 250.; - int stat = gRandom->Integer(5); - cells.emplace_back(tower, en, timeCell, (ChannelType_t)stat); + // In case of cell type 3 cases must be distinguished (FEE, LEDMON, TRU) + // In case the cell is a FEE cell the cell type is correlated with the energy + int readoutsource = gRandom->Integer(3); // + ChannelType_t chantype = ChannelType_t::HIGH_GAIN; + switch (readoutsource) { + case 0: { + // Cell is a FEE cell, determine cell type according to HGLG transition + const auto ENHGLG = o2::emcal::constants::EMCAL_HGLGTRANSITION * o2::emcal::constants::EMCAL_ADCENERGY; + if (en >= ENHGLG) { + chantype = ChannelType_t::LOW_GAIN; + } else { + chantype = ChannelType_t::HIGH_GAIN; + } + break; + } + case 1: + chantype = ChannelType_t::LEDMON; + break; + case 2: + chantype = ChannelType_t::TRU; + break; + + default: + std::cerr << "Unknown type" << std::endl; + break; + } + cells.emplace_back(tower, en, timeCell, chantype); tower += 1 + gRandom->Integer(100); } uint32_t trigBits = gRandom->Integer(0xFFFFFFFF); // will be converted internally to uint16_t by the coder @@ -53,6 +88,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, triggers, cells); // compress } sw.Stop(); @@ -126,9 +162,9 @@ BOOST_AUTO_TEST_CASE(CTFTest) for (size_t i = 0; i < cells.size(); i++) { const auto& cor = cells[i]; const auto& cdc = cellsD[i]; - BOOST_CHECK_EQUAL(cor.getPackedTowerID(), cdc.getPackedTowerID()); - BOOST_CHECK_EQUAL(cor.getPackedTime(), cdc.getPackedTime()); - BOOST_CHECK_EQUAL(cor.getPackedEnergy(), cdc.getPackedEnergy()); - BOOST_CHECK_EQUAL(cor.getPackedCellStatus(), cdc.getPackedCellStatus()); + BOOST_CHECK_EQUAL(cor.getTowerIDEncoded(), cdc.getTowerIDEncoded()); + BOOST_CHECK_EQUAL(cor.getTimeStampEncoded(), cdc.getTimeStampEncoded()); + BOOST_CHECK_EQUAL(cor.getEnergyEncoded(), cdc.getEnergyEncoded()); + BOOST_CHECK_EQUAL(cor.getCellTypeEncoded(), cdc.getCellTypeEncoded()); } } diff --git a/Detectors/CTF/test/test_ctf_io_fdd.cxx b/Detectors/CTF/test/test_ctf_io_fdd.cxx index a1da64af04968..3dd87c1618598 100644 --- a/Detectors/CTF/test/test_ctf_io_fdd.cxx +++ b/Detectors/CTF/test/test_ctf_io_fdd.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test FDDCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "FDDReconstruction/CTFCoder.h" #include "FDDBase/Constants.h" @@ -23,8 +29,11 @@ #include using namespace o2::fdd; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector digits; std::vector channels; @@ -81,6 +90,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, digits, channels); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_ft0.cxx b/Detectors/CTF/test/test_ctf_io_ft0.cxx index 4b7bee2460909..6e2746444e662 100644 --- a/Detectors/CTF/test/test_ctf_io_ft0.cxx +++ b/Detectors/CTF/test/test_ctf_io_ft0.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test FT0CTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "FT0Reconstruction/CTFCoder.h" #include "FT0Base/FT0DigParam.h" @@ -23,8 +29,11 @@ #include using namespace o2::ft0; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector digits; std::vector channels; @@ -87,6 +96,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, digits, channels); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_fv0.cxx b/Detectors/CTF/test/test_ctf_io_fv0.cxx index f9bf4a8a878e5..2e558b91a8e1c 100644 --- a/Detectors/CTF/test/test_ctf_io_fv0.cxx +++ b/Detectors/CTF/test/test_ctf_io_fv0.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test FV0CTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "FV0Reconstruction/CTFCoder.h" #include "FV0Base/Constants.h" @@ -23,8 +29,11 @@ #include using namespace o2::fv0; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector digits; std::vector channels; @@ -59,6 +68,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, digits, channels); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_hmpid.cxx b/Detectors/CTF/test/test_ctf_io_hmpid.cxx index 88df30c8419c0..fd12ee7eb1e36 100644 --- a/Detectors/CTF/test/test_ctf_io_hmpid.cxx +++ b/Detectors/CTF/test/test_ctf_io_hmpid.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test HMPIDCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "HMPIDReconstruction/CTFCoder.h" #include "DataFormatsHMP/CTF.h" @@ -24,8 +30,11 @@ #include using namespace o2::hmpid; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector triggers; std::vector digits; @@ -53,6 +62,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, triggers, digits); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_itsmft.cxx b/Detectors/CTF/test/test_ctf_io_itsmft.cxx index 806ad8536d69d..7f2ff8ce9f340 100644 --- a/Detectors/CTF/test/test_ctf_io_itsmft.cxx +++ b/Detectors/CTF/test/test_ctf_io_itsmft.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test ITSMFTCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/CTF.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -26,8 +32,11 @@ #include using namespace o2::itsmft; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CompressedClustersTest) +BOOST_DATA_TEST_CASE(CompressedClustersTest, boost_data::make(ANSVersions), ansVersion) { std::vector rofRecVec; @@ -72,7 +81,8 @@ BOOST_AUTO_TEST_CASE(CompressedClustersTest) sw.Start(); std::vector vec; { - CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder, o2::detectors::DetID::ITS); + CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder, false); + coder.setANSVersion(ansVersion); coder.encode(vec, rofRecVec, cclusVec, pattVec, pattIdConverter, 0); // compress } sw.Stop(); @@ -110,7 +120,7 @@ BOOST_AUTO_TEST_CASE(CompressedClustersTest) sw.Start(); const auto ctfImage = o2::itsmft::CTF::getImage(vec.data()); { - CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder, o2::detectors::DetID::ITS); + CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder, false); coder.decode(ctfImage, rofRecVecD, cclusVecD, pattVecD, nullptr, clPattLookup); // decompress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_mch.cxx b/Detectors/CTF/test/test_ctf_io_mch.cxx index 9a6553ba70d81..a12ca58a574da 100644 --- a/Detectors/CTF/test/test_ctf_io_mch.cxx +++ b/Detectors/CTF/test/test_ctf_io_mch.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test MCHCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "MCHCTF/CTFCoder.h" #include "DataFormatsMCH/CTF.h" @@ -26,8 +32,11 @@ #include using namespace o2::mch; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest, *boost::unit_test::enabled()) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector rofs; std::vector digs; @@ -59,6 +68,7 @@ BOOST_AUTO_TEST_CASE(CTFTest, *boost::unit_test::enabled()) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, rofs, digs); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_mid.cxx b/Detectors/CTF/test/test_ctf_io_mid.cxx index 2e8ee98b87e7a..2a7122f96e1da 100644 --- a/Detectors/CTF/test/test_ctf_io_mid.cxx +++ b/Detectors/CTF/test/test_ctf_io_mid.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test MIDCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "CommonUtils/IRFrameSelector.h" #include "MIDCTF/CTFCoder.h" @@ -25,8 +31,11 @@ #include using namespace o2::mid; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::array, NEvTypes> colData{}; std::array, NEvTypes> rofData{}; @@ -73,6 +82,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, tfData); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_phos.cxx b/Detectors/CTF/test/test_ctf_io_phos.cxx index f564dfc2b8fc8..6850642ba129e 100644 --- a/Detectors/CTF/test/test_ctf_io_phos.cxx +++ b/Detectors/CTF/test/test_ctf_io_phos.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test PHSCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "PHOSReconstruction/CTFCoder.h" #include "DataFormatsPHOS/CTF.h" @@ -24,8 +30,11 @@ #include using namespace o2::phos; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector triggers; std::vector cells; @@ -51,6 +60,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, triggers, cells); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_tof.cxx b/Detectors/CTF/test/test_ctf_io_tof.cxx index b770dbb2f78d5..276e773f77c69 100644 --- a/Detectors/CTF/test/test_ctf_io_tof.cxx +++ b/Detectors/CTF/test/test_ctf_io_tof.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test TOFCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "DataFormatsTOF/CTF.h" #include "TOFBase/Geo.h" #include "TOFBase/Digit.h" @@ -25,8 +31,11 @@ #include using namespace o2::tof; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CompressedClustersTest) +BOOST_DATA_TEST_CASE(CompressedClustersTest, boost_data::make(ANSVersions), ansVersion) { std::vector digits; @@ -86,6 +95,7 @@ BOOST_AUTO_TEST_CASE(CompressedClustersTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, rows, digits, pattVec); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_tpc.cxx b/Detectors/CTF/test/test_ctf_io_tpc.cxx index 10b5316524baa..cb087e6f2a38f 100644 --- a/Detectors/CTF/test/test_ctf_io_tpc.cxx +++ b/Detectors/CTF/test/test_ctf_io_tpc.cxx @@ -12,8 +12,15 @@ #define BOOST_TEST_MODULE Test TPCCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "DataFormatsTPC/CompressedClusters.h" +#include "DataFormatsTPC/ZeroSuppression.h" #include "DataFormatsTPC/CTF.h" #include "CommonUtils/NameConf.h" #include "TPCReconstruction/CTFCoder.h" @@ -24,15 +31,28 @@ #include using namespace o2::tpc; +namespace boost_data = boost::unit_test::data; -BOOST_AUTO_TEST_CASE(CTFTest) +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; +inline std::vector CombineColumns(true, false); + +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions) ^ boost_data::make(CombineColumns), ansVersion, combineColumns) { + std::vector triggers, triggersR; CompressedClusters c; c.nAttachedClusters = 99; c.nUnattachedClusters = 88; c.nAttachedClustersReduced = 77; c.nTracks = 66; + triggers.emplace_back(); + triggers.back().orbit = 1234; + triggers.back().triggerWord.triggerEntries[0] = (10 & 0xFFF) | ((o2::tpc::TriggerWordDLBZS::TriggerType::PhT & 0x7) << 12) | 0x8000; + triggers.back().triggerWord.triggerEntries[1] = (30 & 0xFFF) | ((o2::tpc::TriggerWordDLBZS::TriggerType::PP & 0x7) << 12) | 0x8000; + triggers.emplace_back(); + triggers.back().orbit = 1236; + triggers.back().triggerWord.triggerEntries[0] = (40 & 0xFFF) | ((o2::tpc::TriggerWordDLBZS::TriggerType::Cal & 0x7) << 12) | 0x8000; + std::vector bVec; CompressedClustersFlat* ccFlat = nullptr; size_t sizeCFlatBody = CTFCoder::alignSize(ccFlat); @@ -43,7 +63,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); coder.setCompClusAddresses(c, buff); - coder.setCombineColumns(true); + coder.setCombineColumns(combineColumns); } ccFlat->set(sz, c); @@ -86,9 +106,43 @@ BOOST_AUTO_TEST_CASE(CTFTest) sw.Start(); std::vector vecIO; { - CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder); - coder.setCombineColumns(true); - coder.encode(vecIO, c, c); // compress + CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setCombineColumns(combineColumns); + coder.setANSVersion(ansVersion); + // prepare trigger info + o2::tpc::detail::TriggerInfo trigComp; + for (const auto& trig : triggers) { + for (int it = 0; it < o2::tpc::TriggerWordDLBZS::MaxTriggerEntries; it++) { + if (trig.triggerWord.isValid(it)) { + trigComp.deltaOrbit.push_back(trig.orbit); + trigComp.deltaBC.push_back(trig.triggerWord.getTriggerBC(it)); + trigComp.triggerType.push_back(trig.triggerWord.getTriggerType(it)); + } else { + break; + } + } + } + // transform trigger info to differential form + uint32_t prevOrbit = -1; + uint16_t prevBC = -1; + if (trigComp.triggerType.size()) { + prevOrbit = trigComp.firstOrbit = trigComp.deltaOrbit[0]; + prevBC = trigComp.deltaBC[0]; + trigComp.deltaOrbit[0] = 0; + for (size_t it = 1; it < trigComp.triggerType.size(); it++) { + if (trigComp.deltaOrbit[it] == prevOrbit) { + auto bc = trigComp.deltaBC[it]; + trigComp.deltaBC[it] -= prevBC; + prevBC = bc; + trigComp.deltaOrbit[it] = 0; + } else { + auto orb = trigComp.deltaOrbit[it]; + trigComp.deltaOrbit[it] -= prevOrbit; + prevOrbit = orb; + } + } + } + coder.encode(vecIO, c, c, trigComp); // compress } sw.Stop(); LOG(info) << "Compressed in " << sw.CpuTime() << " s"; @@ -124,7 +178,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder); coder.setCombineColumns(true); - coder.decode(ctfImage, vecIn); // decompress + coder.decode(ctfImage, vecIn, triggersR); // decompress } sw.Stop(); LOG(info) << "Decompressed in " << sw.CpuTime() << " s"; @@ -142,4 +196,6 @@ BOOST_AUTO_TEST_CASE(CTFTest) BOOST_CHECK(countOrig->solenoidBz == countDeco->solenoidBz); BOOST_CHECK(countOrig->maxTimeBin == countDeco->maxTimeBin); BOOST_CHECK(memcmp(vecIn.data() + sizeof(o2::tpc::CompressedClustersCounters), bVec.data() + sizeof(o2::tpc::CompressedClustersCounters), bVec.size() - sizeof(o2::tpc::CompressedClustersCounters)) == 0); + BOOST_CHECK(triggers.size() == triggersR.size()); + BOOST_CHECK(memcmp(triggers.data(), triggersR.data(), triggers.size() * sizeof(o2::tpc::TriggerInfoDLBZS)) == 0); } diff --git a/Detectors/CTF/test/test_ctf_io_trd.cxx b/Detectors/CTF/test/test_ctf_io_trd.cxx index 158ccd187e6b8..66203074ce817 100644 --- a/Detectors/CTF/test/test_ctf_io_trd.cxx +++ b/Detectors/CTF/test/test_ctf_io_trd.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test TRDCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "TRDReconstruction/CTFCoder.h" #include "DataFormatsTRD/CTF.h" @@ -24,8 +30,11 @@ #include using namespace o2::trd; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector triggers; std::vector tracklets; @@ -70,6 +79,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, triggers, tracklets, digits); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_zdc.cxx b/Detectors/CTF/test/test_ctf_io_zdc.cxx index 6222951c080e6..3ae6f5b991ad1 100644 --- a/Detectors/CTF/test/test_ctf_io_zdc.cxx +++ b/Detectors/CTF/test/test_ctf_io_zdc.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test ZDCCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "ZDCReconstruction/CTFCoder.h" #include "DataFormatsZDC/CTF.h" @@ -24,8 +30,11 @@ #include using namespace o2::zdc; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector bcdata; std::vector chandata; @@ -72,7 +81,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) pedsdata[i].ir = irPed; for (int ic = 0; ic < NChannels; ic++) { pedsdata[i].data[ic] = gRandom->Integer(0xffff); - pedsdata[i].scaler[ic] = (i > 0 ? pedsdata[i].scaler[ic - 1] : 0) + gRandom->Integer(20); + pedsdata[i].scaler[ic] = (ic > 0 ? pedsdata[i].scaler[ic - 1] : 0) + gRandom->Integer(20); } irPed.orbit++; } @@ -81,6 +90,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, bcdata, chandata, pedsdata); // compress } sw.Stop(); diff --git a/Detectors/CTF/utils/CMakeLists.txt b/Detectors/CTF/utils/CMakeLists.txt index 025f7586a0e45..f1b55e027f64a 100644 --- a/Detectors/CTF/utils/CMakeLists.txt +++ b/Detectors/CTF/utils/CMakeLists.txt @@ -29,3 +29,7 @@ o2_add_test_root_macro(dumpCTF.C o2_add_test_root_macro(CTFdict2CCDBfiles.C PUBLIC_LINK_LIBRARIES O2::CTFWorkflow fmt::fmt LABELS ctf COMPILE_ONLY) + +o2_add_test_root_macro(convCTFDict.C + PUBLIC_LINK_LIBRARIES O2::CTFWorkflow fmt::fmt + LABELS ctf COMPILE_ONLY) diff --git a/Detectors/CTF/utils/convCTFDict.C b/Detectors/CTF/utils/convCTFDict.C new file mode 100644 index 0000000000000..1b42cda3e2a1b --- /dev/null +++ b/Detectors/CTF/utils/convCTFDict.C @@ -0,0 +1,121 @@ +// Copyright 2019-2023 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "DetectorsCommonDataFormats/CTFHeader.h" +#include "DetectorsCommonDataFormats/CTFDictHeader.h" +#include "DetectorsCommonDataFormats/EncodedBlocks.h" +#include "DataFormatsCTP/CTF.h" +#include "DataFormatsTRD/CTF.h" +#include "DataFormatsTOF/CTF.h" +#include "DataFormatsTPC/CTF.h" +#include "DataFormatsFT0/CTF.h" +#include "DataFormatsFV0/CTF.h" +#include "DataFormatsFDD/CTF.h" +#include "DataFormatsEMCAL/CTF.h" +#include "DataFormatsITSMFT/CTF.h" +#include "DataFormatsPHOS/CTF.h" +#include "DataFormatsZDC/CTF.h" +#include "DataFormatsMID/CTF.h" +#include "DataFormatsMCH/CTF.h" +#include "DataFormatsHMP/CTF.h" +#include "DataFormatsCPV/CTF.h" + +#include +#include + +#endif + +template +size_t appendToTree(TTree& tree, const std::string brname, T& ptr) +{ + size_t s = 0; + auto* br = tree.GetBranch(brname.c_str()); + auto* pptr = &ptr; + if (br) { + br->SetAddress(&pptr); + } else { + br = tree.Branch(brname.c_str(), &pptr); + } + int res = br->Fill(); + if (res < 0) { + throw std::runtime_error(fmt::format("Failed to fill CTF branch {}", brname)); + } + s += res; + br->ResetAddress(); + return s; +} + +template +void conv(const std::vector* buff) +{ + const auto& ctf = C::getImage(buff->data()); + const auto& ctfhead = ctf.getHeader(); + const auto& dictHead = (const o2::ctf::CTFDictHeader&)ctfhead; + o2::ctf::CTFHeader header{}; + TFile fl(Form("ctf_dictionaryTree_%s_%u_0.root", dictHead.det.getName(), dictHead.dictTimeStamp), "recreate"); + TTree* tree = new TTree("ccdb_object", "O2 CTF dictionary"); + header.detectors.set(dictHead.det); + ctf.appendToTree(*tree, dictHead.det.getName()); + appendToTree(*tree, "CTFHeader", header); + tree->SetEntries(1); + tree->Write(tree->GetName(), TObject::kSingleKey); + delete tree; + fl.Close(); + printf("Stored dictionary to %s\n", fl.GetName()); +} + +void convCTFDict(const char* det, long ts = 0, const char* ccdburl = "http://alice-ccdb.cern.ch") +{ + auto& cm = o2::ccdb::BasicCCDBManager::instance(); + if (ts > 0) { + cm.setTimestamp(ts); + } + std::string pth = std::string(Form("%s/Calib/CTFDictionary", det)); + const auto* buff = cm.get>(pth); + if (!buff) { + printf("Failed to fetch from %s for %ld\n", pth.c_str(), cm.getTimestamp()); + } + std::string dets{det}; + if (dets == "ITS" || dets == "MFT") { + conv(buff); + } else if (dets == "TPC") { + conv(buff); + } else if (dets == "TRD") { + conv(buff); + } else if (dets == "TOF") { + conv(buff); + } else if (dets == "FT0") { + conv(buff); + } else if (dets == "FV0") { + conv(buff); + } else if (dets == "FDD") { + conv(buff); + } else if (dets == "EMC") { + conv(buff); + } else if (dets == "PHS") { + conv(buff); + } else if (dets == "ZDC") { + conv(buff); + } else if (dets == "MID") { + conv(buff); + } else if (dets == "MCH") { + conv(buff); + } else if (dets == "HMP") { + conv(buff); + } else if (dets == "CTP") { + conv(buff); + } else if (dets == "CPV") { + conv(buff); + } +} diff --git a/Detectors/CTF/workflow/CMakeLists.txt b/Detectors/CTF/workflow/CMakeLists.txt index fd00cf98e348b..f7adeaed991c0 100644 --- a/Detectors/CTF/workflow/CMakeLists.txt +++ b/Detectors/CTF/workflow/CMakeLists.txt @@ -9,6 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +#add_compile_options(-O0 -g -fPIC) + o2_add_library(CTFWorkflow SOURCES src/CTFWriterSpec.cxx src/CTFReaderSpec.cxx @@ -54,5 +56,10 @@ o2_add_executable(writer-workflow o2_add_executable(reader-workflow SOURCES src/ctf-reader-workflow.cxx COMPONENT_NAME ctf + TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::CTFWorkflow) +if (OpenMP_CXX_FOUND) + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) + target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) +endif() diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h index 4c732c570d236..51f2fca2c8303 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h @@ -27,22 +27,32 @@ struct CTFReaderInp { std::string inpdata{}; o2::detectors::DetID::mask_t detMask = o2::detectors::DetID::FullMask; std::string copyCmd{}; + std::string copyDir{}; std::string tffileRegex{}; std::string remoteRegex{}; std::string metricChannel{}; std::string fileIRFrames{}; + std::string fileRunTimeSpans{}; + std::string dictOpt{}; std::vector ctfIDs{}; + bool reverseCTFIDs{false}; bool skipSkimmedOutTF = false; + bool invertIRFramesSelection = false; bool allowMissingDetectors = false; + bool checkTFLimitBeforeReading = false; bool sup0xccdb = false; int maxFileCache = 1; int64_t delay_us = 0; int maxLoops = 0; int maxTFs = -1; + int maxTFsPerFile = -1; unsigned int subspec = 0; unsigned int decSSpecEMC = 0; - int tfRateLimit = 0; + int tfRateLimit = -999; size_t minSHM = 0; + bool shuffle{false}; + bool doITSStaggering = false; + bool doMFTStaggering = false; }; /// create a processor spec diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h index 5eb6d65e26cec..12ad483d90881 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h @@ -15,16 +15,23 @@ #define O2_CTFWRITER_SPEC #include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" #include "DetectorsCommonDataFormats/DetID.h" namespace o2 { namespace ctf { +struct CTFWriterInp { + o2::detectors::DetID::mask_t detMask = o2::detectors::DetID::FullMask; + int verbosity = 0; + int reportInterval = 200; + std::string outType = ""; + bool doITSStaggering = false; + bool doMFTStaggering = false; +}; /// create a processor spec -framework::DataProcessorSpec getCTFWriterSpec(o2::detectors::DetID::mask_t dets, const std::string& outType, int verbosity, int reportInterval); +framework::DataProcessorSpec getCTFWriterSpec(const o2::ctf::CTFWriterInp& inp); } // namespace ctf } // namespace o2 diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index 88f7fd8d5fc2a..9fba8a220be55 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -11,9 +11,14 @@ /// @file CTFReaderSpec.cxx +#include #include +#include +#include + #include #include +#include #include "Framework/Logger.h" #include "Framework/ControlService.h" @@ -30,6 +35,7 @@ #include "CommonUtils/NameConf.h" #include "DetectorsCommonDataFormats/CTFHeader.h" #include "Headers/STFHeader.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/CTF.h" #include "DataFormatsTPC/CTF.h" #include "DataFormatsTRD/CTF.h" @@ -45,8 +51,10 @@ #include "DataFormatsZDC/CTF.h" #include "DataFormatsHMP/CTF.h" #include "DataFormatsCTP/CTF.h" +#include "DataFormatsParameters/AggregatedRunInfo.h" +#include "CCDB/BasicCCDBManager.h" +#include "CommonConstants/LHCConstants.h" #include "Algorithm/RangeTokenizer.h" -#include #include using namespace o2::framework; @@ -81,6 +89,8 @@ class CTFReaderSpec : public o2::framework::Task void run(o2::framework::ProcessingContext& pc) final; private: + void runTimeRangesToIRFrameSelector(const o2::framework::TimingInfo& timingInfo); + void loadRunTimeSpans(const std::string& flname); void openCTFFile(const std::string& flname); bool processTF(ProcessingContext& pc); void checkTreeEntries(); @@ -91,15 +101,22 @@ class CTFReaderSpec : public o2::framework::Task void tryToFixCTFHeader(CTFHeader& ctfHeader) const; CTFReaderInp mInput{}; o2::utils::IRFrameSelector mIRFrameSelector; // optional IR frames selector + std::map>> mRunTimeRanges; std::unique_ptr mFileFetcher; std::unique_ptr mCTFFile; std::unique_ptr mCTFTree; bool mRunning = false; bool mUseLocalTFCounter = false; + bool mIFRamesOut = false; + int mConvRunTimeRangesToOrbits = -1; // not defined yet int mCTFCounter = 0; + int mCTFCounterAcc = 0; int mNFailedFiles = 0; int mFilesRead = 0; - int mTFLength = 128; + int mTFLength = 32; + int mNWaits = 0; + int mRunNumberPrev = -1; + long mTotalWaitTime = 0; long mLastSendTime = 0L; long mCurrTreeEntry = 0L; long mImposeRunStartMS = 0L; @@ -127,8 +144,8 @@ void CTFReaderSpec::stopReader() return; } LOGP(info, "CTFReader stops processing, {} files read, {} files failed", mFilesRead - mNFailedFiles, mNFailedFiles); - LOGP(info, "CTF reading total timing: Cpu: {:.3f} Real: {:.3f} s for {} TFs in {} loops", - mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mFileFetcher->getNLoops()); + LOGP(info, "CTF reading total timing: Cpu: {:.3f} Real: {:.3f} s for {} TFs ({} accepted) in {} loops, spent {:.2} s in {} data waiting states", + mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mCTFCounterAcc, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); mRunning = false; mFileFetcher->stop(); mFileFetcher.reset(); @@ -143,10 +160,18 @@ void CTFReaderSpec::stopReader() void CTFReaderSpec::init(InitContext& ic) { mInput.ctfIDs = o2::RangeTokenizer::tokenize(ic.options().get("select-ctf-ids")); + if ((mInput.reverseCTFIDs = ic.options().get("reverse-select-ctf-ids"))) { + std::reverse(mInput.ctfIDs.begin(), mInput.ctfIDs.end()); + } mUseLocalTFCounter = ic.options().get("local-tf-counter"); mImposeRunStartMS = ic.options().get("impose-run-start-timstamp"); + mInput.checkTFLimitBeforeReading = ic.options().get("limit-tf-before-reading"); + mInput.maxTFs = ic.options().get("max-tf"); + mInput.maxTFs = mInput.maxTFs > 0 ? mInput.maxTFs : 0x7fffffff; + mInput.maxTFsPerFile = ic.options().get("max-tf-per-file"); + mInput.maxTFsPerFile = mInput.maxTFsPerFile > 0 ? mInput.maxTFsPerFile : 0x7fffffff; mRunning = true; - mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd); + mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd, mInput.copyDir); mFileFetcher->setMaxFilesInQueue(mInput.maxFileCache); mFileFetcher->setMaxLoops(mInput.maxLoops); mFileFetcher->setFailThreshold(ic.options().get("fetch-failure-threshold")); @@ -156,7 +181,156 @@ void CTFReaderSpec::init(InitContext& ic) const auto& hbfu = o2::raw::HBFUtils::Instance(); mTFLength = hbfu.nHBFPerTF; LOGP(info, "IRFrames will be selected from {}, assumed TF length: {} HBF", mInput.fileIRFrames, mTFLength); + mIFRamesOut = true; + } + if (!mInput.fileRunTimeSpans.empty()) { + loadRunTimeSpans(mInput.fileRunTimeSpans); + mIFRamesOut = true; + } +} + +///_______________________________________ +template <> +void CTFReaderSpec::processDetector(DetID det, const CTFHeader& ctfHeader, ProcessingContext& pc) const +{ + if (mInput.detMask[det]) { + std::string lbl = det.getName(); + int nLayers = 1; + if (det == DetID::ITS) { + nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else { + LOGP(fatal, "This specialization is define only for ITS and MFT detectors, {} provided", det.getName()); + } + for (int iLayer = 0; iLayer < nLayers; iLayer++) { + auto& bufVec = pc.outputs().make>({lbl, mInput.subspec * 100 + iLayer}, ctfHeader.detectors[det] ? sizeof(o2::itsmft::CTF) : 0); + if (ctfHeader.detectors[det]) { + auto brName = nLayers == 1 ? lbl : fmt::format("{}_{}", lbl, iLayer); + o2::itsmft::CTF::readFromTree(bufVec, *(mCTFTree.get()), brName, mCurrTreeEntry); + } else if (!mInput.allowMissingDetectors) { + throw std::runtime_error(fmt::format("Requested detector {} is missing in the CTF", lbl)); + } + } + } +} + +///_______________________________________ +template +void CTFReaderSpec::processDetector(DetID det, const CTFHeader& ctfHeader, ProcessingContext& pc) const +{ + if (mInput.detMask[det]) { + const auto lbl = det.getName(); + auto& bufVec = pc.outputs().make>({lbl, mInput.subspec}, ctfHeader.detectors[det] ? sizeof(C) : 0); + if (ctfHeader.detectors[det]) { + C::readFromTree(bufVec, *(mCTFTree.get()), lbl, mCurrTreeEntry); + } else if (!mInput.allowMissingDetectors) { + throw std::runtime_error(fmt::format("Requested detector {} is missing in the CTF", lbl)); + } + // setMessageHeader(pc, ctfHeader, lbl); + } +} + +void CTFReaderSpec::runTimeRangesToIRFrameSelector(const o2::framework::TimingInfo& timingInfo) +{ + // convert entries in the runTimeRanges to IRFrameSelector, if needed, convert time to orbit + mIRFrameSelector.clear(); + auto ent = mRunTimeRanges.find(timingInfo.runNumber); + if (ent == mRunTimeRanges.end()) { + LOGP(info, "RunTimeRanges selection was provided but run {} has no entries, all TFs will be processed", timingInfo.runNumber); + return; + } + o2::parameters::AggregatedRunInfo rinfo; + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + rinfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb, timingInfo.runNumber); + if (rinfo.runNumber != timingInfo.runNumber || rinfo.orbitsPerTF < 1) { + LOGP(fatal, "failed to extract AggregatedRunInfo for run {}", timingInfo.runNumber); + } + mTFLength = rinfo.orbitsPerTF; + std::vector frames; + for (const auto& rng : ent->second) { + long orbMin = 0, orbMax = 0; + if (mConvRunTimeRangesToOrbits > 0) { + orbMin = rinfo.orbitSOR + (rng.first - rinfo.sor) / (o2::constants::lhc::LHCOrbitMUS * 0.001); + orbMax = rinfo.orbitSOR + (rng.second - rinfo.sor) / (o2::constants::lhc::LHCOrbitMUS * 0.001); + } else { + orbMin = rng.first; + orbMax = rng.second; + } + if (orbMin < 0) { + orbMin = 0; + } + if (orbMax < 0) { + orbMax = 0; + } + if (timingInfo.runNumber > 523897) { + orbMin = (orbMin / rinfo.orbitsPerTF) * rinfo.orbitsPerTF; + orbMax = (orbMax / rinfo.orbitsPerTF + 1) * rinfo.orbitsPerTF - 1; + } + LOGP(info, "TFs overlapping with orbits {}:{} will be {}", orbMin, orbMax, mInput.invertIRFramesSelection ? "rejected" : "selected"); + frames.emplace_back(InteractionRecord{0, uint32_t(orbMin)}, InteractionRecord{o2::constants::lhc::LHCMaxBunches, uint32_t(orbMax)}); + } + mIRFrameSelector.setOwnList(frames, true); +} + +void CTFReaderSpec::loadRunTimeSpans(const std::string& flname) +{ + std::ifstream inputFile(flname); + if (!inputFile) { + LOGP(fatal, "Failed to open selected run/timespans file {}", flname); + } + std::string line; + size_t cntl = 0, cntr = 0; + while (std::getline(inputFile, line)) { + cntl++; + for (char& ch : line) { // Replace semicolons and tabs with spaces for uniform processing + if (ch == ';' || ch == '\t' || ch == ',') { + ch = ' '; + } + } + o2::utils::Str::trim(line); + if (line.size() < 1 || line[0] == '#') { + continue; + } + auto tokens = o2::utils::Str::tokenize(line, ' '); + auto logError = [&cntl, &line]() { LOGP(error, "Expected format for selection is tripplet , failed on line#{}: {}", cntl, line); }; + if (tokens.size() >= 3) { + int run = 0; + long rmin, rmax; + try { + run = std::stoi(tokens[0]); + rmin = std::stol(tokens[1]); + rmax = std::stol(tokens[2]); + } catch (...) { + logError(); + continue; + } + + constexpr long ISTimeStamp = 1514761200000L; + int convmn = rmin > ISTimeStamp ? 1 : 0, convmx = rmax > ISTimeStamp ? 1 : 0; // values above ISTimeStamp are timestamps (need to be converted to orbits) + if (rmin > rmax) { + LOGP(fatal, "Provided range limits are not in increasing order, entry is {}", line); + } + if (mConvRunTimeRangesToOrbits == -1) { + if (convmn != convmx) { + LOGP(fatal, "Provided range limits should be both consistent either with orbit number or with unix timestamp in ms, entry is {}", line); + } + mConvRunTimeRangesToOrbits = convmn; // need to convert to orbit if time + LOGP(info, "Interpret selected time-spans input as {}", mConvRunTimeRangesToOrbits == 1 ? "timstamps(ms)" : "orbits"); + } else { + if (mConvRunTimeRangesToOrbits != convmn || mConvRunTimeRangesToOrbits != convmx) { + LOGP(fatal, "Provided range limits should are not consistent with previously determined {} input, entry is {}", mConvRunTimeRangesToOrbits == 1 ? "timestamps" : "orbits", line); + } + } + + mRunTimeRanges[run].emplace_back(rmin, rmax); + cntr++; + } else { + logError(); + } } + LOGP(info, "Read {} time-spans for {} runs from {}", cntr, mRunTimeRanges.size(), flname); + inputFile.close(); } ///_______________________________________ @@ -166,11 +340,35 @@ void CTFReaderSpec::openCTFFile(const std::string& flname) mFilesRead++; mCTFFile.reset(TFile::Open(flname.c_str())); if (!mCTFFile || !mCTFFile->IsOpen() || mCTFFile->IsZombie()) { - throw std::runtime_error("failed to open CTF file"); + throw std::runtime_error(fmt::format("failed to open CTF file {}, skipping", flname)); } mCTFTree.reset((TTree*)mCTFFile->Get(std::string(o2::base::NameConf::CTFTREENAME).c_str())); if (!mCTFTree) { - throw std::runtime_error("failed to load CTF tree from"); + throw std::runtime_error(fmt::format("failed to load CTF tree from {}, skipping", flname)); + } + if (mCTFTree->GetEntries() < 1) { + throw std::runtime_error(fmt::format("CTF tree in {} has 0 entries, skipping", flname)); + } + if (mInput.shuffle) { + if (mInput.ctfIDs.empty()) { + int entries = mCTFTree->GetEntries(); + if (mInput.maxTFs > 0) { + entries = std::min(entries, mInput.maxTFs); + } + if (mInput.maxTFsPerFile > 0) { + entries = std::min(entries, mInput.maxTFsPerFile); + } + mInput.ctfIDs.clear(); + mInput.ctfIDs.resize(entries); + std::iota(mInput.ctfIDs.begin(), mInput.ctfIDs.end(), 0); + } + std::random_device dev; + std::mt19937 gen{dev()}; + std::shuffle(mInput.ctfIDs.begin(), mInput.ctfIDs.end(), gen); + LOGP(info, "will shuffle reading of CTF entries in this order:"); + for (int i{0}; i < (int)mInput.ctfIDs.size(); ++i) { + LOGP(info, "\tTF {:02} -> {:02}", i, mInput.ctfIDs[i]); + } } } catch (const std::exception& e) { LOG(error) << "Cannot process " << flname << ", reason: " << e.what(); @@ -187,21 +385,20 @@ void CTFReaderSpec::openCTFFile(const std::string& flname) ///_______________________________________ void CTFReaderSpec::run(ProcessingContext& pc) { - static bool initOnceDone = false; - if (!initOnceDone) { + if (mInput.tfRateLimit == -999) { mInput.tfRateLimit = std::stoi(pc.services().get().device()->fConfig->GetValue("timeframes-rate-limit")); } - std::string tfFileName; - if (mCTFCounter >= mInput.maxTFs || (!mInput.ctfIDs.empty() && mSelIDEntry >= mInput.ctfIDs.size())) { // done - LOG(info) << "All CTFs from selected range were injected, stopping"; - mRunning = false; - } + bool waitAcknowledged = false; + long startWait = 0; while (mRunning) { - if (mCTFTree) { // there is a tree open with multiple CTF - if (mInput.ctfIDs.empty() || mInput.ctfIDs[mSelIDEntry] == mCTFCounter) { // no selection requested or matching CTF ID is found + if (mCTFTree) { // there is a tree open with multiple CTF + if (mInput.ctfIDs.empty() || mInput.ctfIDs[mSelIDEntry] == mCTFCounter || mInput.shuffle || mInput.reverseCTFIDs) { // no selection requested or matching CTF ID is found LOG(debug) << "TF " << mCTFCounter << " of " << mInput.maxTFs << " loop " << mFileFetcher->getNLoops(); + if (mInput.shuffle || mInput.reverseCTFIDs) { + mCurrTreeEntry = mInput.ctfIDs[mSelIDEntry]; + } mSelIDEntry++; if (processTF(pc)) { break; @@ -220,16 +417,47 @@ void CTFReaderSpec::run(ProcessingContext& pc) mRunning = false; break; } + if (!waitAcknowledged) { + startWait = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + waitAcknowledged = true; + } pc.services().get().waitFor(5); continue; } + if (waitAcknowledged) { + long waitTime = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count() - startWait; + mTotalWaitTime += waitTime; + if (++mNWaits > 1) { + LOGP(warn, "Resuming reading after waiting for data {:.2} s (accumulated {:.2} s delay in {} waits)", 1e-6 * waitTime, 1e-6 * mTotalWaitTime, mNWaits); + } + waitAcknowledged = false; + } LOG(info) << "Reading CTF input " << ' ' << tfFileName; openCTFFile(tfFileName); } + + if (mCTFCounter >= mInput.maxTFs || (!mInput.ctfIDs.empty() && mSelIDEntry >= mInput.ctfIDs.size())) { // done + LOGP(info, "All CTFs from selected range were injected, stopping"); + mRunning = false; + } else if (mRunning && !mCTFTree && mFileFetcher->getNextFileInQueue().empty() && !mFileFetcher->isRunning()) { // previous tree was done, can we read more? + mRunning = false; + } + if (!mRunning) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); stopReader(); + const std::string dummy{"ctf_read_ntf.txt"}; + if (mCTFCounterAcc == 0) { + LOGP(warn, "No TF passed selection, writing a 0 to file {}", dummy); + } + try { + std::ofstream outfile; + outfile.open(dummy, std::ios::out | std::ios::trunc); + outfile << mCTFCounterAcc << std::endl; + } catch (...) { + LOGP(error, "Failed to write {}", dummy); + } } } @@ -252,7 +480,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) } if (mUseLocalTFCounter) { - ctfHeader.tfCounter = mCTFCounter; + ctfHeader.tfCounter = mCTFCounterAcc; } LOG(info) << ctfHeader; @@ -263,23 +491,35 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) timingInfo.tfCounter = ctfHeader.tfCounter; timingInfo.runNumber = ctfHeader.run; + if (mRunTimeRanges.size() && timingInfo.runNumber != mRunNumberPrev) { + runTimeRangesToIRFrameSelector(timingInfo); + } + mRunNumberPrev = timingInfo.runNumber; + gsl::span irSpan{}; if (mIRFrameSelector.isSet()) { o2::InteractionRecord ir0(0, timingInfo.firstTForbit); - // we cannot have GRPECS via DPL CCDB fetcher in the CTFReader, so we use mTFLength extracted from the HBFUtils o2::InteractionRecord ir1(o2::constants::lhc::LHCMaxBunches - 1, timingInfo.firstTForbit < 0xffffffff - (mTFLength - 1) ? timingInfo.firstTForbit + (mTFLength - 1) : 0xffffffff); - auto irSpan = mIRFrameSelector.getMatchingFrames({ir0, ir1}); - if (irSpan.size() == 0 && mInput.skipSkimmedOutTF) { - LOGP(info, "Skimming did not define any selection for TF [{}] : [{}]", ir0.asString(), ir1.asString()); + irSpan = mIRFrameSelector.getMatchingFrames({ir0, ir1}); + bool acc = true; + if (mInput.skipSkimmedOutTF) { + acc = (irSpan.size() > 0) ? !mInput.invertIRFramesSelection : mInput.invertIRFramesSelection; + LOGP(info, "IRFrame selection contains {} frames for TF [{}] : [{}]: {}use this TF (selection inversion mode is {})", + irSpan.size(), ir0.asString(), ir1.asString(), acc ? "" : "do not ", mInput.invertIRFramesSelection ? "ON" : "OFF"); + } + if (!acc) { return false; - } else { + } + if (mInput.checkTFLimitBeforeReading) { limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); - LOGP(info, "{} IR-Frames are selected for TF [{}] : [{}]", irSpan.size(), ir0.asString(), ir1.asString()); } - auto outVec = pc.outputs().make>(OutputRef{"selIRFrames"}, irSpan.begin(), irSpan.end()); } else { - limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); + if (mInput.checkTFLimitBeforeReading) { + limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); + } + } + if (mIFRamesOut) { + auto outVec = pc.outputs().make>(OutputRef{"selIRFrames"}, irSpan.begin(), irSpan.end()); } - // send CTF Header pc.outputs().snapshot({"header", mInput.subspec}, ctfHeader); @@ -299,6 +539,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) processDetector(DetID::CPV, ctfHeader, pc); processDetector(DetID::ZDC, ctfHeader, pc); processDetector(DetID::CTP, ctfHeader, pc); + mCTFCounterAcc++; // send sTF acknowledge message if (!mInput.sup0xccdb) { @@ -312,7 +553,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) checkTreeEntries(); mTimer.Stop(); - // do we need to way to respect the delay ? + // do we need to wait to respect the delay ? long tNow = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); if (mCTFCounter) { auto tDiff = tNow - mLastSendTime; @@ -320,6 +561,9 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) pc.services().get().waitFor((mInput.delay_us - tDiff) / 1000); // respect requested delay before sending } } + if (!mInput.checkTFLimitBeforeReading) { + limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); + } tNow = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); LOGP(info, "Read CTF {} {} in {:.3f} s, {:.4f} s elapsed from previous CTF", mCTFCounter, entryStr, mTimer.CpuTime() - cput, mCTFCounter ? 1e-6 * (tNow - mLastSendTime) : 0.); mLastSendTime = tNow; @@ -330,8 +574,13 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) ///_______________________________________ void CTFReaderSpec::checkTreeEntries() { - // check if the tree has entries left, if needed, close current tree/file - if (++mCurrTreeEntry >= mCTFTree->GetEntries()) { // this file is done, check if there are other files + bool reachedEnd{false}; + if (mInput.shuffle || mInput.reverseCTFIDs) { // last entry is last id + reachedEnd = (mCurrTreeEntry == mInput.ctfIDs.back()); + } else { // check if the tree has entries left, if needed, close current tree/file + reachedEnd = (++mCurrTreeEntry >= mCTFTree->GetEntries()); + } + if (reachedEnd || (mInput.maxTFsPerFile > 0 && mCurrTreeEntry >= mInput.maxTFsPerFile)) { // this file is done, check if there are other files mCTFTree.reset(); mCTFFile->Close(); mCTFFile.reset(); @@ -356,22 +605,6 @@ void CTFReaderSpec::setMessageHeader(ProcessingContext& pc, const CTFHeader& ctf dph->creation = ctfHeader.creationTime; } -///_______________________________________ -template -void CTFReaderSpec::processDetector(DetID det, const CTFHeader& ctfHeader, ProcessingContext& pc) const -{ - if (mInput.detMask[det]) { - const auto lbl = det.getName(); - auto& bufVec = pc.outputs().make>({lbl, mInput.subspec}, ctfHeader.detectors[det] ? sizeof(C) : 0); - if (ctfHeader.detectors[det]) { - C::readFromTree(bufVec, *(mCTFTree.get()), lbl, mCurrTreeEntry); - } else if (!mInput.allowMissingDetectors) { - throw std::runtime_error(fmt::format("Requested detector {} is missing in the CTF", lbl)); - } - // setMessageHeader(pc, ctfHeader, lbl); - } -} - ///_______________________________________ void CTFReaderSpec::tryToFixCTFHeader(CTFHeader& ctfHeader) const { @@ -430,20 +663,36 @@ DataProcessorSpec getCTFReaderSpec(const CTFReaderInp& inp) for (auto id = DetID::First; id <= DetID::Last; id++) { if (inp.detMask[id]) { DetID det(id); - outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec, Lifetime::Timeframe); + if (det == DetID::ITS) { + uint32_t nLayers = inp.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec * 100 + iLayer, Lifetime::Timeframe); + } + } else if (det == DetID::MFT) { + uint32_t nLayers = inp.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec * 100 + iLayer, Lifetime::Timeframe); + } + } else { + outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec, Lifetime::Timeframe); + } } } - if (!inp.fileIRFrames.empty()) { + if (!inp.fileIRFrames.empty() || !inp.fileRunTimeSpans.empty()) { outputs.emplace_back(OutputLabel{"selIRFrames"}, "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } if (!inp.sup0xccdb) { outputs.emplace_back(OutputSpec{{"TFDist"}, o2::header::gDataOriginFLP, o2::header::gDataDescriptionDISTSTF, 0xccdb}); } - options.emplace_back(ConfigParamSpec{"select-ctf-ids", VariantType::String, "", {"comma-separated list CTF IDs to inject (from cumulative counter of CTFs seen)"}}); + options.emplace_back(ConfigParamSpec{"reverse-select-ctf-ids", VariantType::Bool, false, {"reverse order of to inject CTF IDs"}}); options.emplace_back(ConfigParamSpec{"impose-run-start-timstamp", VariantType::Int64, 0L, {"impose run start time stamp (ms), ignored if 0"}}); options.emplace_back(ConfigParamSpec{"local-tf-counter", VariantType::Bool, false, {"reassign header.tfCounter from local TF counter"}}); - options.emplace_back(ConfigParamSpec{"fetch-failure-threshold", VariantType::Float, 0.f, {"Fatil if too many failures( >0: fraction, <0: abs number, 0: no threshold)"}}); + options.emplace_back(ConfigParamSpec{"fetch-failure-threshold", VariantType::Float, 0.f, {"Fail if too many failures( >0: fraction, <0: abs number, 0: no threshold)"}}); + options.emplace_back(ConfigParamSpec{"limit-tf-before-reading", VariantType::Bool, false, {"Check TF limiting before reading new TF, otherwhise before injecting it"}}); + options.emplace_back(ConfigParamSpec{"max-tf", VariantType::Int, -1, {"max CTFs to process (<= 0 : infinite)"}}); + options.emplace_back(ConfigParamSpec{"max-tf-per-file", VariantType::Int, -1, {"max TFs to process per ctf file (<= 0 : infinite)"}}); + if (!inp.metricChannel.empty()) { options.emplace_back(ConfigParamSpec{"channel-config", VariantType::String, inp.metricChannel, {"Out-of-band channel config for TF throttling"}}); } diff --git a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx index d9d3afcfeaefa..f175bf4c2e5d3 100644 --- a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx @@ -12,11 +12,10 @@ /// @file CTFWriterSpec.cxx #include "Framework/Logger.h" -#include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/InputSpec.h" +#include "Framework/Task.h" #include "Framework/RawDeviceService.h" -#include "Framework/CommonServices.h" #include "Framework/DataTakingContext.h" #include "Framework/TimingInfo.h" #include @@ -29,6 +28,7 @@ #include "DetectorsCommonDataFormats/EncodedBlocks.h" #include "DetectorsCommonDataFormats/FileMetaData.h" #include "CommonUtils/StringUtils.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/CTF.h" #include "DataFormatsTPC/CTF.h" #include "DataFormatsTRD/CTF.h" @@ -44,7 +44,10 @@ #include "DataFormatsCPV/CTF.h" #include "DataFormatsZDC/CTF.h" #include "DataFormatsCTP/CTF.h" -#include "rANS/rans.h" + +#include "rANS/histogram.h" +#include "rANS/compat.h" + #include #include #include @@ -59,6 +62,7 @@ #include #include #include +#include using namespace o2::framework; @@ -88,19 +92,21 @@ size_t appendToTree(TTree& tree, const std::string brname, T& ptr) } using DetID = o2::detectors::DetID; -using FTrans = o2::rans::FrequencyTable; +using FTrans = o2::rans::DenseHistogram; -class CTFWriterSpec : public o2::framework::Task +class CTFWriterSpec final : public o2::framework::Task { public: CTFWriterSpec() = delete; - CTFWriterSpec(DetID::mask_t dm, const std::string& outType, int verbosity, int reportInterval); + CTFWriterSpec(const o2::ctf::CTFWriterInp&); ~CTFWriterSpec() final { finalize(); } void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final { finalize(); } void stop() final { finalize(); } - bool isPresent(DetID id) const { return mDets[id]; } + bool isPresent(DetID id) const { return mInput.detMask[id]; } + + static std::string getBinding(const std::string& name, int spec) { return fmt::format("{}_{}", name, spec); } private: void updateTimeDependentParams(ProcessingContext& pc); @@ -117,7 +123,7 @@ class CTFWriterSpec : public o2::framework::Task void removeLockFile(); void finalize(); - DetID::mask_t mDets; // detectors + CTFWriterInp mInput; bool mFinalized = false; bool mWriteCTF = true; bool mCreateDict = false; @@ -125,27 +131,30 @@ class CTFWriterSpec : public o2::framework::Task bool mStoreMetaFile = false; bool mRejectCurrentTF = false; bool mFallBackDirUsed = false; - int mReportInterval = -1; - int mVerbosity = 0; + bool mFallBackDirProvided = false; int mSaveDictAfter = 0; // if positive and mWriteCTF==true, save dictionary after each mSaveDictAfter TFs processed uint32_t mPrevDictTimeStamp = 0; // timestamp of the previously stored dictionary uint32_t mDictTimeStamp = 0; // timestamp of the currently stored dictionary - size_t mMinSize = 0; // if > 0, accumulate CTFs in the same tree until the total size exceeds this minimum - size_t mMaxSize = 0; // if > MinSize, and accumulated size will exceed this value, stop accumulation (even if mMinSize is not reached) - size_t mChkSize = 0; // if > 0 and fallback storage provided, reserve this size per CTF file in production on primary storage - size_t mAccCTFSize = 0; // so far accumulated size (if any) - size_t mCurrCTFSize = 0; // size of currently processed CTF - size_t mNCTF = 0; // total number of CTFs written - size_t mNCTFPrevDict = 0; // total number of CTFs used for previous dictionary version - size_t mNAccCTF = 0; // total number of CTFs accumulated in the current file - size_t mCTFAutoSave = 0; // if > 0, autosave after so many TFs - size_t mNCTFFiles = 0; // total number of CTF files written - int mMaxCTFPerFile = 0; // max CTFs per files to store - int mRejRate = 0; // CTF rejection rule (>0: percentage to reject randomly, <0: reject if timeslice%|value|!=0) + size_t mMinSize = 0; // if > 0, accumulate CTFs in the same tree until the total size exceeds this minimum + size_t mMaxSize = 0; // if > MinSize, and accumulated size will exceed this value, stop accumulation (even if mMinSize is not reached) + size_t mChkSize = 0; // if > 0 and fallback storage provided, reserve this size per CTF file in production on primary storage + size_t mAccCTFSize = 0; // so far accumulated size (if any) + size_t mCurrCTFSize = 0; // size of currently processed CTF + size_t mNCTF = 0; // total number of CTFs written + size_t mNCTFPrevDict = 0; // total number of CTFs used for previous dictionary version + size_t mNAccCTF = 0; // total number of CTFs accumulated in the current file + int mWaitDiskFull = 0; // if mCheckDiskFull triggers, pause for this amount of ms before new attempt + int mWaitDiskFullMax = -1; // produce fatal mCheckDiskFull block the workflow for more than this time (in ms) + float mCheckDiskFull = 0.; // wait for if available abs. disk space is < mCheckDiskFull (if >0) or if its fraction is < -mCheckDiskFull (if <0) + long mCTFAutoSave = 0; // if > 0, autosave after so many TFs + size_t mNCTFFiles = 0; // total number of CTF files written + int mMaxCTFPerFile = 0; // max CTFs per files to store + int mRejRate = 0; // CTF rejection rule (>0: percentage to reject randomly, <0: reject if timeslice%|value|!=0) + int mCTFFileCompression = 0; // CTF file compression level (if >= 0) + bool mFillMD5 = false; std::vector mTFOrbits{}; // 1st orbits of TF accumulated in current file o2::framework::DataTakingContext mDataTakingContext{}; o2::framework::TimingInfo mTimingInfo{}; - std::string mOutputType{}; // RS FIXME once global/local options clash is solved, --output-type will become device option std::string mDictDir{}; std::string mCTFDir{}; std::string mHostName{}; @@ -180,8 +189,8 @@ class CTFWriterSpec : public o2::framework::Task const std::string CTFWriterSpec::TMPFileEnding{".part"}; //___________________________________________________________________ -CTFWriterSpec::CTFWriterSpec(DetID::mask_t dm, const std::string& outType, int verbosity, int reportInterval) - : mDets(dm), mOutputType(outType), mReportInterval(reportInterval), mVerbosity(verbosity) +CTFWriterSpec::CTFWriterSpec(const o2::ctf::CTFWriterInp& inp) + : mInput(inp) { std::for_each(mIsSaturatedFrequencyTable.begin(), mIsSaturatedFrequencyTable.end(), [](auto& bitset) { bitset.reset(); }); mTimer.Stop(); @@ -192,7 +201,7 @@ CTFWriterSpec::CTFWriterSpec(DetID::mask_t dm, const std::string& outType, int v void CTFWriterSpec::init(InitContext& ic) { // auto outmode = ic.options().get("output-type"); // RS FIXME once global/local options clash is solved, --output-type will become device option - auto outmode = mOutputType; + auto outmode = mInput.outType; if (outmode == "ctf") { mWriteCTF = true; mCreateDict = false; @@ -210,11 +219,13 @@ void CTFWriterSpec::init(InitContext& ic) } mSaveDictAfter = ic.options().get("save-dict-after"); - mCTFAutoSave = ic.options().get("save-ctf-after"); + mCTFAutoSave = ic.options().get("save-ctf-after"); + mCTFFileCompression = ic.options().get("ctf-file-compression"); mCTFMetaFileDir = ic.options().get("meta-output-dir"); if (mCTFMetaFileDir != "/dev/null") { mCTFMetaFileDir = o2::utils::Str::rectifyDirectory(mCTFMetaFileDir); mStoreMetaFile = true; + mFillMD5 = ic.options().get("md5-for-meta"); } mDictDir = o2::utils::Str::rectifyDirectory(ic.options().get("ctf-dict-dir")); mCTFDir = ic.options().get("output-dir"); @@ -227,6 +238,7 @@ void CTFWriterSpec::init(InitContext& ic) mCTFDirFallBack = ic.options().get("output-dir-alt"); if (mCTFDirFallBack != "/dev/null") { mCTFDirFallBack = o2::utils::Str::rectifyDirectory(mCTFDirFallBack); + mFallBackDirProvided = true; } mCreateRunEnvDir = !ic.options().get("ignore-partition-run-dir"); mMinSize = ic.options().get("min-file-size"); @@ -247,6 +259,11 @@ void CTFWriterSpec::init(InitContext& ic) } } } + + mCheckDiskFull = ic.options().get("require-free-disk"); + mWaitDiskFull = 1000 * ic.options().get("wait-for-free-disk"); + mWaitDiskFullMax = 1000 * ic.options().get("max-wait-for-free-disk"); + mChkSize = std::max(size_t(mMinSize * 1.1), mMaxSize); o2::utils::createDirectoriesIfAbsent(LOCKFileDir); @@ -283,66 +300,82 @@ size_t CTFWriterSpec::processDet(o2::framework::ProcessingContext& pc, DetID det { static bool warnedEmpty = false; size_t sz = 0; - if (!isPresent(det) || !pc.inputs().isValid(det.getName())) { + + if (!isPresent(det) || !pc.inputs().isValid(getBinding(det.getName(), 0))) { mSizeReport += fmt::format(" {}:N/A", det.getName()); return sz; } - auto ctfBuffer = pc.inputs().get>(det.getName()); - const o2::ctf::BufferType* bdata = ctfBuffer.data(); - if (bdata) { - if (warnedEmpty) { - throw std::runtime_error(fmt::format("Non-empty input was seen at {}-th TF after empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); - } - const auto ctfImage = C::getImage(bdata); - ctfImage.print(o2::utils::Str::concat_string(det.getName(), ": "), mVerbosity); - if (mWriteCTF && !mRejectCurrentTF) { - sz = ctfImage.appendToTree(*tree, det.getName()); - header.detectors.set(det); - } else { - sz = ctfBuffer.size(); - } - if (mCreateDict) { - if (!mFreqsAccumulation[det].size()) { - mFreqsAccumulation[det].resize(C::getNBlocks()); - mFreqsMetaData[det].resize(C::getNBlocks()); + + uint32_t nLayers = 1; + if (det == DetID::ITS) { + nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + auto binding = getBinding(det.getName(), iLayer); + auto ctfBuffer = pc.inputs().get>(binding); + const o2::ctf::BufferType* bdata = ctfBuffer.data(); + if (bdata) { + if (warnedEmpty) { + throw std::runtime_error(fmt::format("Non-empty input was seen at {}-th TF after empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); } - if (!mHeaders[det]) { // store 1st header - mHeaders[det] = ctfImage.cloneHeader(); - auto& hb = *static_cast(mHeaders[det].get()); - hb.det = det; + const auto ctfImage = C::getImage(bdata); + ctfImage.print(o2::utils::Str::concat_string(binding, ": "), mInput.verbosity); + if (mWriteCTF && !mRejectCurrentTF) { + sz += ctfImage.appendToTree(*tree, nLayers > 1 ? binding : det.getName()); + header.detectors.set(det); + } else { + sz += ctfBuffer.size(); } - for (int ib = 0; ib < C::getNBlocks(); ib++) { - if (!mIsSaturatedFrequencyTable[det][ib]) { - const auto& bl = ctfImage.getBlock(ib); - if (bl.getNDict()) { - auto freq = mFreqsAccumulation[det][ib]; - auto& mdSave = mFreqsMetaData[det][ib]; - const auto& md = ctfImage.getMetadata(ib); - if ([&, this]() { - try { - freq.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min); - } catch (const std::overflow_error& e) { - LOGP(warning, "unable to add frequency table for {}, block {} due to overflow", det.getName(), ib); - mIsSaturatedFrequencyTable[det][ib] = true; - return false; - } - return true; - }()) { - auto newProbBits = static_cast(o2::rans::computeRenormingPrecision(freq)); - mdSave = o2::ctf::Metadata{0, 0, md.messageWordSize, md.coderType, md.streamSize, newProbBits, md.opt, freq.getMinSymbol(), freq.getMaxSymbol(), static_cast(freq.size()), 0, 0}; - mFreqsAccumulation[det][ib] = std::move(freq); + if (mCreateDict) { // RSTODO + if (mFreqsAccumulation[det].empty()) { + mFreqsAccumulation[det].resize(C::getNBlocks()); + mFreqsMetaData[det].resize(C::getNBlocks()); + } + if (!mHeaders[det]) { // store 1st header + mHeaders[det] = ctfImage.cloneHeader(); + auto& hb = *static_cast(mHeaders[det].get()); + hb.det = det; + } + for (int ib = 0; ib < C::getNBlocks(); ib++) { + if (!mIsSaturatedFrequencyTable[det][ib]) { + const auto& bl = ctfImage.getBlock(ib); + if (bl.getNDict()) { + auto freq = mFreqsAccumulation[det][ib]; + auto& mdSave = mFreqsMetaData[det][ib]; + const auto& md = ctfImage.getMetadata(ib); + if ([&, this]() { + try { + freq.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min); + } catch (const std::overflow_error& e) { + LOGP(warning, "unable to add frequency table for {}, block {} due to overflow", det.getName(), ib); + mIsSaturatedFrequencyTable[det][ib] = true; + return false; + } + return true; + }()) { + auto newProbBits = static_cast(o2::rans::compat::computeRenormingPrecision(countNUsedAlphabetSymbols(freq))); + auto histogramView = o2::rans::trim(o2::rans::makeHistogramView(freq)); + mdSave = ctf::detail::makeMetadataRansDict(newProbBits, + static_cast(histogramView.getMin()), + static_cast(histogramView.getMax()), + static_cast(histogramView.size()), + md.opt); + mFreqsAccumulation[det][ib] = std::move(freq); + } } } } } - } - } else { - if (!warnedEmpty) { - if (mNCTF) { - throw std::runtime_error(fmt::format("Empty input was seen at {}-th TF after non-empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); + } else { + if (!warnedEmpty) { + if (mNCTF) { + throw std::runtime_error(fmt::format("Empty input was seen at {}-th TF after non-empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); + } + LOGP(important, "Empty CTF provided for {}, skipping and will not report anymore", det.getName()); + warnedEmpty = true; } - LOGP(important, "Empty CTF provided for {}, skipping and will not report anymore", det.getName()); - warnedEmpty = true; } } mSizeReport += fmt::format(" {}:{}", det.getName(), fmt::group_digits(sz)); @@ -394,10 +427,19 @@ size_t CTFWriterSpec::estimateCTFSize(ProcessingContext& pc) size_t s = 0; for (auto id = DetID::First; id <= DetID::Last; id++) { DetID det(id); - if (!isPresent(det) || !pc.inputs().isValid(det.getName())) { - continue; + uint32_t nLayers = 1; + if (det == DetID::ITS) { + nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + auto binding = getBinding(det.getName(), iLayer); + if (!isPresent(det) || !pc.inputs().isValid(binding)) { + continue; + } + s += pc.inputs().get>(binding).size(); } - s += pc.inputs().get>(det.getName()).size(); } return s; } @@ -416,35 +458,72 @@ void CTFWriterSpec::run(ProcessingContext& pc) mCurrCTFSize = estimateCTFSize(pc); if (mWriteCTF && !mRejectCurrentTF) { prepareTFTreeAndFile(); + + int totalWait = 0, nwaitCycles = 0; + while ((mFallBackDirUsed || !mFallBackDirProvided) && mCheckDiskFull) { // we are on the physical disk and not on the RAM disk + constexpr size_t MB = 1024 * 1024; + constexpr int showFirstN = 10, prsecaleWarnings = 50; + try { + const auto si = std::filesystem::space(mCTFFileOut->GetName()); + std::string wmsg{}; + if (mCheckDiskFull > 0.f && si.available < mCheckDiskFull) { + nwaitCycles++; + wmsg = fmt::format("Disk has {} MB available while at least {} MB is requested, wait for {} ms (on top of {} ms)", si.available / MB, size_t(mCheckDiskFull) / MB, mWaitDiskFull, totalWait); + } else if (mCheckDiskFull < 0.f && float(si.available) / si.capacity < -mCheckDiskFull) { // relative margin requested + nwaitCycles++; + wmsg = fmt::format("Disk has {:.3f}% available while at least {:.3f}% is requested, wait for {} ms (on top of {} ms)", si.capacity ? float(si.available) / si.capacity * 100.f : 0., -mCheckDiskFull, mWaitDiskFull, totalWait); + } else { + nwaitCycles = 0; + } + if (nwaitCycles) { + if (mWaitDiskFullMax > 0 && totalWait > mWaitDiskFullMax) { + closeTFTreeAndFile(); // try to save whatever we have + LOGP(fatal, "Disk has {} MB available out of {} MB after waiting for {} ms", si.available / MB, si.capacity / MB, mWaitDiskFullMax); + } + if (nwaitCycles < showFirstN + 1 || (prsecaleWarnings && (nwaitCycles % prsecaleWarnings) == 0)) { + LOG(alarm) << wmsg; + } + pc.services().get().waitFor((unsigned int)(mWaitDiskFull)); + totalWait += mWaitDiskFull; + continue; + } + } catch (std::exception const& e) { + LOG(fatal) << "unable to query disk space info for path " << mCurrentCTFFileNameFull << ", reason: " << e.what(); + } + break; + } } // create header CTFHeader header{mTimingInfo.runNumber, mTimingInfo.creation, mTimingInfo.firstTForbit, mTimingInfo.tfCounter}; size_t szCTF = 0; mSizeReport = ""; - szCTF += processDet(pc, DetID::ITS, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::TPC, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::TRD, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::TOF, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::PHS, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::CPV, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::EMC, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::HMP, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::MFT, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::MCH, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::MID, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::ZDC, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::FT0, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::FV0, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::FDD, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::CTP, header, mCTFTreeOut.get()); - if (mReportInterval > 0 && (mTimingInfo.tfCounter % mReportInterval) == 0) { - LOGP(important, "CTF {} size report:{}", mTimingInfo.tfCounter, mSizeReport); + std::array szCTFperDet{0}; // DetID::TST is between FDD and CTP and remains empty + szCTFperDet[DetID::ITS] = processDet(pc, DetID::ITS, header, mCTFTreeOut.get()); + szCTFperDet[DetID::TPC] = processDet(pc, DetID::TPC, header, mCTFTreeOut.get()); + szCTFperDet[DetID::TRD] = processDet(pc, DetID::TRD, header, mCTFTreeOut.get()); + szCTFperDet[DetID::TOF] = processDet(pc, DetID::TOF, header, mCTFTreeOut.get()); + szCTFperDet[DetID::PHS] = processDet(pc, DetID::PHS, header, mCTFTreeOut.get()); + szCTFperDet[DetID::CPV] = processDet(pc, DetID::CPV, header, mCTFTreeOut.get()); + szCTFperDet[DetID::EMC] = processDet(pc, DetID::EMC, header, mCTFTreeOut.get()); + szCTFperDet[DetID::HMP] = processDet(pc, DetID::HMP, header, mCTFTreeOut.get()); + szCTFperDet[DetID::MFT] = processDet(pc, DetID::MFT, header, mCTFTreeOut.get()); + szCTFperDet[DetID::MCH] = processDet(pc, DetID::MCH, header, mCTFTreeOut.get()); + szCTFperDet[DetID::MID] = processDet(pc, DetID::MID, header, mCTFTreeOut.get()); + szCTFperDet[DetID::ZDC] = processDet(pc, DetID::ZDC, header, mCTFTreeOut.get()); + szCTFperDet[DetID::FT0] = processDet(pc, DetID::FT0, header, mCTFTreeOut.get()); + szCTFperDet[DetID::FV0] = processDet(pc, DetID::FV0, header, mCTFTreeOut.get()); + szCTFperDet[DetID::FDD] = processDet(pc, DetID::FDD, header, mCTFTreeOut.get()); + szCTFperDet[DetID::CTP] = processDet(pc, DetID::CTP, header, mCTFTreeOut.get()); + szCTF = std::accumulate(szCTFperDet.begin(), szCTFperDet.end(), 0); + if (mInput.reportInterval > 0 && (mTimingInfo.tfCounter % mInput.reportInterval) == 0) { + LOGP(important, "CTF {} size report:{} - Total:{}", mTimingInfo.tfCounter, mSizeReport, fmt::group_digits(szCTF)); } mTimer.Stop(); if (mWriteCTF && !mRejectCurrentTF) { szCTF += appendToTree(*mCTFTreeOut.get(), "CTFHeader", header); + size_t prevSizeMB = mAccCTFSize / (1 << 20); mAccCTFSize += szCTF; mCTFTreeOut->SetEntries(++mNAccCTF); mTFOrbits.push_back(mTimingInfo.firstTForbit); @@ -462,7 +541,7 @@ void CTFWriterSpec::run(ProcessingContext& pc) if (mAccCTFSize >= mMinSize || (mMaxCTFPerFile > 0 && mNAccCTF >= mMaxCTFPerFile)) { closeTFTreeAndFile(); - } else if (mCTFAutoSave > 0 && mNAccCTF % mCTFAutoSave == 0) { + } else if ((mCTFAutoSave > 0 && mNAccCTF % mCTFAutoSave == 0) || (mCTFAutoSave < 0 && int(prevSizeMB / (-mCTFAutoSave)) != size_t(mAccCTFSize / (1 << 20)) / (-mCTFAutoSave))) { mCTFTreeOut->AutoSave("override"); } } else { @@ -473,6 +552,9 @@ void CTFWriterSpec::run(ProcessingContext& pc) if (mCreateDict && mSaveDictAfter > 0 && (mNCTF % mSaveDictAfter) == 0) { storeDictionaries(); } + int dummy = 0; + pc.outputs().snapshot({"ctfdone", 0}, dummy); + pc.outputs().snapshot(Output{"CTF", "SIZES", 0}, szCTFperDet); } //___________________________________________________________________ @@ -515,7 +597,7 @@ void CTFWriterSpec::prepareTFTreeAndFile() closeTFTreeAndFile(); mFallBackDirUsed = false; auto ctfDir = mCTFDir.empty() ? o2::utils::Str::rectifyDirectory("./") : mCTFDir; - if (mChkSize > 0 && (mCTFDirFallBack != "/dev/null")) { + if (mChkSize > 0 && mFallBackDirProvided) { createLockFile(0); auto sz = getAvailableDiskSpace(ctfDir, 0); // check main storage if (sz < mChkSize) { @@ -535,6 +617,9 @@ void CTFWriterSpec::prepareTFTreeAndFile() mCurrentCTFFileName = o2::base::NameConf::getCTFFileName(mTimingInfo.runNumber, mTimingInfo.firstTForbit, mTimingInfo.tfCounter, mHostName); mCurrentCTFFileNameFull = fmt::format("{}{}", ctfDir, mCurrentCTFFileName); mCTFFileOut.reset(TFile::Open(fmt::format("{}{}", mCurrentCTFFileNameFull, TMPFileEnding).c_str(), "recreate")); // to prevent premature external usage, use temporary name + if (mCTFFileCompression >= 0) { + mCTFFileOut->SetCompressionLevel(mCTFFileCompression); + } mCTFTreeOut = std::make_unique(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); mNCTFFiles++; @@ -551,13 +636,13 @@ void CTFWriterSpec::closeTFTreeAndFile() mCTFTreeOut.reset(); mCTFFileOut->Close(); mCTFFileOut.reset(); - if (!TMPFileEnding.empty()) { - std::filesystem::rename(o2::utils::Str::concat_string(mCurrentCTFFileNameFull, TMPFileEnding), mCurrentCTFFileNameFull); - } // write CTF file metaFile data + auto actualFileName = TMPFileEnding.empty() ? mCurrentCTFFileNameFull : o2::utils::Str::concat_string(mCurrentCTFFileNameFull, TMPFileEnding); if (mStoreMetaFile) { o2::dataformats::FileMetaData ctfMetaData; - ctfMetaData.fillFileData(mCurrentCTFFileNameFull); + if (!ctfMetaData.fillFileData(actualFileName, mFillMD5, TMPFileEnding)) { + throw std::runtime_error("metadata file was requested but not created"); + } ctfMetaData.setDataTakingContext(mDataTakingContext); ctfMetaData.type = mMetaDataType; ctfMetaData.priority = mFallBackDirUsed ? "low" : "high"; @@ -568,10 +653,15 @@ void CTFWriterSpec::closeTFTreeAndFile() std::ofstream metaFileOut(metaFileNameTmp); metaFileOut << ctfMetaData; metaFileOut.close(); + if (!TMPFileEnding.empty()) { + std::filesystem::rename(actualFileName, mCurrentCTFFileNameFull); + } std::filesystem::rename(metaFileNameTmp, metaFileName); } catch (std::exception const& e) { LOG(error) << "Failed to store CTF meta data file " << metaFileName << ", reason: " << e.what(); } + } else if (!TMPFileEnding.empty()) { + std::filesystem::rename(actualFileName, mCurrentCTFFileNameFull); } } catch (std::exception const& e) { LOG(error) << "Failed to finalize CTF file " << mCurrentCTFFileNameFull << ", reason: " << e.what(); @@ -589,7 +679,7 @@ void CTFWriterSpec::storeDictionaries() // monolitic dictionary in tree format mDictTimeStamp = uint32_t(std::time(nullptr)); auto getFileName = [this](bool curr) { - return fmt::format("{}{}Tree_{}_{}_{}.root", this->mDictDir, o2::base::NameConf::CTFDICT, DetID::getNames(this->mDets, '-'), curr ? this->mDictTimeStamp : this->mPrevDictTimeStamp, curr ? this->mNCTF : this->mNCTFPrevDict); + return fmt::format("{}{}Tree_{}_{}_{}.root", this->mDictDir, o2::base::NameConf::CTFDICT, DetID::getNames(this->mInput.detMask, '-'), curr ? this->mDictTimeStamp : this->mPrevDictTimeStamp, curr ? this->mNCTF : this->mNCTFPrevDict); }; auto dictFileName = getFileName(true); mDictFileOut.reset(TFile::Open(dictFileName.c_str(), "recreate")); @@ -717,33 +807,49 @@ size_t CTFWriterSpec::getAvailableDiskSpace(const std::string& path, int level) } //___________________________________________________________________ -DataProcessorSpec getCTFWriterSpec(DetID::mask_t dets, const std::string& outType, int verbosity, int reportInterval) +DataProcessorSpec getCTFWriterSpec(const o2::ctf::CTFWriterInp& inp) { std::vector inputs; LOG(debug) << "Detectors list:"; for (auto id = DetID::First; id <= DetID::Last; id++) { - if (dets[id]) { - inputs.emplace_back(DetID::getName(id), DetID::getDataOrigin(id), "CTFDATA", 0, Lifetime::Timeframe); + if (inp.detMask[id]) { + uint32_t nLayers = 1; + DetID det{id}; + if (det == DetID::ITS) { + nLayers = inp.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = inp.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + inputs.emplace_back(CTFWriterSpec::getBinding(det.getName(), iLayer), det.getDataOrigin(), "CTFDATA", iLayer, Lifetime::Timeframe); + } LOG(debug) << "Det " << DetID::getName(id) << " added"; } } return DataProcessorSpec{ "ctf-writer", inputs, - Outputs{}, - AlgorithmSpec{adaptFromTask(dets, outType, verbosity, reportInterval)}, // RS FIXME once global/local options clash is solved, --output-type will become device option - Options{ //{"output-type", VariantType::String, "ctf", {"output types: ctf (per TF) or dict (create dictionaries) or both or none"}}, - {"save-ctf-after", VariantType::Int, 0, {"if > 0, autosave CTF tree with multiple CTFs after every N CTFs"}}, - {"save-dict-after", VariantType::Int, 0, {"if > 0, in dictionary generation mode save it dictionary after certain number of TFs processed"}}, - {"ctf-dict-dir", VariantType::String, "none", {"CTF dictionary directory, must exist"}}, - {"output-dir", VariantType::String, "none", {"CTF output directory, must exist"}}, - {"output-dir-alt", VariantType::String, "/dev/null", {"Alternative CTF output directory, must exist (if not /dev/null)"}}, - {"meta-output-dir", VariantType::String, "/dev/null", {"CTF metadata output directory, must exist (if not /dev/null)"}}, - {"min-file-size", VariantType::Int64, 0l, {"accumulate CTFs until given file size reached"}}, - {"max-file-size", VariantType::Int64, 0l, {"if > 0, try to avoid exceeding given file size, also used for space check"}}, - {"max-ctf-per-file", VariantType::Int, 0, {"if > 0, avoid storing more than requested CTFs per file"}}, - {"ctf-rejection", VariantType::Int, 0, {">0: percentage to reject randomly, <0: reject if timeslice%|value|!=0"}}, - {"ignore-partition-run-dir", VariantType::Bool, false, {"Do not creare partition-run directory in output-dir"}}}}; + Outputs{{OutputLabel{"ctfdone"}, "CTF", "DONE", 0, Lifetime::Timeframe}, + {"CTF", "SIZES", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask(inp)}, + Options{ + //{"output-type", VariantType::String, "ctf", {"output types: ctf (per TF) or dict (create dictionaries) or both or none"}}, + {"save-ctf-after", VariantType::Int64, 0ll, {"autosave CTF tree with multiple CTFs after every N CTFs if >0 or every -N MBytes if < 0"}}, + {"save-dict-after", VariantType::Int, 0, {"if > 0, in dictionary generation mode save it dictionary after certain number of TFs processed"}}, + {"ctf-dict-dir", VariantType::String, "none", {"CTF dictionary directory, must exist"}}, + {"output-dir", VariantType::String, "none", {"CTF output directory, must exist"}}, + {"output-dir-alt", VariantType::String, "/dev/null", {"Alternative CTF output directory, must exist (if not /dev/null)"}}, + {"meta-output-dir", VariantType::String, "/dev/null", {"CTF metadata output directory, must exist (if not /dev/null)"}}, + {"md5-for-meta", VariantType::Bool, false, {"fill CTF file MD5 sum in the metadata file"}}, + {"min-file-size", VariantType::Int64, 0l, {"accumulate CTFs until given file size reached"}}, + {"max-file-size", VariantType::Int64, 0l, {"if > 0, try to avoid exceeding given file size, also used for space check"}}, + {"max-ctf-per-file", VariantType::Int, 0, {"if > 0, avoid storing more than requested CTFs per file"}}, + {"ctf-rejection", VariantType::Int, 0, {">0: percentage to reject randomly, <0: reject if timeslice%|value|!=0"}}, + {"ctf-file-compression", VariantType::Int, 0, {"if >= 0: impose CTF file compression level"}}, + {"require-free-disk", VariantType::Float, 0.f, {"pause writing op. if available disk space is below this margin, in bytes if >0, as a fraction of total if <0"}}, + {"wait-for-free-disk", VariantType::Float, 10.f, {"if paused due to the low disk space, recheck after this time (in s)"}}, + {"max-wait-for-free-disk", VariantType::Float, 60.f, {"produce fatal if paused due to the low disk space for more than this amount in s."}}, + {"ignore-partition-run-dir", VariantType::Bool, false, {"Do not creare partition-run directory in output-dir"}}}}; } } // namespace ctf diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index c90efc2732536..366fa76f74983 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -21,9 +21,11 @@ #include "DetectorsCommonDataFormats/DetID.h" #include "CommonUtils/ConfigurableParam.h" #include "Algorithm/RangeTokenizer.h" +#include "DetectorsBase/DPLWorkflowUtils.h" // Specific detectors specs #include "ITSMFTWorkflow/EntropyDecoderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "TPCWorkflow/EntropyDecoderSpec.h" #include "TRDWorkflow/EntropyDecoderSpec.h" #include "HMPIDWorkflow/EntropyDecoderSpec.h" @@ -38,6 +40,9 @@ #include "CPVWorkflow/EntropyDecoderSpec.h" #include "ZDCWorkflow/EntropyDecoderSpec.h" #include "CTPWorkflow/EntropyDecoderSpec.h" +#ifdef WITH_OPENMP +#include +#endif using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -48,12 +53,14 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options; options.push_back(ConfigParamSpec{"ctf-input", VariantType::String, "none", {"comma-separated list CTF input files"}}); + options.push_back(ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}); options.push_back(ConfigParamSpec{"onlyDet", VariantType::String, std::string{DetID::ALL}, {"comma-separated list of detectors to accept. Overrides skipDet"}}); options.push_back(ConfigParamSpec{"skipDet", VariantType::String, std::string{DetID::NONE}, {"comma-separate list of detectors to skip"}}); - options.push_back(ConfigParamSpec{"max-tf", VariantType::Int, -1, {"max CTFs to process (<= 0 : infinite)"}}); options.push_back(ConfigParamSpec{"loop", VariantType::Int, 0, {"loop N times (infinite for N<0)"}}); options.push_back(ConfigParamSpec{"delay", VariantType::Float, 0.f, {"delay in seconds between consecutive TFs sending"}}); + options.push_back(ConfigParamSpec{"shuffle", VariantType::Bool, false, {"shuffle TF sending order (for debug)"}}); options.push_back(ConfigParamSpec{"copy-cmd", VariantType::String, "alien_cp ?src file://?dst", {"copy command for remote files or no-copy to avoid copying"}}); // Use "XrdSecPROTOCOL=sss,unix xrdcp -N root://eosaliceo2.cern.ch/?src ?dst" for direct EOS access + options.push_back(ConfigParamSpec{"copy-dir", VariantType::String, "/tmp/", {"copy base directory for remote files"}}); options.push_back(ConfigParamSpec{"ctf-file-regex", VariantType::String, ".*o2_ctf_run.+\\.root$", {"regex string to identify CTF files"}}); options.push_back(ConfigParamSpec{"remote-regex", VariantType::String, "^(alien://|)/alice/data/.+", {"regex string to identify remote files"}}); // Use "^/eos/aliceo2/.+" for direct EOS access options.push_back(ConfigParamSpec{"max-cached-files", VariantType::Int, 3, {"max CTF files queued (copied for remote source)"}}); @@ -63,7 +70,9 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"ctf-data-subspec", VariantType::Int, 0, {"subspec to use for decoded CTF messages (use non-0 if CTF writer will be attached downstream)"}}); options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}); options.push_back(ConfigParamSpec{"ir-frames-files", VariantType::String, "", {"If non empty, inject selected IRFrames from this file"}}); + options.push_back(ConfigParamSpec{"run-time-span-file", VariantType::String, "", {"If non empty, inject selected IRFrames from this text file (run, min/max orbit or unix time)"}}); options.push_back(ConfigParamSpec{"skip-skimmed-out-tf", VariantType::Bool, false, {"Do not process TFs with empty IR-Frame coverage"}}); + options.push_back(ConfigParamSpec{"invert-irframe-selection", VariantType::Bool, false, {"Select only frames mentioned in ir-frames-file (skip-skimmed-out-tf applied to TF not selected!)"}}); // options.push_back(ConfigParamSpec{"its-digits", VariantType::Bool, false, {"convert ITS clusters to digits"}}); options.push_back(ConfigParamSpec{"mft-digits", VariantType::Bool, false, {"convert MFT clusters to digits"}}); @@ -72,6 +81,8 @@ void customize(std::vector& workflowOptions) // options.push_back(ConfigParamSpec{"timeframes-shm-limit", VariantType::String, "0", {"Minimum amount of SHM required in order to publish data"}}); options.push_back(ConfigParamSpec{"metric-feedback-channel-format", VariantType::String, "name=metric-feedback,type=pull,method=connect,address=ipc://{}metric-feedback-{},transport=shmem,rateLogging=0", {"format for the metric-feedback channel for TF rate limiting"}}); + options.push_back(ConfigParamSpec{"combine-devices", VariantType::Bool, false, {"combine multiple DPL devices (entropy decoders)"}}); + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -111,77 +122,153 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (ctfInput.delay_us < 0) { ctfInput.delay_us = 0; } - int n = configcontext.options().get("max-tf"); - ctfInput.maxTFs = n > 0 ? n : 0x7fffffff; ctfInput.maxFileCache = std::max(1, configcontext.options().get("max-cached-files")); + ctfInput.shuffle = configcontext.options().get("shuffle"); ctfInput.copyCmd = configcontext.options().get("copy-cmd"); + ctfInput.copyDir = configcontext.options().get("copy-dir"); ctfInput.tffileRegex = configcontext.options().get("ctf-file-regex"); ctfInput.remoteRegex = configcontext.options().get("remote-regex"); ctfInput.allowMissingDetectors = configcontext.options().get("allow-missing-detectors"); ctfInput.sup0xccdb = !configcontext.options().get("send-diststf-0xccdb"); ctfInput.minSHM = std::stoul(configcontext.options().get("timeframes-shm-limit")); ctfInput.fileIRFrames = configcontext.options().get("ir-frames-files"); + ctfInput.fileRunTimeSpans = configcontext.options().get("run-time-span-file"); ctfInput.skipSkimmedOutTF = configcontext.options().get("skip-skimmed-out-tf"); + ctfInput.invertIRFramesSelection = configcontext.options().get("invert-irframe-selection"); + ctfInput.dictOpt = configcontext.options().get("ctf-dict"); int verbosity = configcontext.options().get("ctf-reader-verbosity"); int rateLimitingIPCID = std::stoi(configcontext.options().get("timeframes-rate-limit-ipcid")); std::string chanFmt = configcontext.options().get("metric-feedback-channel-format"); if (rateLimitingIPCID > -1 && !chanFmt.empty()) { - ctfInput.metricChannel = fmt::format(chanFmt, o2::framework::ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); + ctfInput.metricChannel = fmt::format(fmt::runtime(chanFmt), o2::framework::ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); } + if (!ctfInput.fileRunTimeSpans.empty()) { + ctfInput.skipSkimmedOutTF = true; + } + if (!ctfInput.fileIRFrames.empty() && !ctfInput.fileRunTimeSpans.empty()) { + LOGP(fatal, "One cannot provide --ir-frames-files and --run-time-span-file options simultaneously"); + } + ctfInput.doITSStaggering = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + ctfInput.doMFTStaggering = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); specs.push_back(o2::ctf::getCTFReaderSpec(ctfInput)); + auto pipes = configcontext.options().get("pipeline"); + std::unordered_map plines; + auto ptokens = o2::utils::Str::tokenize(pipes, ','); + for (auto& token : ptokens) { + auto split = token.find(":"); + if (split == std::string::npos) { + throw std::runtime_error("bad pipeline definition. Syntax :"); + } + auto key = token.substr(0, split); + token.erase(0, split + 1); + size_t error; + auto value = std::stoll(token, &error, 10); + if (token[error] != '\0') { + throw std::runtime_error("Bad pipeline definition. Expecting integer"); + } + if (value > 1) { + plines[key] = value; + } + } + + std::vector decSpecsV; + + auto addSpecs = [&decSpecsV, &plines](DataProcessorSpec&& s) { + auto entry = plines.find(s.name); + size_t mult = (entry == plines.end() || entry->second < 2) ? 1 : entry->second; + if (mult > decSpecsV.size()) { + decSpecsV.resize(mult); + } + decSpecsV[mult - 1].push_back(s); + }; + // add decoders for all allowed detectors. if (ctfInput.detMask[DetID::ITS]) { - specs.push_back(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::ITS), verbosity, configcontext.options().get("its-digits"), ctfInput.subspec)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + addSpecs(o2::itsmft::getITSEntropyDecoderSpec(verbosity, doStag, configcontext.options().get("its-digits"), ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MFT]) { - specs.push_back(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::MFT), verbosity, configcontext.options().get("mft-digits"), ctfInput.subspec)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); + addSpecs(o2::itsmft::getMFTEntropyDecoderSpec(verbosity, doStag, configcontext.options().get("mft-digits"), ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TPC]) { - specs.push_back(o2::tpc::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::tpc::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TRD]) { - specs.push_back(o2::trd::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::trd::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TOF]) { - specs.push_back(o2::tof::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::tof::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FT0]) { - specs.push_back(o2::ft0::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::ft0::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FV0]) { - specs.push_back(o2::fv0::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::fv0::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FDD]) { - specs.push_back(o2::fdd::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::fdd::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MID]) { - specs.push_back(o2::mid::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::mid::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MCH]) { - specs.push_back(o2::mch::getEntropyDecoderSpec(verbosity, "mch-entropy-decoder", ctfInput.subspec)); + addSpecs(o2::mch::getEntropyDecoderSpec(verbosity, "mch-entropy-decoder", ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::EMC]) { - specs.push_back(o2::emcal::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.decSSpecEMC)); + addSpecs(o2::emcal::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.decSSpecEMC, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::PHS]) { - specs.push_back(o2::phos::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::phos::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::CPV]) { - specs.push_back(o2::cpv::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::cpv::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::ZDC]) { - specs.push_back(o2::zdc::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::zdc::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::HMP]) { - specs.push_back(o2::hmpid::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::hmpid::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::CTP]) { - specs.push_back(o2::ctp::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::ctp::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); + } + + bool combine = configcontext.options().get("combine-devices"); + if (!combine) { + for (auto& decSpecs : decSpecsV) { + for (auto& s : decSpecs) { + specs.push_back(s); + } + } + } else { + std::vector remaining; + if (decSpecsV.size() && decSpecsV[0].size()) { + specs.push_back(specCombiner("EntropyDecoders", decSpecsV[0], remaining)); // processing w/o pipelining + } + bool updatePipelines = false; + for (size_t i = 1; i < decSpecsV.size(); i++) { // add pipelined processes separately, consider combining them to separate groups (need to have modify argument of pipeline option) + if (decSpecsV[i].size() > 1) { + specs.push_back(specCombiner(fmt::format("EntropyDecodersP{}", i + 1), decSpecsV[i], remaining)); // processing pipelining multiplicity i+1 + updatePipelines = true; + pipes += fmt::format(",EntropyDecodersP{}:{}", i + 1, i + 1); + } else { + for (auto& s : decSpecsV[i]) { + specs.push_back(s); + } + } + } + for (auto& s : remaining) { + specs.push_back(s); + } + if (updatePipelines) { + configcontext.options().override("pipeline", pipes); + } } return std::move(specs); diff --git a/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx b/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx index 2757192727521..77dbbd80bc1a7 100644 --- a/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx @@ -20,6 +20,7 @@ #include "CTFWorkflow/CTFWriterSpec.h" #include "DetectorsCommonDataFormats/DetID.h" #include "CommonUtils/ConfigurableParam.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -35,6 +36,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"ctf-writer-verbosity", VariantType::Int, 0, {"verbosity level (0: summary per detector, 1: summary per block"}}); options.push_back(ConfigParamSpec{"report-data-size-interval", VariantType::Int, 200, {"report sizes per detector for every N-th timeframe"}}); options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}); + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -51,7 +53,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { DetID::mask_t dets = 0; o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - std::string outType{}; // RS FIXME once global/local options clash is solved, --output-type will become device option + o2::ctf::CTFWriterInp inp; if (!configcontext.helpOnCommandLine()) { dets.set(); // by default read all auto mskOnly = DetID::getMask(configcontext.options().get("onlyDet")); @@ -64,10 +66,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (dets.none()) { throw std::invalid_argument("Invalid workflow: no detectors found"); } - outType = configcontext.options().get("output-type"); + inp.detMask = dets; + inp.outType = configcontext.options().get("output-type"); } - WorkflowSpec specs{o2::ctf::getCTFWriterSpec(dets, outType, - configcontext.options().get("ctf-writer-verbosity"), - configcontext.options().get("report-data-size-interval"))}; + inp.verbosity = configcontext.options().get("ctf-writer-verbosity"); + inp.reportInterval = configcontext.options().get("report-data-size-interval"); + inp.doITSStaggering = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + inp.doMFTStaggering = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); + + WorkflowSpec specs{o2::ctf::getCTFWriterSpec(inp)}; return std::move(specs); } diff --git a/Detectors/CTP/macro/CMakeLists.txt b/Detectors/CTP/macro/CMakeLists.txt index 14c8e0a1ee070..8608c1a8b7846 100644 --- a/Detectors/CTP/macro/CMakeLists.txt +++ b/Detectors/CTP/macro/CMakeLists.txt @@ -33,6 +33,10 @@ o2_add_test_root_macro(CheckAOD2CTPDigits.C PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP O2::CCDB LABELS ctp) +o2_add_test_root_macro(CheckAOD2CTPDigitsII.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) o2_add_test_root_macro(TestConfig.C PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP O2::CCDB @@ -41,3 +45,43 @@ o2_add_test_root_macro(dumpCTPRO.C PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP O2::CCDB LABELS ctp) +o2_add_test_root_macro(GetAndSave.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(ReadCTPRunScalersFromFile.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(GetScalersForRun.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(PlotPbLumi.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(PlotOrbit.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(TestFetcher.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(CreateBKForRun.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(CheckCTPConfig.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(GetRates.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(TestGetRates.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) \ No newline at end of file diff --git a/Detectors/CTP/macro/CheckAOD2CTPDigitsII.C b/Detectors/CTP/macro/CheckAOD2CTPDigitsII.C new file mode 100644 index 0000000000000..14e60194bdd82 --- /dev/null +++ b/Detectors/CTP/macro/CheckAOD2CTPDigitsII.C @@ -0,0 +1,183 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file checkAOD2CTPDigits.C +/// \brief create CTP config, test it and add to database +/// \author Roman Lietava + +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "TFile.h" +#include "TTree.h" +#include +#include +#include +#include "TKey.h" +#include "TTreeReader.h" +#include "TTreeReaderValue.h" +#include "CommonDataFormat/InteractionRecord.h" +#endif +// To produce CTP digits: +// o2-raw-tf-reader-workflow -b --onlyDet CTP,FT0 --input-data list.txt | o2-ctp-reco-workflow -b --use-verbose-mode +// To produce AOD: +// WORKFLOW_PARAMETERS="AOD" WORKFLOW_DETECTORS="CTP,FT0,FV0" IGNORE_EXISTING_SHMFILES=1 $O2_ROOT/prodtests/full-system-test/run-workflow-on-inputlist.sh TF list.txt +using namespace o2::ctp; +int CheckAOD2CTPDigits(bool files = 1) +{ + if (files == 0) { + return 0; + } + int fRunNumber; + ULong64_t fGlobalBC; + ULong64_t fTriggerMask; + ULong64_t fInputMask; + std::unique_ptr file(TFile::Open("AO2D.root")); + if (file == nullptr) { + std::cout << "Can not open file AO2D.root" << std::endl; + return 1; + } + file->ls(); + TIter keyList(file->GetListOfKeys()); + TKey* key; + TTree* tree; + std::map bc2classmask; + std::map bc2inputmask; + // Find aod trigger info + int i = 0; + while ((key = (TKey*)keyList())) { + std::cout << "loop:" << i << " " << key->GetName() << std::endl; + i++; + std::string name = key->GetName(); + if (name.find("metaData") != std::string::npos) { + std::cout << "Skipping:" << name << std::endl; + continue; + } + tree = (TTree*)file->Get(Form("%s/O2bc_001", key->GetName())); + if (tree != 0) { + std::cout << "found O2bc" << std::endl; + tree->SetBranchAddress("fRunNumber", &fRunNumber); + tree->SetBranchAddress("fGlobalBC", &fGlobalBC); + tree->SetBranchAddress("fTriggerMask", &fTriggerMask); + tree->SetBranchAddress("fInputMask", &fInputMask); + std::cout << "# of entries:" << tree->GetEntries() << std::endl; + int NN = tree->GetEntries(); + int Nloop = NN; + for (int n{0}; n < Nloop; ++n) { + tree->GetEvent(n); + if (fTriggerMask) { + bc2classmask[fGlobalBC] = fTriggerMask; + // std::cout << std::dec << n << " Run:" << fRunNumber << " GBC:" << std::hex << fGlobalBC << " TM:0x" << std::hex << fTriggerMask << " count:" << bc2classmask.count(fGlobalBC) << std::endl; + } + if (fInputMask) { + bc2inputmask[fGlobalBC] = fInputMask; + } + if (1) { + if (fInputMask || fTriggerMask) { + auto ir = o2::InteractionRecord::long2IR(fGlobalBC); + // auto bcc = ir.orbit; + // if(fTriggerMask) std::cout << "===>"; + // std::cout << std::hex << ir.orbit << " " << ir.bc << " " << fInputMask << " " << fTriggerMask << std::dec << " (" << ir.orbit << " " << ir.bc << ")" << std::endl; + } + } + } + } else { + std::cout << "return 1" << std::endl; + return 1; + } + } + // return 0; + // Read CTP digits and check if every class mask in digits is in AOD + // CTP digits + TFile* fileDigits = TFile::Open("ctpdigitsWOD.root"); + // + fileDigits->ls(); + o2::ctp::CTPDigit* dig = new o2::ctp::CTPDigit; + // + // tree->Print(); + TTreeReader reader("o2sim", fileDigits); + TTreeReaderValue> ctpdigs(reader, "CTPDigits"); + // TTreeReaderArray ctpdigs(reader,"CTPDigits"); + bool firstE = true; + // + std::cout << "Doing ctpdigits" << std::endl; + std::map bc2classmaskD; + std::map bc2inputmaskD; + std::bitset<48> tvxmask; + tvxmask.set(2); + int nnotfinp = 0; + int nokinp = 0; + int nnotf = 0; + int nok = 0; + while (reader.Next()) { + if (ctpdigs.GetSetupStatus() < 0) { + std::cout << "Error:" << std::dec << ctpdigs.GetSetupStatus() << " for:" << ctpdigs.GetBranchName() << std::endl; + return 1; + } + // std::cout << "size:" << std::dec << ctpdigs.GetSize() << std::endl; + // std::cout << "size:" << std::dec << ctpdigs->size() << std::endl; + for (auto const& dig : *ctpdigs) { + ULong64_t gbc = dig.intRecord.toLong(); + if (dig.CTPClassMask.count()) { + bc2classmaskD[gbc] = dig.CTPClassMask.to_ullong(); + // int del = 280+17; + // auto it = bc2classmask.find (gbc); + // if(it != bc2classmask.end()) { + if (bc2classmask.count(gbc)) { + // bc2classmask.erase(gbc); + nok++; + // std::cout << std::hex << gbc << " tc aod clsmask:" << bc2classmask[gbc] << " " << dig.CTPClassMask.to_ullong() << std::endl; + // dig.printStream(std::cout); + } else { + std::cout << std::dec << dig.intRecord.orbit << " " << dig.intRecord.bc << " " << std::hex << dig.intRecord.toLong() << " not found " << bc2classmask.count(gbc) << std::endl; + nnotf++; + } + } + if (dig.CTPInputMask.count()) { + bc2inputmaskD[gbc] = dig.CTPInputMask.to_ullong(); + // auto it = bc2inputmask.find (gbc); + // if(it != bc2inputmask.end()) { + if (bc2inputmask.count(gbc)) { + // bc2inputmask.erase(gbc); + nokinp++; + // std::cout << std::hex << gbc << " ir aod inpmask:" << bc2inputmask[gbc] << " " << dig.CTPInputMask.to_ullong() << std::endl; + } else { + std::cout << std::dec << dig.intRecord.orbit << " " << dig.intRecord.bc << " " << std::hex << dig.intRecord.toLong() << " not found " << bc2inputmask.count(gbc) << std::endl; + nnotfinp++; + } + } + } + } + std::cout << "TClasses ===> nok:" << nok << " NOT found in digits:" << nnotf << " left:" << bc2classmask.size() << std::endl; + std::cout << "Inputs ===> nok:" << nokinp << " NOT found in digits:" << nnotfinp << " left:" << bc2inputmask.size() << std::endl; + nok = 0; + nokinp = 0; + nnotf = 0; + nnotfinp = 0; + for (auto const& tm : bc2classmask) { + if (bc2classmaskD.count(tm.first)) { + nok++; + } else { + nnotf++; + } + } + for (auto const& tm : bc2inputmask) { + if (bc2inputmaskD.count(tm.first)) { + nokinp++; + } else { + nnotfinp++; + } + } + + std::cout << "TClasses ===> nok:" << nok << " NOT found in aod:" << nnotf << " left:" << bc2classmaskD.size() << std::endl; + std::cout << "Inputs ===> nok:" << nokinp << " NOT found in aod:" << nnotfinp << " left:" << bc2inputmaskD.size() << std::endl; + return 0; +} diff --git a/Detectors/CTP/macro/CheckCTPConfig.C b/Detectors/CTP/macro/CheckCTPConfig.C new file mode 100644 index 0000000000000..24a5e354f3fcd --- /dev/null +++ b/Detectors/CTP/macro/CheckCTPConfig.C @@ -0,0 +1,58 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CreateCTPConfig.C +/// \brief create CTP config, test it and add to database +/// \author Roman Lietava + +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Configuration.h" +#include +#include +#include +#endif +using namespace o2::ctp; +int CheckCTPConfig(std::string cfgRun3str = "/home/rl/backup24/runs/559781.rcfg2", int writeToFile = 0) +{ + // + // run3 config + // + if (cfgRun3str.find(".rcfg") == std::string::npos) { + std::cout << "No file name:" << cfgRun3str << std::endl; + return 1; + } else { + std::string filename = cfgRun3str; + std::ifstream in; + in.open(filename); + if (!in) { + std::cout << "Can not open file:" << filename << std::endl; + return 2; + } + std::stringstream buffer; + buffer << in.rdbuf(); + cfgRun3str = buffer.str(); + } + // + CTPConfiguration ctpcfg; + int ret = ctpcfg.loadConfigurationRun3(cfgRun3str); + ctpcfg.printStream(std::cout); + std::cout << "CTP config done" << std::endl; + // ctpcfg.checkConfigConsistency(); + auto ctpclasses = ctpcfg.getCTPClasses(); + for (auto const& cls : ctpclasses) { + std::cout << cls.descriptor->name << ":" << std::hex << cls.descriptor->getInputsMask() << std::endl; + } + return ret; +} diff --git a/Detectors/CTP/macro/CreateBKForRun.C b/Detectors/CTP/macro/CreateBKForRun.C new file mode 100644 index 0000000000000..06ef9eac5f900 --- /dev/null +++ b/Detectors/CTP/macro/CreateBKForRun.C @@ -0,0 +1,74 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#endif +using namespace o2::ctp; + +void CreateBKForRun() +{ + std::vector runs = {558124, 558126, 558215, 558217, 558221, 558244, 558247}; + std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; + std::string mCCDBPathCTPConfig = "CTP/Config/Config"; + // + std::string filename = "BKcounters.txt"; + std::ofstream outfile(filename); + if (!outfile) { + Error("", "Failed to open file %s", filename.c_str()); + return; + } + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + for (auto const& runNumber : runs) { + auto soreor = ccdbMgr.getRunDuration(runNumber); + uint64_t timeStamp = (soreor.second - soreor.first) / 2 + soreor.first; + std::cout << runNumber << " Timestamp:" << timeStamp << std::endl; + // + std::string srun = std::to_string(runNumber); + std::map metadata; + metadata["runNumber"] = srun; + auto ctpscalers = ccdbMgr.getSpecific(mCCDBPathCTPScalers, timeStamp, metadata); + if (ctpscalers == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timeStamp; + } + auto ctpcfg = ccdbMgr.getSpecific(mCCDBPathCTPConfig, timeStamp, metadata); + if (ctpcfg == nullptr) { + LOG(info) << "CTPRunConfig not in database, timestamp:" << timeStamp; + } + // + ctpscalers->convertRawToO2(); + std::vector& ctpcls = ctpcfg->getCTPClasses(); + std::vector clslist = ctpcfg->getTriggerClassList(); + auto times = ctpscalers->getTimeLimit(); + for (size_t i = 0; i < clslist.size(); i++) { + // std::cout << i << " " << ctpcls[i].name ; + std::array cnts = ctpscalers->getIntegralForClass(i); + if (clslist[i] != (int)cnts[0]) { + LOG(fatal) << "cls list incompatible with counters"; + } + std::cout << std::setw(21) << ctpcls[cnts[0]].name; + outfile << runNumber << ", " << ctpcls[i].name << ", " << std::get<1>(times) / 1000; + for (int j = 1; j < 7; j++) { + // std::cout << std::setw(21) << " " << cnts[j]; + std::cout << ", " << cnts[j]; + outfile << ", " << cnts[j]; + } + std::cout << std::endl; + outfile << std::endl; + } + } + // ctpscalers->printFromZero(std::cout); + outfile.close(); +} diff --git a/Detectors/CTP/macro/CreateCTPConfig.C b/Detectors/CTP/macro/CreateCTPConfig.C index 1c47cf1e75cac..a3e4b0c8c35f1 100644 --- a/Detectors/CTP/macro/CreateCTPConfig.C +++ b/Detectors/CTP/macro/CreateCTPConfig.C @@ -19,6 +19,7 @@ #include "CCDB/CcdbApi.h" #include "CCDB/BasicCCDBManager.h" #include "DataFormatsCTP/Configuration.h" +#include "CTPWorkflowScalers/ctpCCDBManager.h" #include #include #include @@ -77,11 +78,14 @@ ferst 1 \n\ ctpcfg.loadConfigurationRun3(cfgRun3str); ctpcfg.printStream(std::cout); std::cout << "CTP config done" << std::endl; - CTPRunManager* man = new CTPRunManager; - man->setCCDBHost("http://ccdb-test.cern.ch:8080"); - man->saveRunConfigToCCDB(&ctpcfg, 1665784953); - // uint64_t classmask = ctpcfg.getClassMaskForInputMask(0x4); - // std::cout << "classmask:" << std::hex << classmask << std::dec << std::endl; + ctpcfg.checkConfigConsistency(); + if (0) { + o2::ctp::ctpCCDBManager* man = new ctpCCDBManager; + man->setCCDBHost("http://ccdb-test.cern.ch:8080"); + man->saveRunConfigToCCDB(&ctpcfg, 1665784953); + // uint64_t classmask = ctpcfg.getClassMaskForInputMask(0x4); + // std::cout << "classmask:" << std::hex << classmask << std::dec << std::endl; + } if (writeToFile == 1) { std::unique_ptr myFile(TFile::Open("CTPConfig.root", "RECREATE")); myFile->WriteObject(&ctpcfg, "CTPConfig"); diff --git a/Detectors/CTP/macro/GetAndSave.C b/Detectors/CTP/macro/GetAndSave.C new file mode 100644 index 0000000000000..ff70a3055c957 --- /dev/null +++ b/Detectors/CTP/macro/GetAndSave.C @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TestCTPScalers.C +/// \brief create CTP scalers, test it and add to database +/// \author Roman Lietava +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Scalers.h" +#include "DataFormatsCTP/Configuration.h" +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "TFile.h" +#include "TString.h" +#include +#include +#include +#endif +using namespace o2::ctp; +void GetAndSave(std::string ccdbHost = "http://ccdb-test.cern.ch:8080") +{ + std::string CCDBPathCTPScalers = "CTP/Calib/Scalers"; + // std::vector runs = {"518541","518543","518546","518547"}; + // std::vector timestamps = {1655116302316,1655118513690,1655121997478,1655123792911}; + std::vector runs = {"519903", "519904", "519905", "519906"}; + std::vector timestamps = {1656658674161, 1656660737184, 1656667772462, 1656669421115}; + // std::vector runs = {"518543"}; + // std::vector timestamps = {1655118513690}; + int i = 0; + ctpCCDBManager mng; + // mng.setCCDBHost(ccdbHost); + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(ccdbHost); + for (auto const& run : runs) { + CTPConfiguration ctpcfg; + CTPRunScalers scl; + std::map metadata; // can be empty + metadata["runNumber"] = run; + CTPRunScalers* ctpscalers = mgr.getSpecific(CCDBPathCTPScalers, timestamps[i], metadata); + if (ctpscalers == nullptr) { + std::cout << run << " CTPRunScalers not in database, timestamp:" << timestamps[i] << std::endl; + } else { + // ctpscalers->printStream(std::cout); + std::string name = run + ".root"; + TFile* myFile = TFile::Open(name.c_str(), "RECREATE"); + myFile->WriteObject(ctpscalers, "CTPRunScalers"); + // myFile->Write(); + std::cout << run << " ok" << std::endl; + } + i++; + } +} diff --git a/Detectors/CTP/macro/GetRates.C b/Detectors/CTP/macro/GetRates.C new file mode 100644 index 0000000000000..8894d7935b99e --- /dev/null +++ b/Detectors/CTP/macro/GetRates.C @@ -0,0 +1,55 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "Framework/Logger.h" +#endif +using namespace o2::ctp; + +void GetRates(int run = 559617) +{ + uint64_t inputmaskCum = 0, classmackCum = 0; + int ntrigSel = 0; + + auto& cmb = o2::ccdb::BasicCCDBManager::instance(); + auto ctpcfg = cmb.getSpecificForRun("CTP/Config/Config", run); + if (!ctpcfg) { + LOGP(error, "Can not get config for run {}", run); + return; + } + CTPConfiguration ctpconfig; + ctpconfig.loadConfigurationRun3(ctpcfg->getConfigString()); + ctpconfig.printStream(std::cout); + auto& triggerclasses = ctpconfig.getCTPClasses(); + LOGP(info, "Found {} trigger classes", triggerclasses.size()); + int indexInList = 0; + for (const auto& trgclass : triggerclasses) { + uint64_t inputmask = 0; + if (trgclass.descriptor != nullptr) { + inputmask = trgclass.descriptor->getInputsMask(); + // LOGP(info, "inputmask: {:#x}", inputmask); + } + trgclass.printStream(std::cout); + // std::cout << indexInList << ": " << trgclass.name << ", input mask 0x" << std::hex << inputmask << ", class mask 0x" << trgclass.classMask << std::dec << std::endl; + indexInList++; + if (trgclass.cluster->getClusterDetNames().find("TRD") != std::string::npos || trgclass.cluster->getClusterDetNames().find("trd") != std::string::npos) { + LOGP(info, "Found TRD trigger cluster, class mask: {:#x}, input mask: {:#x}", trgclass.classMask, inputmask); + inputmaskCum |= inputmask; + classmackCum |= trgclass.classMask; + ntrigSel++; + } + } + + LOGP(info, "Found {} triggers with TRD: classMasks: {:#x} inputMasks: {:#x}", ntrigSel, classmackCum, inputmaskCum); +} diff --git a/Detectors/CTP/macro/GetScalers.C b/Detectors/CTP/macro/GetScalers.C index 31feaf0b29c92..1f104850c8c39 100644 --- a/Detectors/CTP/macro/GetScalers.C +++ b/Detectors/CTP/macro/GetScalers.C @@ -12,6 +12,7 @@ /// \file TestCTPScalers.C /// \brief create CTP scalers, test it and add to database /// \author Roman Lietava +// root -b -q "GetScalers.C(\"519499\", 1656286373953)" #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -19,6 +20,8 @@ #include "CCDB/BasicCCDBManager.h" #include "DataFormatsCTP/Scalers.h" #include "DataFormatsCTP/Configuration.h" +// #include "BookkeepingApi/BkpClientFactory.h" +#include "CTPWorkflowScalers/ctpCCDBManager.h" #include #include #include @@ -33,21 +36,30 @@ void GetScalers(std::string srun, long time, std::string ccdbHost = "http://ccdb // std::cout << stol(hd["SOR"]) << "\n"; CTPConfiguration ctpcfg; CTPRunScalers scl; - CTPRunManager mng; + o2::ctp::ctpCCDBManager mng; mng.setCCDBHost(ccdbHost); bool ok; + // ctpcfg = mng.getConfigFromCCDB(time, srun); + // ctpcfg.printStream(std::cout); + // return; scl = mng.getScalersFromCCDB(time, srun, ok); if (ok == 1) { + scl.printStream(std::cout); scl.convertRawToO2(); - scl.printO2(std::cout); - scl.printFromZero(std::cout); - scl.printIntegrals(); - ctpcfg = mng.getConfigFromCCDB(time, srun); - scl.printRates(); - // std::vector clsses; - // clsses = ctpcfg.getTriggerClassList(); - // std::cout << clsses.size() << std::endl; - // for(auto const& i : clsses) std::cout << i << std::endl; + // scl.printO2(std::cout); + // scl.printFromZero(std::cout); + // scl.printIntegrals(); + // scl.printRates(); + std::cout << "TVX,TSC,TCE,ZNC:" << std::endl; + scl.printInputRateAndIntegral(3); + scl.printInputRateAndIntegral(4); + scl.printInputRateAndIntegral(5); + scl.printInputRateAndIntegral(26); + std::cout << " TVX,TVX&TCE,TVX&TSC,TVX&VCH:" << std::endl; + scl.printClassBRateAndIntegral(3); + scl.printClassBRateAndIntegral(4); + scl.printClassBRateAndIntegral(5); + scl.printClassBRateAndIntegral(6); } else { std::cout << "Can not find run, please, check parameters" << std::endl; } diff --git a/Detectors/CTP/macro/GetScalersForRun.C b/Detectors/CTP/macro/GetScalersForRun.C new file mode 100644 index 0000000000000..a96d79d569213 --- /dev/null +++ b/Detectors/CTP/macro/GetScalersForRun.C @@ -0,0 +1,141 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include "Common/CCDB/ctpRateFetcher.h" +#endif +using namespace o2::ctp; + +void GetScalersForRun(int runNumber = 0, int fillN = 0, bool test = 1) +{ + if (test == 0) { + return; + } + std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; + std::string mCCDBPathCTPConfig = "CTP/Config/Config"; + + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + auto soreor = ccdbMgr.getRunDuration(runNumber); + uint64_t timeStamp = (soreor.second - soreor.first) / 2 + soreor.first; + std::cout << "Timestamp:" << timeStamp << std::endl; + // + std::string sfill = std::to_string(fillN); + std::map metadata; + metadata["fillNumber"] = sfill; + auto lhcifdata = ccdbMgr.getSpecific("GLO/Config/GRPLHCIF", timeStamp); + auto bfilling = lhcifdata->getBunchFilling(); + std::vector bcs = bfilling.getFilledBCs(); + std::cout << "Number of interacting bc:" << bcs.size() << std::endl; + // + std::string srun = std::to_string(runNumber); + metadata.clear(); // can be empty + metadata["runNumber"] = srun; + auto ctpscalers = ccdbMgr.getSpecific(mCCDBPathCTPScalers, timeStamp, metadata); + if (ctpscalers == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timeStamp; + } + auto ctpcfg = ccdbMgr.getSpecific(mCCDBPathCTPConfig, timeStamp, metadata); + if (ctpcfg == nullptr) { + LOG(info) << "CTPRunConfig not in database, timestamp:" << timeStamp; + } + std::cout << "all good" << std::endl; + ctpscalers->convertRawToO2(); + std::vector ctpcls = ctpcfg->getCTPClasses(); + // std::vector clslist = ctpcfg->getTriggerClassList(); + std::vector clslist = ctpscalers->getClassIndexes(); + std::map clsIndexToScaler; + std::cout << "Classes:"; + int i = 0; + for (auto const& cls : clslist) { + std::cout << cls << " "; + clsIndexToScaler[cls] = i; + i++; + } + std::cout << std::endl; + int tsc = 255; + int tce = 255; + int vch = 255; + int iznc = 255; + for (auto const& cls : ctpcls) { + if (cls.name.find("CMTVXTSC-B-NOPF-CRU") != std::string::npos) { + tsc = cls.getIndex(); + std::cout << cls.name << ":" << tsc << std::endl; + } + if (cls.name.find("CMTVXTCE-B-NOPF-CRU") != std::string::npos) { + tce = cls.getIndex(); + std::cout << cls.name << ":" << tce << std::endl; + } + if (cls.name.find("CMTVXVCH-B-NOPF-CRU") != std::string::npos) { + vch = cls.getIndex(); + std::cout << cls.name << ":" << vch << std::endl; + } + // if (cls.name.find("C1ZNC-B-NOPF-CRU") != std::string::npos) { + if (cls.name.find("C1ZNC-B-NOPF") != std::string::npos) { + iznc = cls.getIndex(); + std::cout << cls.name << ":" << iznc << std::endl; + } + } + std::vector recs = ctpscalers->getScalerRecordO2(); + if (recs[0].scalersInps.size() == 48) { + std::cout << "ZNC:"; + int inp = 26; + double_t nbc = bcs.size(); + double_t frev = 11245; + double_t sigmaratio = 28.; + double_t time0 = recs[0].epochTime; + double_t timeL = recs[recs.size() - 1].epochTime; + double_t Trun = timeL - time0; + double_t integral = recs[recs.size() - 1].scalersInps[inp - 1] - recs[0].scalersInps[inp - 1]; + double_t rate = integral / Trun; + double_t rat = integral / Trun / nbc / frev; + double_t mu = -TMath::Log(1 - rat); + double_t pp = 1 - mu / (TMath::Exp(mu) - 1); + double_t ratepp = mu * nbc * frev; + double_t integralpp = ratepp * Trun; + std::cout << "Rate:" << rate / sigmaratio << " Integral:" << integral << " mu:" << mu << " Pileup prob:" << pp; + std::cout << " Integralpp:" << integralpp << " Ratepp:" << ratepp / sigmaratio << std::endl; + // ctpscalers->printInputRateAndIntegral(26); + } else { + std::cout << "Inputs not available" << std::endl; + } + // + if (tsc != 255) { + std::cout << "TSC:"; + ctpscalers->printClassBRateAndIntegral(clsIndexToScaler[tsc] + 1); + } + if (tce != 255) { + std::cout << "TCE:"; + ctpscalers->printClassBRateAndIntegral(clsIndexToScaler[tce] + 1); + } + // std::cout << "TCE input:" << ctpscalers->printInputRateAndIntegral(5) << std::endl;; + if (vch != 255) { + std::cout << "VCH:"; + ctpscalers->printClassBRateAndIntegral(clsIndexToScaler[vch] + 1); + } + if (iznc != 255) { + std::cout << "ZNC class:"; + // uint64_t integral = recs[recs.size() - 1].scalers[iznc].l1After - recs[0].scalers[iznc].l1After; + auto zncrate = ctpscalers->getRateGivenT(timeStamp * 1.e-3, iznc, 6); + std::cout << "ZNC class rate:" << zncrate.first / 28. << std::endl; + } else { + std::cout << "ZNC class not available, rate from input:" << std::endl; + } + auto zncinprate = ctpscalers->getRateGivenT((timeStamp + 100) * 1.e-3, 25, 7); + std::cout << "ZNC inp rate:" << zncinprate.first / 28. << " " << zncinprate.second / 28. << std::endl; + + // ctpRateFetcher ctprate; + // auto fetcherRate = ctprate.fetch(&ccdbMgr, timeStamp, runNumber, "ZNChadronic"); + // std::cout << "Result from the fetcher (ZNC): " << fetcherRate << std::endl; +} diff --git a/Detectors/CTP/macro/PlotOrbit.C b/Detectors/CTP/macro/PlotOrbit.C new file mode 100644 index 0000000000000..9f70341ecc0da --- /dev/null +++ b/Detectors/CTP/macro/PlotOrbit.C @@ -0,0 +1,100 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TestCTPScalers.C +/// \brief create CTP scalers, test it and add to database +/// \author Roman Lietava +// root -b -q "GetScalers.C(\"519499\", 1656286373953)" +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Scalers.h" +#include "DataFormatsCTP/Configuration.h" +#include +#include +#include +#endif +//"http://ccdb-test.cern.ch:8080" +using namespace o2::ctp; +void PlotOrbit(int runNumber) +{ // + std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + // Timestamp + auto soreor = ccdbMgr.getRunDuration(runNumber); + // uint64_t timeStamp = (soreor.second - soreor.first) / 2 + soreor.first; + uint64_t timeStamp = soreor.first + 60 * 1000; + std::cout << "Timestamp:" << timeStamp << std::endl; + // + // Scalers + std::string srun = std::to_string(runNumber); + std::map metadata; + metadata["runNumber"] = srun; + // ccdbMgr.setURL("http://ccdb-test.cern.ch:8080"); + auto scl = ccdbMgr.getSpecific(mCCDBPathCTPScalers, timeStamp, metadata); + if (scl == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timeStamp; + return; + } + scl->convertRawToO2(); + // return ; + std::vector recs = scl->getScalerRecordRaw(); + std::cout << "raw rec size:" << recs.size() << std::endl; + // + // + // Anal + // + // Times + int64_t time0 = recs[0].epochTime; + int64_t timeL = recs[recs.size() - 1].epochTime; + double_t Trun = timeL - time0; + // double_t orbit0 = recs[0].intRecord.orbit; + int64_t orbit0 = scl->getOrbitLimitFromRaw().first; + int64_t orbitL = scl->getOrbitLimitFromRaw().second; + int n = recs.size() - 1; + std::cout << " Run duration:" << Trun << " Scalers size:" << n + 1 << std::endl; + std::cout << "Orbit0:" << orbit0 << " orbitL:" << orbitL << std::endl; + return; + Double_t x[n], orbit[n]; + // Double_t tcetsctoznc[n], tcetoznc[n], vchtoznc[n]; + for (int i = 0; i < n; i++) { + // x[i] = i; + x[i] = recs[i + 1].epochTime - time0; + orbit[i] = recs[i + 1].intRecord.orbit - orbit0; + } + // + gStyle->SetMarkerSize(0.5); + TGraph* gr1 = new TGraph(n, x, orbit); + // TGraph* gr2 = new TGraph(n, x, tcetsctoznc); + // TGraph* gr3 = new TGraph(n, x, tcetoznc); + // TGraph* gr4 = new TGraph(n, x, vchtoznc); + gr1->SetMarkerStyle(20); + // gr2->SetMarkerStyle(21); + // gr3->SetMarkerStyle(23); + // gr4->SetMarkerStyle(23); + std::string title = "Orbit vs EpochTime for run " + srun + " ;EpochTime[s]; Orbit"; + gr1->SetTitle(title.c_str()); + // gr2->SetTitle("R=(TSC+TCE)*TVTX*B*28/ZNC; time[sec]; R"); + // gr2->GetHistogram()->SetMaximum(1.1); + // gr2->GetHistogram()->SetMinimum(0.9); + // gr3->SetTitle("R=(TCE)*TVTX*B*28/ZNC; time[sec]; R"); + // gr3->GetHistogram()->SetMaximum(0.6); + // gr3->GetHistogram()->SetMinimum(0.4); + // gr4->SetTitle("R=(VCH)*TVTX*B*28/ZNC; time[sec]; R"); + // gr4->GetHistogram()->SetMaximum(0.6); + // gr4->GetHistogram()->SetMinimum(0.4); + TCanvas* c1 = new TCanvas("c1", srun.c_str(), 200, 10, 800, 500); + gr1->Draw("AP"); + std::cout << "epoch0:" << time0 << " epochL:" << timeL << " T:" << Trun << std::endl; + std::cout << "orbit0:" << orbit0 << " orbitL:" << orbitL << " T:" << (orbitL - orbit0) * 88e-6 << std::endl; +} diff --git a/Detectors/CTP/macro/PlotPbLumi.C b/Detectors/CTP/macro/PlotPbLumi.C new file mode 100644 index 0000000000000..1022b6e30d0be --- /dev/null +++ b/Detectors/CTP/macro/PlotPbLumi.C @@ -0,0 +1,280 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file PlotPbLumi.C +/// \brief create CTP scalers, test it and add to database +/// \author Roman Lietava +// root "PLotPbLumi.C(519499)" +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Scalers.h" +#include "DataFormatsCTP/Configuration.h" +#include "DataFormatsParameters/GRPLHCIFData.h" +#include "TGraph.h" +#include "TMath.h" +#include "TCanvas.h" +#include "TStyle.h" +#include +#include +#include +#endif +using namespace o2::ctp; +// +// sum = 0: TCE and TSC separatelly otherwise TCE and (TCE+TSC) +// qc = 0: takes scalers from CCDB (available only for finished runs) otherwise from QCCDB (available for active runs) +// t0-tlast: window in seconds counted from beginning of run +// +void PlotPbLumi(int runNumber = 572073, bool sum = 1, double cut = 0, bool qc = 0, Double_t t0 = 0., Double_t tlast = 0.) +{ // + // PLots in one canvas + // znc rate/28 + // R = (TCE+TSC)*TVX*B*/ZNC*28 + // R = TCE*TVX*B/ZNC*28 + // R = VCH*TVX*B/ZNC*28 + std::string ccdbHost = "http://alice-ccdb.cern.ch"; + std::string mCCDBPathCTPScalers = "/CTP/Calib/Scalers"; + std::string mCCDBPathCTPScalersQC = "qc/CTP/Scalers"; + std::string mCCDBPathCTPConfig = "CTP/Config/Config"; + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + // Timestamp + auto soreor = ccdbMgr.getRunDuration(runNumber); + uint64_t timeStamp = (soreor.second - soreor.first) / 2 + soreor.first; + std::cout << "Timestamp:" << timeStamp << std::endl; + // Filling + auto lhcifdata = ccdbMgr.getForRun("GLO/Config/GRPLHCIF", runNumber); + // auto lhcifdata = ccdbMgr.getSpecific("GLO/Config/GRPLHCIF", timeStamp, metadata); + if (!lhcifdata) { + throw std::runtime_error("No GRPLHCIFData for run " + std::to_string(runNumber)); + } + auto bfilling = lhcifdata->getBunchFilling(); + std::vector bcs = bfilling.getFilledBCs(); + int nbc = bcs.size(); + std::cout << "Number of interacting bc:" << nbc << std::endl; + // Scalers + std::string srun = std::to_string(runNumber); + std::map metadata; + metadata["runNumber"] = srun; + CTPRunScalers* scl = nullptr; + if (qc) { + ccdbMgr.setURL("http://ali-qcdb-gpn.cern.ch:8083"); + scl = ccdbMgr.getSpecific(mCCDBPathCTPScalersQC, timeStamp, metadata); + } else { + scl = ccdbMgr.getSpecific(mCCDBPathCTPScalers, timeStamp, metadata); + } + if (scl == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timeStamp; + return; + } + scl->convertRawToO2(); + std::vector recs = scl->getScalerRecordO2(); + // + // CTPConfiguration ctpcfg; + ccdbMgr.setURL("http://alice-ccdb.cern.ch"); + auto ctpcfg = ccdbMgr.getSpecific(mCCDBPathCTPConfig, timeStamp, metadata); + if (ctpcfg == nullptr) { + LOG(info) << "CTPRunConfig not in database, timestamp:" << timeStamp; + return; + } + std::vector clslist = ctpcfg->getTriggerClassList(); + // std::vector clslist = scl->getClassIndexes(); + std::map clsIndexToScaler; + std::cout << "Classes:"; + int i = 0; + for (auto const& cls : clslist) { + std::cout << cls << " "; + clsIndexToScaler[cls] = i; + i++; + } + std::cout << std::endl; + std::vector ctpcls = ctpcfg->getCTPClasses(); + int tsc = 255; + int tce = 255; + int vch = 255; + int zncclsi = 255; + for (auto const& cls : ctpcls) { + if (cls.name.find("CMTVXTSC-B-NOPF") != std::string::npos && tsc == 255) { + int itsc = cls.getIndex(); + tsc = clsIndexToScaler[itsc]; + // tsc = scl->getScalerIndexForClass(itsc); + std::cout << cls.name << ":" << tsc << ":" << itsc << std::endl; + } + if (cls.name.find("CMTVXTCE-B-NOPF-CRU") != std::string::npos) { + int itce = cls.getIndex(); + tce = clsIndexToScaler[itce]; + // tce = scl->getScalerIndexForClass(itce); + std::cout << cls.name << ":" << tce << ":" << itce << std::endl; + } + if (cls.name.find("CMTVXVCH-B-NOPF-CRU") != std::string::npos) { + int ivch = cls.getIndex(); + vch = clsIndexToScaler[ivch]; + // vch = scl->getScalerIndexForClass(ivch); + std::cout << cls.name << ":" << vch << ":" << ivch << std::endl; + } + if (cls.name.find("C1ZNC-B-NOPF-CRU") != std::string::npos) { + int iznc = cls.getIndex(); + zncclsi = clsIndexToScaler[iznc]; + // vch = scl->getScalerIndexForClass(ivch); + std::cout << cls.name << ":" << zncclsi << ":" << iznc << std::endl; + } + } + if (tsc == 255 || tce == 255 || vch == 255) { + std::cout << " One of dcalers not available, check config to find alternative)" << std::endl; + return; + } + // + // Anal + // + // Times + double_t frev = 11245; + double_t time0 = recs[0].epochTime; + double_t timeL = recs[recs.size() - 1].epochTime; + double_t Trun = timeL - time0; + double_t orbit0 = recs[0].intRecord.orbit; + int n = recs.size() - 1; + std::cout << " Run duration:" << Trun << " Scalers size:" << n + 1 << std::endl; + // + int i0 = 0; + int ilast = 0; + if (t0 != 0. || tlast != 0.) { + for (int i = 0; i < n; i++) { + double_t ttime = recs[i].epochTime - time0; + if (!i0 && t0 < ttime) { + i0 = i; + } + if (!ilast && tlast < ttime) { + ilast = i; + } + } + } else { + ilast = n; + } + n = ilast - i0; + std::cout << "i0:" << i0 << " ilast:" << ilast << std::endl; + // Double_t x[n], znc[n], zncpp[n]; + std::vector xvec(n), zncvec(n), zncppvec(n), zncclassvec(n); + Double_t* x = xvec.data(); + Double_t* znc = zncvec.data(); + Double_t* zncpp = zncppvec.data(); + Double_t* zncclass = zncclassvec.data(); + // Double_t tcetsctoznc[n], tcetoznc[n], vchtoznc[n]; + std::vector tcetsctozncvec(n), tcetozncvec(n), vchtozncvec(n); + Double_t* tcetsctoznc = tcetsctozncvec.data(); + Double_t* tcetoznc = tcetozncvec.data(); + Double_t* vchtoznc = vchtozncvec.data(); + for (int i = i0; i < ilast; i++) { + // for (int i = 30; i < 40; i++) { + + int iv = i - i0; + x[iv] = (double_t)(recs[i + 1].intRecord.orbit + recs[i].intRecord.orbit) / 2. - orbit0; + x[iv] *= 88e-6; + // x[i] = (double_t)(recs[i+1].epochTime + recs[i].epochTime)/2.; + double_t tt = (double_t)(recs[i + 1].intRecord.orbit - recs[i].intRecord.orbit); + tt = tt * 88e-6; + // std::cout << i << " " << iv << " " << tt << std::endl; + // + // std::cout << recs[i+1].scalersInps[25] << std::endl; + double_t znci = (double_t)(recs[i + 1].scalersInps[25] - recs[i].scalersInps[25]); + double_t mu = -TMath::Log(1. - znci / tt / nbc / frev); + double_t zncipp = mu * nbc * frev; + zncpp[iv] = zncipp / 28.; + znc[iv] = znci / 28. / tt; + // znc class + znci = recs[i + 1].scalers[zncclsi].l1Before - recs[i].scalers[zncclsi].l1Before; + zncclass[iv] = znci / 28. / tt; + // std::cout << znc[i]/zncclass[i] << std::endl; + // + double_t had = 0; + if (sum) { + had += recs[i + 1].scalers[tce].lmBefore - recs[i].scalers[tce].lmBefore; + } + double_t mutce = -TMath::Log(1. - had / tt / nbc / frev); + // std::cout << recs[i+1].scalers[tce].lmBefore << std::endl; + had += recs[i + 1].scalers[tsc].lmBefore - recs[i].scalers[tsc].lmBefore; + // rat = (double_t)(had)/double_t(recs[i+1].scalersInps[25] - recs[i].scalersInps[25])*28; + if (zncpp[iv] > cut) { + tcetsctoznc[iv] = (double_t)(had) / zncpp[iv] / tt; + } else { + tcetsctoznc[iv] = 0.; + } + had = recs[i + 1].scalers[tce].lmBefore - recs[i].scalers[tce].lmBefore; + // rat = (double_t)(had)/double_t(recs[i+1].scalersInps[25] - recs[i].scalersInps[25])*28; + if (zncpp[iv] > cut) { + tcetoznc[iv] = (double_t)(had) / zncpp[iv] / tt; + } else { + tcetoznc[iv] = 0.; + } + had = recs[i + 1].scalers[vch].lmBefore - recs[i].scalers[vch].lmBefore; + double_t muvch = -TMath::Log(1. - had / tt / nbc / frev); + + // rat = (double_t)(had)/double_t(recs[i+1].scalersInps[25] - recs[i].scalersInps[25])*28; + if (zncpp[iv] > cut) { + vchtoznc[iv] = (double_t)(had) / zncpp[iv] / tt; + } else { + vchtoznc[iv] = 0.; + } + // std::cout << "muzdc:" << mu << " mu tce:" << mutce << " muvch:" << muvch << std::endl; + } + // + gStyle->SetMarkerSize(0.5); + TGraph* gr1 = new TGraph(n, x, znc); + TGraph* gr11 = new TGraph(n, x, zncpp); // PileuP corrected + TGraph* gr12 = new TGraph(n, x, zncclass); // NOT PileuP corrected + TGraph* gr2 = new TGraph(n, x, tcetsctoznc); + TGraph* gr3 = new TGraph(n, x, tcetoznc); + TGraph* gr4 = new TGraph(n, x, vchtoznc); + gr1->SetMarkerStyle(20); + gr11->SetMarkerStyle(20); + gr12->SetMarkerStyle(20); + gr11->SetMarkerColor(kRed); + gr12->SetMarkerColor(kBlue); + gr2->SetMarkerStyle(21); + gr3->SetMarkerStyle(23); + gr4->SetMarkerStyle(23); + if (sum) { + gr2->SetTitle("R=(TSC+TCE)*TVTX*B*28/ZNC; time[sec]; R"); + } else { + gr2->SetTitle("R=(TSC)*TVTX*B*28/ZNC; time[sec]; R"); + } + // gr2->GetHistogram()->SetMaximum(1.1); + // gr2->GetHistogram()->SetMinimum(0.9); + gr3->SetTitle("R=(TCE)*TVTX*B*28/ZNC; time[sec]; R"); + // gr3->GetHistogram()->SetMaximum(0.6); + // gr3->GetHistogram()->SetMinimum(0.4); + gr4->SetTitle("R=(VCH)*TVTX*B*28/ZNC; time[sec]; R"); + // gr4->GetHistogram()->SetMaximum(0.6); + // gr4->GetHistogram()->SetMinimum(0.4); + TMultiGraph* mg1 = new TMultiGraph(); + mg1->SetTitle("R=ZNC/28 rate [Hz] (red=PilUp Corrected); time[sec]; R"); + mg1->Add(gr1); + mg1->Add(gr11); + mg1->Add(gr12); + TCanvas* c1 = new TCanvas("c1", srun.c_str(), 200, 10, 800, 500); + std::string title = "RUN " + std::to_string(runNumber); + c1->SetTitle(title.c_str()); + c1->Divide(2, 2); + c1->cd(1); + mg1->Draw("AP"); + c1->cd(2); + gr2->Draw("AP"); + c1->cd(3); + gr3->Draw("AP"); + c1->cd(4); + gr4->Draw("AP"); + // getRate test: + double tt = timeStamp / 1000.; + std::pair r1 = scl->getRateGivenT(tt, 25, 7); + std::cout << "ZDC input getRateGivetT:" << r1.first / 28. << " " << r1.second / 28. << std::endl; + std::pair r2 = scl->getRateGivenT(tt, tce, 1); + std::cout << "LM before TCE class getRateGivetT:" << r2.first << " " << r2.second << std::endl; +} diff --git a/Detectors/CTP/macro/ReadCTPRunScalersFromFile.C b/Detectors/CTP/macro/ReadCTPRunScalersFromFile.C new file mode 100644 index 0000000000000..385a8c7e91e15 --- /dev/null +++ b/Detectors/CTP/macro/ReadCTPRunScalersFromFile.C @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TestCTPScalers.C +/// \brief create CTP scalers, test it and add to database +/// \author Roman Lietava +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Scalers.h" +#include "DataFormatsCTP/Configuration.h" +#include "TFile.h" +#include "TString.h" +#include +#include +#include +#endif +using namespace o2::ctp; +void ReadCTPRunScalersFromFile(std::string name = "s.root") +{ + std::cout << "Reading file:" << name << std::endl; + TFile* myFile = TFile::Open(name.c_str()); + bool doscalers = 1; + bool doconfig = 0; + if (doscalers) { + // CTPRunScalers* ctpscalers = myFile->Get("ccdb_object"); + CTPRunScalers* ctpscalers = myFile->Get("CTPRunScalers"); + if (ctpscalers != nullptr) { + ctpscalers->printStream(std::cout); + ctpscalers->convertRawToO2(); + ctpscalers->printIntegrals(); + } else { + std::cout << "Scalers not there ?" << std::endl; + } + } + if (doconfig) { + CTPConfiguration* ctpconfig = myFile->Get("CTPConfig"); + if (ctpconfig != nullptr) { + ctpconfig->printStream(std::cout); + } else { + std::cout << "Config not there ?" << std::endl; + } + } +} diff --git a/Detectors/CTP/macro/SaveInputsConfig.C b/Detectors/CTP/macro/SaveInputsConfig.C index 99cae77905541..459fbf4024c95 100644 --- a/Detectors/CTP/macro/SaveInputsConfig.C +++ b/Detectors/CTP/macro/SaveInputsConfig.C @@ -41,7 +41,7 @@ void SaveInputsConfig(std::string filename = "inputs.cfg", std::string ccdbHost long tmax = timeStamp + time365days; o2::ccdb::CcdbApi api; api.init(ccdbHost); // or http://localhost:8080 for a local installation - map metadata; // can be empty + std::map metadata; // can be empty // store abitrary user object in strongly typed manner api.storeAsTFileAny(&ctpcfginps, "CTP/Calib/Inputs", metadata, tmin, tmax); } diff --git a/Detectors/CTP/macro/TestConfig.C b/Detectors/CTP/macro/TestConfig.C index 0ff91c514ee93..38da821ea4807 100644 --- a/Detectors/CTP/macro/TestConfig.C +++ b/Detectors/CTP/macro/TestConfig.C @@ -12,6 +12,7 @@ #if !defined(__CLING__) || defined(__ROOTCLING__) #include #include +#include "CTPWorkflowScalers/ctpCCDBManager.h" #endif using namespace o2::ctp; @@ -22,8 +23,12 @@ void TestConfig(bool test = 0) } uint64_t timestamp = 1660196771632; std::string run = "523148"; - o2::ctp::CTPRunManager::setCCDBHost("https://alice-ccdb.cern.ch"); - auto ctpcfg = o2::ctp::CTPRunManager::getConfigFromCCDB(timestamp, run); + o2::ctp::ctpCCDBManager::setCCDBHost("https://alice-ccdb.cern.ch"); + bool ok; + auto ctpcfg = o2::ctp::ctpCCDBManager::getConfigFromCCDB(timestamp, run, ok); + if (ok == 0) { + std::cout << "Can not get config for run:" << run << std::endl; + } CTPConfiguration ctpconfig; ctpconfig.loadConfigurationRun3(ctpcfg.getConfigString()); // ctpconfig.printStream(std::cout); diff --git a/Detectors/CTP/macro/TestFetcher.C b/Detectors/CTP/macro/TestFetcher.C new file mode 100644 index 0000000000000..b2b6912f49911 --- /dev/null +++ b/Detectors/CTP/macro/TestFetcher.C @@ -0,0 +1,46 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#endif +using namespace o2::ctp; + +void TestFetcher(int runNumber = 557251) +{ + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + std::pair pp = ccdb.getRunDuration(runNumber); + long ts = pp.first + 60; + std::cout << "Run duration:" << pp.first << " " << pp.second << std::endl; + // Opening run + CTPRateFetcher fetcher; + fetcher.setupRun(runNumber, &ccdb, ts, 0); + ccdb.setURL("http://ali-qcdb-gpn.cern.ch:8083/"); + std::string QCDBPathCTPScalers = "qc/CTP/Scalers"; + std::map metadata; // can be empty + std::string run = std::to_string(runNumber); + metadata["runNumber"] = run; + CTPRunScalers* ctpscalers = ccdb.getSpecific(QCDBPathCTPScalers, ts, metadata); + auto tt = ctpscalers->getTimeLimitFromRaw(); + std::cout << "1st scalers duration:" << tt.first << " " << tt.second << std::endl; + fetcher.updateScalers(*ctpscalers); + auto rate = fetcher.fetchNoPuCorr(&ccdb, ts, runNumber, "T0VTX"); + std::cout << "1st rate:" << rate << std::endl; + // Running on the same run + ts = ts + 5 * 1000 * 3600; + ctpscalers = ccdb.getSpecific(QCDBPathCTPScalers, ts, metadata); + std::cout << "Later scalers duration:" << tt.first << " " << tt.second << std::endl; + fetcher.updateScalers(*ctpscalers); + rate = fetcher.fetchNoPuCorr(&ccdb, ts, runNumber, "T0VTX"); + std::cout << "Later rate:" << rate << std::endl; +} diff --git a/Detectors/CTP/macro/TestGetRates.C b/Detectors/CTP/macro/TestGetRates.C new file mode 100644 index 0000000000000..19644853c568b --- /dev/null +++ b/Detectors/CTP/macro/TestGetRates.C @@ -0,0 +1,48 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#endif +using namespace o2::ctp; + +void TestGetRates(int runN = 0) +{ + std::vector runs; + std::vector codes = {"T0VTX", "T0VTX", "ZNChadronic", "ZNChadronic", "T0VTX"}; + if (runN == 0) { + runs = {529066, 539218, 544013, 544518, 557251}; + } else { + runs.push_back(runN); + } + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + int i = 0; + for (auto const& runNumber : runs) { + // Opening run + std::pair pp = ccdb.getRunDuration(runNumber); + long ts = pp.first + 60; + // std::cout << "Run duration:" << pp.first << " " << pp.second << std::endl; + std::cout << "===> RUN:" << runNumber << " duration:" << (pp.second - pp.first) / 1000. << std::endl; + + CTPRateFetcher fetcher; + fetcher.setupRun(runNumber, &ccdb, ts, 1); + fetcher.setOrbit(1); + std::array rates; + fetcher.getRates(rates, &ccdb, runNumber, codes[i]); + std::cout << "Start:" << rates[0] << " End:" << rates[1] << " Middle:" << rates[2] << " code:" << codes[i] << std::endl; + double lumi1 = fetcher.getLumi(&ccdb, runNumber, codes[i], 0); + double lumi2 = fetcher.getLumi(&ccdb, runNumber, codes[i], 1); + std::cout << " Lumi NO pile up corr:" << lumi1 << " Lumi with pile upcorr:" << lumi2 << " code:" << codes[i] << std::endl; + i++; + } +} diff --git a/Detectors/CTP/reconstruction/CMakeLists.txt b/Detectors/CTP/reconstruction/CMakeLists.txt index e06f1e8ec9909..3aba8c3c2d588 100644 --- a/Detectors/CTP/reconstruction/CMakeLists.txt +++ b/Detectors/CTP/reconstruction/CMakeLists.txt @@ -11,7 +11,10 @@ o2_add_library(CTPReconstruction SOURCES src/CTFCoder.cxx src/CTFHelper.cxx + src/RawDataDecoder.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::DPLUtils + O2::DetectorsRaw O2::DetectorsBase O2::CommonDataFormat O2::DetectorsCommonDataFormats diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index 6c6d867a1a3a4..8dbc5adadbfc5 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -23,8 +23,9 @@ #include "DataFormatsCTP/CTF.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/CTFCoderBase.h" -#include "rANS/rans.h" #include "CTPReconstruction/CTFHelper.h" +#include "CTPReconstruction/RawDataDecoder.h" +#include "DataFormatsCTP/Configuration.h" class TTree; @@ -33,10 +34,10 @@ namespace o2 namespace ctp { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CTP) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CTP, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF @@ -47,7 +48,16 @@ class CTFCoder : public o2::ctf::CTFCoderBase template o2::ctf::CTFIOSize decode(const CTF::base& ec, VTRG& data, LumiInfo& lumi); + /// add CTP related shifts + template + bool finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj); + void createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) final; + void setDecodeInps(bool decodeinps) { mDecodeInps = decodeinps; } + void setCTPConfig(CTPConfiguration cfg) { mCTPConfig = std::move(cfg); } + bool getDecodeInps() { return mDecodeInps; } + CTPConfiguration& getCTPConfig() { return mCTPConfig; } + bool canApplyBCShiftInputs(const o2::InteractionRecord& ir) const { return canApplyBCShift(ir, mBCShiftInputs); } private: template @@ -56,6 +66,9 @@ class CTFCoder : public o2::ctf::CTFCoderBase void appendToTree(TTree& tree, CTF& ec); void readFromTree(TTree& tree, int entry, std::vector& data, LumiInfo& lumi); std::vector mDataFilt; + CTPConfiguration mCTPConfig; + int mBCShiftInputs = 0; + bool mDecodeInps = false; }; /// entropy-encode clusters to buffer with CTF @@ -80,10 +93,10 @@ o2::ctf::CTFIOSize CTFCoder::encode_impl(VEC& buff, const gsl::spansetHeader(helper.createHeader(lumi)); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODECTP(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODECTP(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODECTP(helper.begin_bcIncTrig(), helper.end_bcIncTrig(), CTF::BLC_bcIncTrig, 0); iosize += ENCODECTP(helper.begin_orbitIncTrig(), helper.end_orbitIncTrig(), CTF::BLC_orbitIncTrig, 0); @@ -121,12 +133,12 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l auto header = ec.getHeader(); checkDictVersion(static_cast(header)); ec.print(getPrefix(), mVerbosity); - std::vector bcInc; - std::vector orbitInc; + std::vector bcInc; + std::vector orbitInc; std::vector bytesInput, bytesClass; o2::ctf::CTFIOSize iosize; -#define DECODECTP(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODECTP(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODECTP(bcInc, CTF::BLC_bcIncTrig); iosize += DECODECTP(orbitInc, CTF::BLC_orbitIncTrig); @@ -135,14 +147,19 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l // clang-format on // data.clear(); - + std::map digitsMap; o2::InteractionRecord ir(header.firstBC, header.firstOrbit); lumi.nHBFCounted = header.lumiNHBFs; + lumi.nHBFCountedFV0 = header.lumiNHBFsFV0 ? header.lumiNHBFsFV0 : header.lumiNHBFs; lumi.counts = header.lumiCounts; + lumi.countsFV0 = header.lumiCountsFV0; lumi.orbit = header.lumiOrbit; + lumi.inp1 = int(header.inp1); + lumi.inp2 = int(header.inp2); auto itInp = bytesInput.begin(); auto itCls = bytesClass.begin(); bool checkIROK = (mBCShift == 0); // need to check if CTP offset correction does not make the local time negative ? + bool checkIROKInputs = (mBCShiftInputs == 0); // need to check if CTP offset correction does not make the local time negative ? for (uint32_t itrig = 0; itrig < header.nTriggers; itrig++) { // restore TrigRecord if (orbitInc[itrig]) { // non-0 increment => new orbit @@ -151,20 +168,71 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l } else { ir.bc += bcInc[itrig]; } - if (checkIROK || canApplyBCShift(ir)) { // correction will be ok - checkIROK = true; + if (checkIROKInputs || canApplyBCShiftInputs(ir)) { // correction will be ok + auto irs = ir - mBCShiftInputs; + uint64_t CTPInputMask = 0; + for (int i = 0; i < CTFHelper::CTPInpNBytes; i++) { + CTPInputMask |= static_cast(*itInp++) << (8 * i); + } + if (CTPInputMask) { + if (digitsMap.count(irs)) { + if (digitsMap[irs].isInputEmpty()) { + digitsMap[irs].CTPInputMask = CTPInputMask; + // LOG(info) << "IR1:"; + // digitsMap[irs].printStream(std::cout); + } else { + LOG(error) << "CTPInpurMask already exist:" << irs << " dig.CTPInputMask:" << digitsMap[irs].CTPInputMask << " CTPInputMask:" << CTPInputMask; + } + } else { + CTPDigit dig = {irs, CTPInputMask, 0}; + digitsMap[irs] = dig; + // LOG(info) << "IR2:"; + // digitsMap[irs].printStream(std::cout); + } + } } else { // correction would make IR prior to mFirstTFOrbit, skip itInp += CTFHelper::CTPInpNBytes; + } + if (checkIROK || canApplyBCShift(ir)) { // correction will be ok + auto irs = ir - mBCShift; + uint64_t CTPClassMask = 0; + for (int i = 0; i < CTFHelper::CTPClsNBytes; i++) { + CTPClassMask |= static_cast(*itCls++) << (8 * i); + } + if (CTPClassMask) { + if (digitsMap.count(irs)) { + if (digitsMap[irs].isClassEmpty()) { + digitsMap[irs].CTPClassMask = CTPClassMask; + // LOG(info) << "TCM1:"; + // digitsMap[irs].printStream(std::cout); + } else { + LOG(error) << "CTPClassMask already exist:" << irs << " dig.CTPClassMask:" << digitsMap[irs].CTPClassMask << " CTPClassMask:" << CTPClassMask; + } + } else { + CTPDigit dig = {irs, 0, CTPClassMask}; + digitsMap[irs] = dig; + // LOG(info) << "TCM2:"; + // digitsMap[irs].printStream(std::cout); + } + } + } else { // correction would make IR prior to mFirstTFOrbit, skip itCls += CTFHelper::CTPClsNBytes; - continue; } - auto& dig = data.emplace_back(); - dig.intRecord = ir - mBCShift; - for (int i = 0; i < CTFHelper::CTPInpNBytes; i++) { - dig.CTPInputMask |= static_cast(*itInp++) << (8 * i); + } + if (mDecodeInps) { + uint64_t trgclassmask = 0xffffffffffffffff; + if (mCTPConfig.getRunNumber() != 0) { + trgclassmask = mCTPConfig.getTriggerClassMask(); } - for (int i = 0; i < CTFHelper::CTPClsNBytes; i++) { - dig.CTPClassMask |= static_cast(*itCls++) << (8 * i); + // std::cout << "trgclassmask:" << std::hex << trgclassmask << std::dec << std::endl; + o2::pmr::vector digits; + o2::ctp::RawDataDecoder::shiftInputs(digitsMap, digits, mFirstTFOrbit, trgclassmask); + for (auto const& dig : digits) { + data.emplace_back(dig); + } + } else { + for (auto const& dig : digitsMap) { + data.emplace_back(dig.second); } } assert(itInp == bytesInput.end()); @@ -172,6 +240,15 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l iosize.rawIn = header.nTriggers * sizeof(CTPDigit); return iosize; } +///________________________________ +template +bool CTFCoder::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) +{ + auto match = o2::ctf::CTFCoderBase::finaliseCCDB(matcher, obj); + mBCShiftInputs = -o2::ctp::TriggerOffsetsParam::Instance().globalInputsShift; + LOG(info) << "BCShiftInputs:" << mBCShiftInputs; + return match; +} } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h index ceb99c36a8f26..34228e3acc55f 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h @@ -38,8 +38,8 @@ class CTFHelper CTFHeader createHeader(const LumiInfo& lumi) { CTFHeader h{o2::detectors::DetID::CTP, 0, 1, 0, // dummy timestamp, version 1.0 - lumi.counts, lumi.countsFV0, lumi.nHBFCounted, lumi.orbit, - uint32_t(mData.size()), 0, 0}; + lumi.counts, lumi.countsFV0, lumi.nHBFCounted, lumi.nHBFCountedFV0, lumi.orbit, + uint32_t(mData.size()), 0, 0, uint16_t(lumi.inp1), uint16_t(lumi.inp2)}; if (mData.size()) { h.firstOrbit = mData[0].intRecord.orbit; h.firstBC = mData[0].intRecord.bc; @@ -55,7 +55,7 @@ class CTFHelper class _Iter { public: - using difference_type = int64_t; + using difference_type = std::ptrdiff_t; using value_type = T; using pointer = const T*; using reference = const T&; @@ -64,91 +64,86 @@ class CTFHelper _Iter(const gsl::span& data, bool end = false) : mData(data), mIndex(end ? M * data.size() : 0){}; _Iter() = default; - const I& operator++() + inline I& operator++() noexcept { ++mIndex; - return (I&)(*this); + return static_cast(*this); } - const I& operator--() + inline I operator++(int) { - mIndex--; - return (I&)(*this); + I res = *(static_cast(this)); + ++mIndex; + return res; } - difference_type operator-(const I& other) const { return mIndex - other.mIndex; } - - difference_type operator-(size_t idx) const { return mIndex - idx; } - - const I& operator-(size_t idx) - { - mIndex -= idx; - return (I&)(*this); - } - const I operator++(int) + inline I& operator--() noexcept { - auto res = *this; - ++mIndex; - return res; + mIndex--; + return static_cast(*this); } - const I operator--(int) + inline I operator--(int) { - auto res = *this; + I res = *(static_cast(this)); --mIndex; return res; } - const I& operator+=(difference_type i) + I& operator+=(difference_type i) noexcept { mIndex += i; - return (I&)(*this); + return static_cast(*this); } - const I operator+=(difference_type i) const + I operator+(difference_type i) const { - auto tmp = *const_cast(this); - return tmp += i; + I res = *(const_cast(static_cast(this))); + return res += i; } - const I& operator-=(difference_type i) + I& operator-=(difference_type i) noexcept { mIndex -= i; - return (I&)(*this); + return static_cast(*this); } - const I operator-=(difference_type i) const + I operator-(difference_type i) const { - auto tmp = *const_cast(this); - return tmp -= i; + I res = *(const_cast(static_cast(this))); + return res -= i; } - bool operator!=(const I& other) const { return mIndex != other.mIndex; } - bool operator==(const I& other) const { return mIndex == other.mIndex; } - bool operator>(const I& other) const { return mIndex > other.mIndex; } - bool operator<(const I& other) const { return mIndex < other.mIndex; } - bool operator>=(const I& other) const { return mIndex >= other.mIndex; } - bool operator<=(const I& other) const { return mIndex <= other.mIndex; } + difference_type operator-(const I& other) const noexcept { return mIndex - other.mIndex; } + + inline friend I operator+(difference_type i, const I& iter) { return iter + i; }; + + bool operator!=(const I& other) const noexcept { return mIndex != other.mIndex; } + bool operator==(const I& other) const noexcept { return mIndex == other.mIndex; } + bool operator>(const I& other) const noexcept { return mIndex > other.mIndex; } + bool operator<(const I& other) const noexcept { return mIndex < other.mIndex; } + bool operator>=(const I& other) const noexcept { return mIndex >= other.mIndex; } + bool operator<=(const I& other) const noexcept { return mIndex <= other.mIndex; } protected: gsl::span mData{}; - size_t mIndex = 0; + difference_type mIndex = 0; }; //_______________________________________________ // BC difference wrt previous if in the same orbit, otherwise the abs.value. // For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_bcIncTrig : public _Iter + class Iter_bcIncTrig : public _Iter { public: - using _Iter::_Iter; + using _Iter::_Iter; value_type operator*() const { if (mIndex) { if (mData[mIndex].intRecord.orbit == mData[mIndex - 1].intRecord.orbit) { - return mData[mIndex].intRecord.bc - mData[mIndex - 1].intRecord.bc; + return value_type(mData[mIndex].intRecord.bc - mData[mIndex - 1].intRecord.bc); } else { - return mData[mIndex].intRecord.bc; + return value_type(mData[mIndex].intRecord.bc); } } return 0; @@ -158,9 +153,9 @@ class CTFHelper size_t id = mIndex + i; if (id) { if (mData[id].intRecord.orbit == mData[id - 1].intRecord.orbit) { - return mData[id].intRecord.bc - mData[id - 1].intRecord.bc; + return value_type(mData[id].intRecord.bc - mData[id - 1].intRecord.bc); } else { - return mData[id].intRecord.bc; + return value_type(mData[id].intRecord.bc); } } return 0; @@ -169,15 +164,15 @@ class CTFHelper //_______________________________________________ // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_orbitIncTrig : public _Iter + class Iter_orbitIncTrig : public _Iter { public: - using _Iter::_Iter; - value_type operator*() const { return mIndex ? mData[mIndex].intRecord.orbit - mData[mIndex - 1].intRecord.orbit : 0; } + using _Iter::_Iter; + value_type operator*() const { return value_type(mIndex ? mData[mIndex].intRecord.orbit - mData[mIndex - 1].intRecord.orbit : 0); } value_type operator[](difference_type i) const { size_t id = mIndex + i; - return id ? mData[id].intRecord.orbit - mData[id - 1].intRecord.orbit : 0; + return value_type(id ? mData[id].intRecord.orbit - mData[id - 1].intRecord.orbit : 0); } }; diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h new file mode 100644 index 0000000000000..53addf32c538f --- /dev/null +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h @@ -0,0 +1,104 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RawDataDecoder.h +/// \brief Digits tw Raw translation +/// \author Roman Lietava + +#ifndef ALICEO2_CTP_RAWDATADECODER_H_ +#define ALICEO2_CTP_RAWDATADECODER_H_ + +#include +#include +#include +#include "Framework/InputRecord.h" +#include "DataFormatsCTP/Digits.h" +#include "DataFormatsCTP/LumiInfo.h" +#include "DataFormatsCTP/Configuration.h" + +namespace o2 +{ +namespace ctp +{ +class RawDataDecoder +{ + public: + RawDataDecoder() = default; + ~RawDataDecoder() = default; + static void makeGBTWordInverse(std::vector& diglets, gbtword80_t& GBTWord, gbtword80_t& remnant, uint32_t& size_gbt, uint32_t Npld); + int addCTPDigit(uint32_t linkCRU, uint32_t triggerOrbit, gbtword80_t& diglet, gbtword80_t& pldmask, std::map& digits); + int decodeRaw(o2::framework::InputRecord& inputs, std::vector& filter, o2::pmr::vector& digits, std::vector& lumiPointsHBF1); + int decodeRawFatal(o2::framework::InputRecord& inputs, std::vector& filter); + int decodeRaw(o2::framework::InputRecord& inputs, std::vector& filter, std::vector& digits, std::vector& lumiPointsHBF1); + void setDecodeInps(bool decodeinps) { mDecodeInps = decodeinps; } + void setDoLumi(bool lumi) { mDoLumi = lumi; } + void setDoDigits(bool digi) { mDoDigits = digi; } + void setVerbose(bool v) { mVerbose = v; } + void setMAXErrors(int m) { mErrorMax = m; } + int setLumiInp(int lumiinp, std::string inp); + void setCTPConfig(CTPConfiguration cfg) { mCTPConfig = std::move(cfg); }; + void setCheckConsistency(bool check) { mCheckConsistency = check; } + uint32_t getIRRejected() const { return mIRRejected; } + uint32_t getTCRRejected() const { return mTCRRejected; } + std::vector& getTFOrbits() { return mTFOrbits; } + int getErrorIR() { return mErrorIR; } + int getErrorTCR() { return mErrorTCR; } + CTPConfiguration& getCTPConfig() { return mCTPConfig; } + int init(); + static int shiftNew(const o2::InteractionRecord& irin, uint32_t TFOrbit, std::bitset<48>& inpmask, int64_t shift, int level, std::map& digmap); + static int shiftInputs(std::map& digitsMap, o2::pmr::vector& digits, uint32_t TFOrbit, uint64_t trgclassmask = 0xffffffffffffffff); + int checkReadoutConsistentncy(o2::pmr::vector& digits, uint64_t trgclassmask = 0xffffffffffffffff, uint64_t trigclassmaskNoTrgDets = 0xffffffffffffffff); + std::array getClassErrorsA() { return mClassErrorsA; } + std::array getClassErrorsB() { return mClassErrorsB; } + std::array getClassCountersA() { return mClassCountersA; } + std::array getClassCountersB() { return mClassCountersB; } + int getLostDueToShiftCls() { return mLostDueToShiftCC; } + int getLostDueToShiftInp() { return mLostDueToShiftInps; } + + private: + static constexpr uint32_t TF_TRIGGERTYPE_MASK = 0x800; + static constexpr uint32_t HB_TRIGGERTYPE_MASK = 0x2; + // true: full inps decoding includine latency shifts here; false: latency shifts in CTF decoder + bool mDecodeInps = false; + bool mCheckConsistency = false; + // for digits + bool mDoDigits = true; + std::vector mOutputDigits; + // for lumi + bool mDoLumi = true; + // + static constexpr std::bitset LMMASKInputs = 0xfff; + static constexpr std::bitset L0MASKInputs = 0xfff000; + static constexpr std::bitset L1MASKInputs = (0xffffffull << 24); + gbtword80_t mTVXMask = 0x4; // TVX is 3rd input + gbtword80_t mVBAMask = 0x20; // VBA is 6 th input + bool mVerbose = false; + int mIRRejected = 0; + int mTCRRejected = 0; + bool mPadding = true; + uint32_t mTFOrbit = 0; + std::vector mTFOrbits; + // error verbosness + int mErrorIR = 0; + int mErrorTCR = 0; + int mErrorMax = 5; + bool mStickyError = false; + std::array mClassErrorsA{}; + std::array mClassErrorsB{}; // from inputs + std::array mClassCountersA{}; + std::array mClassCountersB{}; // from inputs + int mLostDueToShiftCC = 0; + int mLostDueToShiftInps = 0; + CTPConfiguration mCTPConfig; +}; +} // namespace ctp +} // namespace o2 +#endif diff --git a/Detectors/CTP/reconstruction/src/CTFCoder.cxx b/Detectors/CTP/reconstruction/src/CTFCoder.cxx index facbc9ebd40cf..e182032e12605 100644 --- a/Detectors/CTP/reconstruction/src/CTFCoder.cxx +++ b/Detectors/CTP/reconstruction/src/CTFCoder.cxx @@ -18,7 +18,6 @@ #include using namespace o2::ctp; - ///___________________________________________________________________________________ // Register encoded data in the tree (Fill is not called, will be done by caller) void CTFCoder::appendToTree(TTree& tree, CTF& ec) @@ -41,10 +40,10 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); // just to get types - uint16_t bcInc = 0; - uint32_t orbitInc = 0; + int16_t bcInc = 0; + int32_t orbitInc = 0; uint8_t bytesInput = 0, bytesClass = 0; -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(bcInc, CTF::BLC_bcIncTrig); MAKECODER(orbitInc, CTF::BLC_orbitIncTrig); diff --git a/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx b/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx new file mode 100644 index 0000000000000..913b1983fe61c --- /dev/null +++ b/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx @@ -0,0 +1,695 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RawDataDecoder.cxx +/// \author Roman Lietava +#include +#include "DetectorsRaw/RDHUtils.h" +#include "DPLUtils/DPLRawParser.h" +#include "DataFormatsCTP/TriggerOffsetsParam.h" +#include "CTPReconstruction/RawDataDecoder.h" +#include "DataFormatsCTP/Configuration.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include + +using namespace o2::ctp; + +// Inverse of Digits2Raw::makeGBTWord +void RawDataDecoder::makeGBTWordInverse(std::vector& diglets, gbtword80_t& GBTWord, gbtword80_t& remnant, uint32_t& size_gbt, uint32_t Npld) +{ + gbtword80_t diglet = remnant; + uint32_t i = 0; + while (i < (NGBT - Npld)) { + std::bitset masksize = 0; + for (uint32_t j = 0; j < (Npld - size_gbt); j++) { + masksize[j] = 1; + } + diglet |= (GBTWord & masksize) << (size_gbt); + diglets.push_back(diglet); + diglet = 0; + i += Npld - size_gbt; + GBTWord = GBTWord >> (Npld - size_gbt); + size_gbt = 0; + } + size_gbt = NGBT - i; + remnant = GBTWord; +} +int RawDataDecoder::addCTPDigit(uint32_t linkCRU, uint32_t orbit, gbtword80_t& diglet, gbtword80_t& pldmask, std::map& digits) +{ + int ret = 0; + gbtword80_t pld = (diglet & pldmask); + if (pld.count() == 0) { + return 0; + } + pld >>= 12; + CTPDigit digit; + const gbtword80_t bcidmask = 0xfff; + uint16_t bcid = (diglet & bcidmask).to_ulong(); + LOG(debug) << bcid << " pld:" << pld; + o2::InteractionRecord ir = {bcid, orbit}; + if (linkCRU == o2::ctp::GBTLinkIDIntRec) { + int32_t BCShiftCorrectionInps = -o2::ctp::TriggerOffsetsParam::Instance().globalInputsShift; + LOG(debug) << "InputMaskCount:" << digits[ir].CTPInputMask.count(); + LOG(debug) << "ir ir ori:" << ir; + if ((ir.orbit <= mTFOrbit) && ((int32_t)ir.bc < BCShiftCorrectionInps)) { + // LOG(warning) << "Loosing ir:" << ir; + mIRRejected++; + return 0; + } + ir -= BCShiftCorrectionInps; + LOG(debug) << "ir ir corrected:" << ir; + digit.intRecord = ir; + if (digits.count(ir) == 0) { + digit.setInputMask(pld); + digits[ir] = digit; + LOG(debug) << bcid << " inputs case 0 bcid orbit " << orbit << " pld:" << pld; + } else if (digits.count(ir) == 1) { + if (digits[ir].CTPInputMask.count() == 0) { + digits[ir].setInputMask(pld); + LOG(debug) << bcid << " inputs bcid case 1 orbit " << orbit << " pld:" << pld; + } else { + if (mErrorIR < mErrorMax) { + LOG(error) << "Two CTP IRs with the same timestamp:" << ir.bc << " " << ir.orbit << " pld:" << pld << " dig:" << digits[ir]; + } + ret = 4; + mErrorIR++; + mStickyError = true; + } + } else { + LOG(error) << "Two digits with the same timestamp:" << ir.bc << " " << ir.orbit; + ret = 8; + } + } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { + int32_t BCShiftCorrection = -o2::ctp::TriggerOffsetsParam::Instance().customOffset[o2::detectors::DetID::CTP]; + int32_t offset = BCShiftCorrection + o2::ctp::TriggerOffsetsParam::Instance().LM_L0 + o2::ctp::TriggerOffsetsParam::Instance().L0_L1_classes - 1; + LOG(debug) << "tcr ir ori:" << ir; + if ((ir.orbit <= mTFOrbit) && ((int32_t)ir.bc < offset)) { + // LOG(warning) << "Loosing tclass:" << ir; + mTCRRejected++; + return 0; + } + ir -= offset; + LOG(debug) << "tcr ir corrected:" << ir; + digit.intRecord = ir; + if (digits.count(ir) == 0) { + digit.setClassMask(pld); + digits[ir] = digit; + LOG(debug) << bcid << " class bcid case 0 orbit " << orbit << " pld:" << pld; + } else if (digits.count(ir) == 1) { + if (digits[ir].CTPClassMask.count() == 0) { + digits[ir].setClassMask(pld); + LOG(debug) << bcid << " class bcid case 1 orbit " << orbit << " pld:" << pld; + } else { + if (mErrorTCR < mErrorMax) { + LOG(error) << "Two CTP Class masks for same timestamp"; + mStickyError = true; + } + mErrorTCR++; + ret = 16; + } + } else { + LOG(error) << "Two digits with the same timestamp:" << ir.bc << " " << ir.orbit; + ret = 32; + } + } else { + LOG(error) << "Unxpected CTP CRU link:" << linkCRU; + } + return ret; +} +// +// Decodes one page +// It is NOT assumed that CTP HBF has never more than one page. +// 1 HBF/page <= 8000kB = 8*1024*8/120 = 546 GBT words = 546 IRs/page = 5.5 MHz +int RawDataDecoder::decodeRaw(o2::framework::InputRecord& inputs, std::vector& filter, o2::pmr::vector& digits, std::vector& lumiPointsHBF1) +{ + int ret = 0; + static int nwrites = 0; + uint64_t countsMBT = 0; + uint64_t countsMBV = 0; + std::map digitsMap; + // + // using InputSpec = o2::framework::InputSpec; + // using ConcreteDataTypeMatcher = o2::framework::ConcreteDataTypeMatcher; + // using Lifetime = o2::framework::Lifetime; + o2::framework::DPLRawParser parser(inputs, filter); + uint32_t payloadCTP = 0; + gbtword80_t remnant = 0; + uint32_t size_gbt = 0; + mTFOrbit = 0; + uint32_t orbit0 = 0; + for (auto it = parser.begin(); it != parser.end(); ++it) { + const o2::header::RDHAny* rdh = nullptr; + try { + rdh = reinterpret_cast(it.raw()); + mPadding = (o2::raw::RDHUtils::getDataFormat(rdh) == 0); + } catch (std::exception& e) { + LOG(error) << "Failed to extract RDH, abandoning TF sending dummy output, exception was: " << e.what(); + // dummyOutput(); + return 1; + } + // auto triggerOrbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); + uint32_t stopBit = o2::raw::RDHUtils::getStop(rdh); + uint32_t packetCounter = o2::raw::RDHUtils::getPageCounter(rdh); + uint32_t version = o2::raw::RDHUtils::getVersion(rdh); + uint32_t rdhOrbit = o2::raw::RDHUtils::getHeartBeatOrbit(rdh); + uint32_t triggerType = o2::raw::RDHUtils::getTriggerType(rdh); + // std::cout << "diff orbits:" << triggerOrbit - rdhOrbit << std::endl; + bool tf = (triggerType & TF_TRIGGERTYPE_MASK) && (packetCounter == 0); + bool hb = (triggerType & HB_TRIGGERTYPE_MASK) && (packetCounter == 0); + if (tf) { + mTFOrbit = rdhOrbit; + // std::cout << "tforbit==================>" << mTFOrbit << " " << std::hex << mTFOrbit << std::endl; + mTFOrbits.push_back(mTFOrbit); + } + static bool prt = true; + if (prt) { + LOG(info) << "RDH version:" << version << " Padding:" << mPadding; + prt = false; + } + auto feeID = o2::raw::RDHUtils::getFEEID(rdh); // 0 = IR, 1 = TCR + auto linkCRU = (feeID & 0xf00) >> 8; + // LOG(info) << "CRU link:" << linkCRU; + if (linkCRU == o2::ctp::GBTLinkIDIntRec) { + payloadCTP = o2::ctp::NIntRecPayload; + } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { + payloadCTP = o2::ctp::NClassPayload; + if (!mDoDigits) { // Do not do TCR if only lumi + continue; + } + } else { + LOG(error) << "Unxpected CTP CRU link:" << linkCRU; + } + LOG(debug) << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << rdhOrbit << " triggerType:" << triggerType; + // LOG(info) << "remnant :" << remnant.count(); + gbtword80_t pldmask = 0; + for (uint32_t i = 0; i < payloadCTP; i++) { + pldmask[12 + i] = 1; + } + // std::cout << (orbit0 != rdhOrbit) << " comp " << (mTFOrbit==rdhOrbit) << std::endl; + // if(orbit0 != rdhOrbit) { + if (hb) { + if ((mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) && !tf) { // create lumi per HB + lumiPointsHBF1.emplace_back(LumiInfo{rdhOrbit, 0, 0, countsMBT, countsMBV}); + // std::cout << "hb:" << nhb << " countsMBT:" << countsMBT << std::endl; + countsMBT = 0; + countsMBV = 0; + // nhb++; + } + remnant = 0; + size_gbt = 0; + orbit0 = rdhOrbit; + // std::cout << "orbit0============>" << std::dec << orbit0 << " " << std::hex << orbit0 << std::endl; + } + // Create 80 bit words + gsl::span payload(it.data(), it.size()); + gbtword80_t gbtWord80; + gbtWord80.set(); + int wordCount = 0; + int wordSize = 10; + std::vector gbtwords80; + // mPadding = 0; + if (mPadding == 1) { + wordSize = 16; + } + // LOG(info) << ii << " payload size:" << payload.size(); + /* if (payload.size()) { + //LOG(info) << "payload size:" << payload.size(); + // LOG(info) << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << triggerOrbit << " stopbit:" << stopBit << " packet:" << packetCounter; + // LOGP(info, "RDH FEEid: {} CRU link: {}, Orbit: {}", feeID, linkCRU, triggerOrbit); + std::cout << std::hex << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << rdhOrbit << std::endl; + } */ + gbtword80_t bcmask = std::bitset<80>("111111111111"); + for (auto payloadWord : payload) { + int wc = wordCount % wordSize; + // LOG(info) << wordCount << ":" << wc << " payload:" << int(payloadWord); + if ((wc == 0) && (wordCount != 0)) { + if (gbtWord80.count() != 80) { + gbtwords80.push_back(gbtWord80); + } + gbtWord80.set(); + } + if (wc < 10) { + for (int i = 0; i < 8; i++) { + gbtWord80[wc * 8 + i] = bool(int(payloadWord) & (1 << i)); + } + } + wordCount++; + } + if ((gbtWord80.count() != 80) && (gbtWord80.count() > 0)) { + gbtwords80.push_back(gbtWord80); + } + // decode 80 bits payload + for (auto word : gbtwords80) { + std::vector diglets; + gbtword80_t gbtWord = word; + makeGBTWordInverse(diglets, gbtWord, remnant, size_gbt, payloadCTP); + for (auto diglet : diglets) { + if (mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) { + gbtword80_t pld = (diglet >> 12) & mTVXMask; + if (pld.count() != 0) { + countsMBT++; + } + pld = (diglet >> 12) & mVBAMask; + if (pld.count() != 0) { + countsMBV++; + } + } + if (!mDoDigits) { + continue; + } + LOG(debug) << "diglet:" << diglet << " " << (diglet & bcmask).to_ullong(); + ret = addCTPDigit(linkCRU, rdhOrbit, diglet, pldmask, digitsMap); + } + } + if ((remnant.count() > 0) && stopBit) { + if (mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) { + gbtword80_t pld = (remnant >> 12) & mTVXMask; + if (pld.count() != 0) { + countsMBT++; + } + pld = (remnant >> 12) & mVBAMask; + if (pld.count() != 0) { + countsMBV++; + } + } + if (!mDoDigits) { + continue; + } + ret = addCTPDigit(linkCRU, rdhOrbit, remnant, pldmask, digitsMap); + LOG(debug) << "diglet:" << remnant << " " << (remnant & bcmask).to_ullong(); + remnant = 0; + } + } + if (mDoLumi) { + lumiPointsHBF1.emplace_back(LumiInfo{orbit0, 0, 0, countsMBT, countsMBV}); + // std::cout << "last lumi:" << nhb << std::endl; + } + if (mDoDigits & mDecodeInps) { + uint64_t trgclassmask = 0xffffffffffffffff; + uint64_t trgclassmaskNOTRGDet = 0xffffffffffffffff; + if (mCTPConfig.getRunNumber() != 0) { + trgclassmask = mCTPConfig.getTriggerClassMaskWInputs(); // classes triggered by internal ctp generators not here + trgclassmaskNOTRGDet = mCTPConfig.getTriggerClassMaskWInputsNoTrgDets(); + // mCTPConfig.printStream(std::cout); + } + // std::cout << "trgclassmask:" << std::hex << trgclassmask << std::dec << std::endl; + mLostDueToShiftInps += shiftInputs(digitsMap, digits, mTFOrbit); + if (mCheckConsistency) { + ret = checkReadoutConsistentncy(digits, trgclassmask, trgclassmaskNOTRGDet); + } + } + if (mDoDigits && !mDecodeInps) { + for (auto const& dig : digitsMap) { + digits.push_back(dig.second); + } + } + // ret = 1; + if (mStickyError) { + if (nwrites < mErrorMax) { + std::string file = "dumpCTP" + std::to_string(nwrites) + ".bin"; + std::ofstream dumpctp(file.c_str(), std::ios::out | std::ios::binary); + if (!dumpctp.good()) { + LOGP(error, "Failed to open file {}", file); + } else { + LOGP(info, "CTP dump file open {}", file); + for (auto it = parser.begin(); it != parser.end(); ++it) { + char* dataout = (char*)(it.raw()); + dumpctp.write(dataout, it.sizeTotal()); + } + dumpctp.close(); + } + nwrites++; + } + // LOG(error) << "CTP decoding IR errors:" << mErrorIR << " TCR errors:" << mErrorTCR; + } + return ret; +} +// +int RawDataDecoder::decodeRawFatal(o2::framework::InputRecord& inputs, std::vector& filter) +{ + o2::framework::DPLRawParser parser(inputs, filter); + uint32_t payloadCTP = 0; + gbtword80_t remnant = 0; + uint32_t size_gbt = 0; + mTFOrbit = 0; + uint32_t orbit0 = 0; + std::array rates{}; + std::array ratesC{}; + for (auto it = parser.begin(); it != parser.end(); ++it) { + const o2::header::RDHAny* rdh = nullptr; + try { + rdh = reinterpret_cast(it.raw()); + mPadding = (o2::raw::RDHUtils::getDataFormat(rdh) == 0); + } catch (std::exception& e) { + LOG(error) << "Failed to extract RDH, abandoning TF sending dummy output, exception was: " << e.what(); + // dummyOutput(); + return 1; + } + // auto triggerOrbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); + uint32_t stopBit = o2::raw::RDHUtils::getStop(rdh); + uint32_t packetCounter = o2::raw::RDHUtils::getPageCounter(rdh); + uint32_t version = o2::raw::RDHUtils::getVersion(rdh); + uint32_t rdhOrbit = o2::raw::RDHUtils::getHeartBeatOrbit(rdh); + uint32_t triggerType = o2::raw::RDHUtils::getTriggerType(rdh); + // std::cout << "diff orbits:" << triggerOrbit - rdhOrbit << std::endl; + bool tf = (triggerType & TF_TRIGGERTYPE_MASK) && (packetCounter == 0); + bool hb = (triggerType & HB_TRIGGERTYPE_MASK) && (packetCounter == 0); + if (tf) { + mTFOrbit = rdhOrbit; + // std::cout << "tforbit==================>" << mTFOrbit << " " << std::hex << mTFOrbit << std::endl; + mTFOrbits.push_back(mTFOrbit); + } + static bool prt = true; + if (prt) { + LOG(info) << "RDH version:" << version << " Padding:" << mPadding; + prt = false; + } + auto feeID = o2::raw::RDHUtils::getFEEID(rdh); // 0 = IR, 1 = TCR + auto linkCRU = (feeID & 0xf00) >> 8; + // LOG(info) << "CRU link:" << linkCRU; + if (linkCRU == o2::ctp::GBTLinkIDIntRec) { + payloadCTP = o2::ctp::NIntRecPayload; + } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { + payloadCTP = o2::ctp::NClassPayload; + } else { + LOG(error) << "Unxpected CTP CRU link:" << linkCRU; + } + LOG(debug) << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << rdhOrbit << " triggerType:" << triggerType; + // LOG(info) << "remnant :" << remnant.count(); + gbtword80_t pldmask = 0; + for (uint32_t i = 0; i < payloadCTP; i++) { + pldmask[12 + i] = 1; + } + // std::cout << (orbit0 != rdhOrbit) << " comp " << (mTFOrbit==rdhOrbit) << std::endl; + // if(orbit0 != rdhOrbit) { + if (hb) { + remnant = 0; + size_gbt = 0; + orbit0 = rdhOrbit; + // std::cout << "orbit0============>" << std::dec << orbit0 << " " << std::hex << orbit0 << std::endl; + } + // Create 80 bit words + gsl::span payload(it.data(), it.size()); + gbtword80_t gbtWord80; + gbtWord80.set(); + int wordCount = 0; + int wordSize = 10; + std::vector gbtwords80; + // mPadding = 0; + if (mPadding == 1) { + wordSize = 16; + } + // LOG(info) << ii << " payload size:" << payload.size(); + gbtword80_t bcmask = std::bitset<80>("111111111111"); + for (auto payloadWord : payload) { + int wc = wordCount % wordSize; + // LOG(info) << wordCount << ":" << wc << " payload:" << int(payloadWord); + if ((wc == 0) && (wordCount != 0)) { + if (gbtWord80.count() != 80) { + gbtwords80.push_back(gbtWord80); + } + gbtWord80.set(); + } + if (wc < 10) { + for (int i = 0; i < 8; i++) { + gbtWord80[wc * 8 + i] = bool(int(payloadWord) & (1 << i)); + } + } + wordCount++; + } + if ((gbtWord80.count() != 80) && (gbtWord80.count() > 0)) { + gbtwords80.push_back(gbtWord80); + } + // decode 80 bits payload + for (auto word : gbtwords80) { + std::vector diglets; + gbtword80_t gbtWord = word; + makeGBTWordInverse(diglets, gbtWord, remnant, size_gbt, payloadCTP); + for (auto diglet : diglets) { + int nbits = payloadCTP - 12; + for (int i = 0; i < nbits; i++) { + gbtword80_t mask = 1ull << i; + gbtword80_t pld = (diglet >> 12) & mask; + // LOG(info) << "diglet:" << diglet << " pld:" << pld; + if (pld.count() != 0) { + if (linkCRU == o2::ctp::GBTLinkIDIntRec) { + rates[i]++; + } else { + ratesC[i]++; + } + } + } + // LOG(debug) << "diglet:" << diglet << " " << (diglet & bcmask).to_ullong(); + } + } + if ((remnant.count() > 0) && stopBit) { + int nbits = payloadCTP - 12; + for (int i = 0; i < nbits; i++) { + gbtword80_t mask = 1ull << i; + gbtword80_t pld = (remnant >> 12) & mask; + // LOG(info) << "diglet:" << remnant << " pld:" << pld; + if (pld.count() != 0) { + if (linkCRU == o2::ctp::GBTLinkIDIntRec) { + rates[i]++; + } else { + ratesC[i]++; + } + } + } + remnant = 0; + } + } + // print max rates + std::map ratesmap; + std::map ratesmapC; + for (int i = 0; i < o2::ctp::CTP_NCLASSES; i++) { + if (rates[i]) { + ratesmap[rates[i]] = i; + } + if (ratesC[i]) { + ratesmapC[ratesC[i]] = i; + } + } + auto nhb = o2::base::GRPGeomHelper::getNHBFPerTF(); + std::string message = "Ringing inputs [MHz]:"; + for (auto const& r : boost::adaptors::reverse(ratesmap)) { + // LOG(error) << r.second; + message += " " + o2::ctp::CTPInputsConfiguration::getInputNameFromIndex(r.second + 1) + ":" + std::to_string(r.first / 32. / o2::constants::lhc::LHCOrbitMUS); + } + std::string messageC = "Ringing classes [MHz]:"; + for (auto const& r : boost::adaptors::reverse(ratesmapC)) { + messageC += " class" + std::to_string(r.second) + ":" + std::to_string(r.first / 32. / o2::constants::lhc::LHCOrbitMUS); + } + LOG(error) << messageC; + LOG(fatal) << message; + return 0; +} +// +int RawDataDecoder::decodeRaw(o2::framework::InputRecord& inputs, std::vector& filter, std::vector& digits, std::vector& lumiPointsHBF1) +{ + o2::pmr::vector pmrdigits; + int ret = decodeRaw(inputs, filter, pmrdigits, lumiPointsHBF1); + for (auto const d : pmrdigits) { + digits.push_back(d); + } + return ret; +} +// +// Not to be called with level LM +// Keeping shift in params if needed to be generalised +int RawDataDecoder::shiftNew(const o2::InteractionRecord& irin, uint32_t TFOrbit, std::bitset<48>& inpmask, int64_t shift, int level, std::map& digmap) +{ + // + if (irin.orbit > TFOrbit || irin.bc >= shift) { + auto lxmask = L0MASKInputs; + if (level == 1) { + lxmask = L1MASKInputs; + } + auto ir = irin - shift; // add L0 to prev digit + if (digmap.count(ir)) { + if ((digmap[ir].CTPInputMask & lxmask).count()) { + LOG(error) << " Overwriting LX ? X:" << level; + } + digmap[ir].CTPInputMask = digmap[ir].CTPInputMask | (inpmask & lxmask); + } else { + CTPDigit digit = {ir, inpmask & lxmask, 0}; + digmap[ir] = digit; + } + } else { + // LOG(info) << "LOST:" << irin << " shift:" << shift; + return 1; + ; + } + return 0; +} +// + +int RawDataDecoder::shiftInputs(std::map& digitsMap, o2::pmr::vector& digits, uint32_t TFOrbit, uint64_t trgclassmask) +{ + // int nClasswoInp = 0; // counting classes without input which should never happen + int lost = 0; + std::map digitsMapShifted; + auto L0shift = o2::ctp::TriggerOffsetsParam::Instance().LM_L0; + auto L1shift = L0shift + o2::ctp::TriggerOffsetsParam::Instance().L0_L1; + for (auto const& dig : digitsMap) { + auto inpmask = dig.second.CTPInputMask; + auto inpmaskLM = inpmask & LMMASKInputs; + auto inpmaskL0 = inpmask & L0MASKInputs; + auto inpmaskL1 = inpmask & L1MASKInputs; + int lm = inpmaskLM.count() > 0; + int l0 = inpmaskL0.count() > 0; + int l1 = inpmaskL1.count() > 0; + int lut = lm + (l0 << 1) + (l1 << 2); + // std::cout << "L0mask:" << L0MASKInputs << std::endl; + // std::cout << "L0:" << inpmaskL0 << std::endl; + // std::cout << "L1:" << inpmaskL1 << std::endl; + if (lut == 0 || lut == 1) { // no inps or LM + digitsMapShifted[dig.first] = dig.second; + } else if (lut == 2) { // L0 + lost += shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); + if (dig.second.CTPClassMask.count()) { + // LOG(error) << "Adding class mask without input ?"; + // This is not needed as it can happen; Full checj done below - see next LOG(error) + CTPDigit digi = {dig.first, 0, dig.second.CTPClassMask}; + digitsMapShifted[dig.first] = digi; + } + } else if (lut == 4) { // L1 + lost += shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + if (dig.second.CTPClassMask.count()) { + CTPDigit digi = {dig.first, 0, dig.second.CTPClassMask}; + digitsMapShifted[dig.first] = digi; + } + } else if (lut == 6) { // L0 and L1 + lost += shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); + lost += shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + if (dig.second.CTPClassMask.count()) { + CTPDigit digi = {dig.first, 0, dig.second.CTPClassMask}; + digitsMapShifted[dig.first] = digi; + } + } else if (lut == 3) { // LM and L0 + lost += shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); + CTPDigit digi = {dig.first, inpmask & (~L0MASKInputs), dig.second.CTPClassMask}; + // if LM level do not need to add class as LM is not shifted; + digitsMapShifted[dig.first] = digi; + } else if (lut == 5) { // LM and L1 + lost += shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + CTPDigit digi = {dig.first, inpmask & (~L1MASKInputs), dig.second.CTPClassMask}; + digitsMapShifted[dig.first] = digi; + } else if (lut == 7) { // LM and L0 and L1 + lost += shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); + lost += shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + CTPDigit digi = {dig.first, inpmaskLM, dig.second.CTPClassMask}; + digitsMapShifted[dig.first] = digi; + } else { + LOG(fatal) << "lut = " << lut; + } + } + for (auto const& dig : digitsMapShifted) { + digits.push_back(dig.second); + } + return lost; +} +// +int RawDataDecoder::checkReadoutConsistentncy(o2::pmr::vector& digits, uint64_t trgclassmask, uint64_t trgclassmaskNoTrgDet) +{ + LOG(debug) << "Checking readout"; + int ret = 0; + static int nerror = 0; + int32_t magicBC = o2::constants::lhc::LHCMaxBunches - o2::ctp::TriggerOffsetsParam::Instance().LM_L0 - o2::ctp::TriggerOffsetsParam::Instance().L0_L1_classes - 1; + for (auto const& digit : digits) { + // if class mask => inps + for (int i = 0; i < digit.CTPClassMask.size(); i++) { + bool trgcls = trgclassmask & (1ull << i); + if (digit.CTPClassMask[i] & trgcls) { + const CTPClass* cls = mCTPConfig.getCTPClassFromHWIndex(i); + if (cls == nullptr) { + if (nerror < mErrorMax) { + LOG(error) << "Class mask index not found in CTP config:" << i; + nerror++; + } + ret = 128; + continue; + } + mClassCountersA[i]++; + if (cls->descriptor == nullptr) { + continue; + } + uint64_t clsinpmask = cls->descriptor->getInputsMask(); + uint64_t diginpmask = digit.CTPInputMask.to_ullong(); + if (!((clsinpmask & diginpmask) == clsinpmask)) { + bool e = !(((digit.intRecord.bc == magicBC) || (digit.intRecord.bc == (magicBC + 1))) && (clsinpmask & L1MASKInputs.to_ullong())); + if (e) { + if (nerror < mErrorMax) { + LOG(error) << "Cls=>Inps: CTP class:" << cls->name << " inpmask:" << clsinpmask << " not compatible with inputs mask:" << diginpmask << " " << digit.intRecord; + nerror++; + } + mClassErrorsA[i]++; + ret = 128; + } + } + } + } + // if inps => class mask + for (auto const& cls : mCTPConfig.getCTPClasses()) { + // cls.printStream(std::cout); + if (cls.descriptor == nullptr) { + continue; + } + uint64_t clsinpmask = cls.descriptor->getInputsMask(); // class definition + uint64_t diginpmask = digit.CTPInputMask.to_ullong(); + uint64_t digclsmask = digit.CTPClassMask.to_ullong(); + if ((clsinpmask & diginpmask) == clsinpmask) { + if (cls.classMask & trgclassmask) { + mClassCountersB[cls.getIndex()]++; + if ((cls.classMask & digclsmask) == 0) { + int32_t BCShiftCorrection = -o2::ctp::TriggerOffsetsParam::Instance().customOffset[o2::detectors::DetID::CTP]; + int32_t offset = BCShiftCorrection + o2::ctp::TriggerOffsetsParam::Instance().LM_L0 + o2::ctp::TriggerOffsetsParam::Instance().L0_L1_classes - 1; + offset = o2::constants::lhc::LHCMaxBunches - offset; + if (digit.intRecord.bc < offset) { + if ((nerror < mErrorMax) && (cls.classMask & ~trgclassmaskNoTrgDet)) { + LOG(info) << "Inp=>Cls: CTP class:" << cls.name << " inpmask:" << clsinpmask << " cls mask:" << cls.classMask << " not found in digit:" << digit; + nerror++; + } + mClassErrorsB[cls.getIndex()]++; + ret = 256; + } else { + mLostDueToShiftCC++; + } + } + } + } + } + } + return ret; +} +// +int RawDataDecoder::setLumiInp(int lumiinp, std::string inp) +{ + // check if valid input + int index = o2::ctp::CTPInputsConfiguration::getInputIndexFromName(inp); + if (index == 0xff) { + LOG(fatal) << "CTP raw decoder: input index not found:" << inp; + return 0xff; + } + if (lumiinp == 1) { + mTVXMask.reset(); + mTVXMask[index - 1] = true; + } else { + mVBAMask.reset(); + mVBAMask[index - 1] = true; + } + return index; +} +// +int RawDataDecoder::init() +{ + return 0; +} diff --git a/Detectors/CTP/simulation/include/CTPSimulation/Digitizer.h b/Detectors/CTP/simulation/include/CTPSimulation/Digitizer.h index 68202c8d22996..7d29f2144c2b3 100644 --- a/Detectors/CTP/simulation/include/CTPSimulation/Digitizer.h +++ b/Detectors/CTP/simulation/include/CTPSimulation/Digitizer.h @@ -34,7 +34,10 @@ class Digitizer void setCCDBServer(const std::string& server) { mCCDBServer = server; } std::vector process(const gsl::span detinputs); void calculateClassMask(const std::bitset ctpinpmask, std::bitset& classmask); + void setCTPConfiguration(o2::ctp::CTPConfiguration* config); + o2::ctp::CTPConfiguration* getDefaultCTPConfiguration(); void init(); + private: // CTP configuration std::string mCCDBServer = o2::base::NameConf::getCCDBServer(); @@ -43,4 +46,4 @@ class Digitizer }; } // namespace ctp } // namespace o2 -#endif //ALICEO2_CTP_DIGITIZER_H +#endif // ALICEO2_CTP_DIGITIZER_H diff --git a/Detectors/CTP/simulation/src/Digitizer.cxx b/Detectors/CTP/simulation/src/Digitizer.cxx index dc84e1bee1982..b1d4ef40b7b0e 100644 --- a/Detectors/CTP/simulation/src/Digitizer.cxx +++ b/Detectors/CTP/simulation/src/Digitizer.cxx @@ -18,67 +18,97 @@ #include #include +#include + using namespace o2::ctp; ClassImp(Digitizer); // Trigger detector config needed here. std::vector Digitizer::process(const gsl::span detinputs) { + if (!mCTPConfiguration) { + setCTPConfiguration(getDefaultCTPConfiguration()); + } std::map> det2ctpinp = mCTPConfiguration->getDet2InputMap(); // To be taken from config database ? std::map detInputName2Mask = - {{"MVBA", 1}, {"MVIR", 0x10}, {"MVOR", 2}, {"MVNC", 4}, {"MVCH", 8}, {"MT0A", 1}, {"MT0C", 2}, {"MTVX", 0x10}, {"MTCE", 8}, {"MTSC", 0x4}, {"0U0A", 1}, {"0U0C", 2}, {"0UVX", 0x10}, {"0UCE", 8}, {"0USC", 0x4}}; + {{"MVBA", 1}, {"MVOR", 2}, {"MVNC", 4}, {"MVCH", 8}, {"MVIR", 0x10}, {"MT0A", 1}, {"MT0C", 2}, {"MTSC", 4}, {"MTCE", 8}, {"MTVX", 0x10}, {"0U0A", 1}, {"0U0C", 2}, {"0USC", 4}, {"0UCE", 8}, {"0UVX", 0x10}, {"EMBA", 0x1}, {"0EMC", 0x2}, {"0DMC", 0x4}}; + + // pre-sorting detector inputs per interaction record std::map> predigits; for (auto const& inp : detinputs) { predigits[inp.intRecord].push_back(&inp); } + std::vector digits; for (auto const& hits : predigits) { - CTPDigit data; - data.intRecord = hits.first; std::bitset inpmaskcoll = 0; + auto currentIR = hits.first; for (auto const inp : hits.second) { switch (inp->detector) { case o2::detectors::DetID::FT0: { // see dummy database above for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::FT0]) { uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } break; } case o2::detectors::DetID::FV0: { for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::FV0]) { uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } break; } case o2::detectors::DetID::FDD: { for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::FDD]) { uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } break; } case o2::detectors::DetID::EMC: { + // uint64_t inpmaskdebug = 1; + uint64_t inpmaskdebug = (inp->inputsMask).to_ullong(); + if (inpmaskdebug & detInputName2Mask["EMBA"]) { + // MB-accept must be treated separately, as it is not a CTP input + std::bitset emcMBaccept; + emcMBaccept.set(CTP_NINPUTS - 1, 1); + inpmaskcoll |= emcMBaccept; + } // else { // needs to be done always, remove else for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::EMC]) { - uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + uint64_t mask = inpmaskdebug & detInputName2Mask[ctpinp.name]; + // uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } + // } + // LOG(info) << "EMC input mask:" << inpmaskcoll << " with IR = " << currentIR.bc << ", orbit = " << currentIR.orbit; break; } case o2::detectors::DetID::PHS: { for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::PHS]) { uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } break; } case o2::detectors::DetID::ZDC: { for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::ZDC]) { uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } break; } @@ -87,11 +117,25 @@ std::vector Digitizer::process(const gsl::span LOG(error) << "CTP Digitizer: unknown detector:" << inp->detector; break; } + // inpmaskcoll.reset(); // debug + // inpmaskcoll[47] = 1; // debug + } // end loop over trigger input for this interaction + if (inpmaskcoll.to_ullong()) { + // we put the trigger only when non-trivial + std::bitset<64> classmask; + calculateClassMask(inpmaskcoll, classmask); + if (classmask.to_ulong() == 0) { + // No class accepted + continue; + } + CTPDigit data; + data.intRecord = hits.first; + data.CTPInputMask = inpmaskcoll; + data.CTPClassMask = classmask; + digits.emplace_back(data); + LOG(info) << "Trigger-Event " << data.intRecord.bc << " " << data.intRecord.orbit << " Input mask:" << inpmaskcoll << " with IR = " << data.intRecord.bc << ", orbit = " << data.intRecord.orbit; + // LOG(info) << "Trigger-Event " << data.intRecord.bc << " " << data.intRecord.orbit << " Class mask:" << data.CTPClassMask << " with IR = " << data.intRecord.bc << ", orbit = " << data.intRecord.orbit; } - LOG(info) << data.intRecord.bc << " " << data.intRecord.orbit << " Input mask:" << inpmaskcoll; - data.CTPInputMask = inpmaskcoll; - calculateClassMask(inpmaskcoll, data.CTPClassMask); - digits.emplace_back(data); } return std::move(digits); } @@ -99,12 +143,48 @@ void Digitizer::calculateClassMask(const std::bitset ctpinpmask, st { classmask = 0; for (auto const& tcl : mCTPConfiguration->getCTPClasses()) { - if (tcl.descriptor->getInputsMask() & ctpinpmask.to_ullong()) { - classmask |= (1 << tcl.classMask); + auto clustername = boost::algorithm::to_lower_copy(tcl.cluster->name); // make case-insensitive for string comparison + if (clustername == "emc") { + // check if Min Bias EMC class + bool tvxMBemc = tcl.name.find("C0TVX-B-NOPF-EMC") != std::string::npos; // 2023 + tvxMBemc |= tcl.name.find("C0TVX-A-NOPF-EMC") != std::string::npos; + tvxMBemc |= tcl.name.find("C0TVX-C-NOPF-EMC") != std::string::npos; + tvxMBemc |= tcl.name.find("C0TVX-E-NOPF-EMC") != std::string::npos; + if (clustername == "emc") { + tvxMBemc |= tcl.name.find("minbias_TVX_L0") != std::string::npos; // 2022 + } + // require real physics input in any case + if (tvxMBemc) { + // if the class is a min. bias class accept it only if the MB-accept bit is set in addition + // (fake trigger input) + if (ctpinpmask[CTP_NINPUTS - 1]) { + classmask |= tcl.classMask; + LOG(info) << "adding MBA:" << tcl.name; + } + } else if ((ctpinpmask.to_ullong() & tcl.descriptor->getInputsMask()) == tcl.descriptor->getInputsMask()) { + // EMCAL rare triggers - physical trigger input + // class identification can be handled like in the case of the other + // classes as EMCAL trigger input is required + LOG(info) << "adding EMCal rare trigger:" << tcl.name; + classmask |= tcl.classMask; + } + } else { + if (tcl.descriptor && ((ctpinpmask.to_ullong() & tcl.descriptor->getInputsMask()) == tcl.descriptor->getInputsMask())) { + classmask |= tcl.classMask; + } } } + LOG(info) << "input mask:" << ctpinpmask; + LOG(info) << "class mask:" << classmask; } -void Digitizer::init() + +void Digitizer::setCTPConfiguration(o2::ctp::CTPConfiguration* config) +{ + mCTPConfiguration = config; + LOG(info) << *mCTPConfiguration; +} + +o2::ctp::CTPConfiguration* Digitizer::getDefaultCTPConfiguration() { // CTP Configuration if (mCCDBServer.empty()) { @@ -114,9 +194,14 @@ void Digitizer::init() } auto& mgr = o2::ccdb::BasicCCDBManager::instance(); mgr.setURL(mCCDBServer); - map metadata = {}; + std::map metadata = {}; long timestamp = 1546300800000; - mCTPConfiguration = mgr.getSpecific(o2::ctp::CCDBPathCTPConfig, timestamp, metadata); - mCTPConfiguration->printStream(std::cout); + + auto config = mgr.getSpecific(o2::ctp::CCDBPathCTPConfig, timestamp, metadata); LOG(info) << " @@@ CTP Digitizer:: CCDB connected " << std::endl; + return config; +} + +void Digitizer::init() +{ } diff --git a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h index 4596fe12cb31d..dda45c9f11a34 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h @@ -28,12 +28,13 @@ namespace ctp class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final; + void updateTimeDependentParams(framework::ProcessingContext& pc); private: o2::ctp::CTFCoder mCTFCoder; @@ -41,7 +42,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h index 3a023ce2022dc..a63119264e071 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace ctp class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR, bool noLumi); + EntropyEncoderSpec(bool selIR, bool noLumi, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -43,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, bool noLumiInput = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, bool noLumiInput = false, const std::string& ctfdictOpt = "none"); } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h b/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h index 39cb398071b7b..3198e5c33e219 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h @@ -14,12 +14,12 @@ #include #include - #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include "Framework/WorkflowSpec.h" #include "DataFormatsCTP/Digits.h" #include "DataFormatsCTP/LumiInfo.h" -#include "DataFormatsCTP/TriggerOffsetsParam.h" +#include "CTPReconstruction/RawDataDecoder.h" namespace o2 { @@ -38,13 +38,12 @@ class RawDecoderSpec : public framework::Task /// \brief Constructor /// \param propagateMC If true the MCTruthContainer is propagated to the output RawDecoderSpec(bool digits, bool lumi) : mDoDigits(digits), mDoLumi(lumi) {} - /// \brief Destructor ~RawDecoderSpec() override = default; - /// \brief Initializing the RawDecoderSpec /// \param ctx Init context void init(framework::InitContext& ctx) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; /// \brief Run conversion of raw data to cells /// \param ctx Processing context /// @@ -52,20 +51,18 @@ class RawDecoderSpec : public framework::Task /// Input RawData: {"ROUT", "RAWDATA", 0, Lifetime::Timeframe} /// Output HW errors: {"CTP", "RAWHWERRORS", 0, Lifetime::Timeframe} -later void run(framework::ProcessingContext& ctx) final; - static void makeGBTWordInverse(std::vector& diglets, gbtword80_t& GBTWord, gbtword80_t& remnant, uint32_t& size_gbt, uint32_t Npld); - int addCTPDigit(uint32_t linkCRU, uint32_t triggerOrbit, gbtword80_t& diglet, gbtword80_t& pldmask, std::map& digits); - gbtword80_t subbitset(int pos1, int pos2, gbtword128_t& bs, int shift = 0); + void updateTimeDependentParams(framework::ProcessingContext& pc); protected: private: // for digits bool mDoDigits = true; - std::vector mOutputDigits; + o2::pmr::vector mOutputDigits; + int mMaxInputSize = 0; + bool mMaxInputSizeFatal = 0; // for lumi bool mDoLumi = true; // - gbtword80_t mTVXMask = 0x4; // TVX is 3rd input - gbtword80_t mVBAMask = 0x20; // VBA is 6 th input LumiInfo mOutputLumiInfo; bool mVerbose = false; uint64_t mCountsT = 0; @@ -73,11 +70,21 @@ class RawDecoderSpec : public framework::Task uint32_t mNTFToIntegrate = 1; uint32_t mNHBIntegratedT = 0; uint32_t mNHBIntegratedV = 0; - uint32_t mIRRejected = 0; - uint32_t mTCRRejected = 0; - bool mPadding = true; + bool mDecodeinputs = 0; std::deque mHistoryT; std::deque mHistoryV; + RawDataDecoder mDecoder; + // Errors + int mLostDueToShiftInps = 0; + int mErrorIR = 0; + int mErrorTCR = 0; + int mIRRejected = 0; + int mTCRRejected = 0; + std::array mClsEA{}; + std::array mClsEB{}; // from inputs + std::array mClsA{}; + std::array mClsB{}; // from inputs + bool mCheckConsistency = false; }; /// \brief Creating DataProcessorSpec for the CTP diff --git a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx index edce577599acf..0fa8fb0004e4c 100644 --- a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx @@ -24,13 +24,13 @@ namespace o2 { namespace ctp { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); mCTFCoder.setSupportBCShifts(true); + mCTFCoder.setDictBinding("ctfdict_CTP"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -43,6 +43,9 @@ void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matche void EntropyDecoderSpec::init(o2::framework::InitContext& ic) { mCTFCoder.init(ic); + bool decodeinps = !ic.options().get("ignore-ctpinputs-decoding-ctf"); + mCTFCoder.setDecodeInps(decodeinps); + LOG(info) << "Decode inputs:" << decodeinps; } void EntropyDecoderSpec::run(ProcessingContext& pc) @@ -51,11 +54,10 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc, true); - auto buff = pc.inputs().get>("ctf"); - + updateTimeDependentParams(pc); + auto buff = pc.inputs().get>("ctf_CTP"); auto& digits = pc.outputs().make>(OutputRef{"digits"}); - auto& lumi = pc.outputs().make(OutputRef{"lumi"}); + auto& lumi = pc.outputs().make(OutputRef{"CTPLumi"}); // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object if (buff.size()) { @@ -72,26 +74,43 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) LOGF(info, "CTP Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } +void EntropyDecoderSpec::updateTimeDependentParams(framework::ProcessingContext& pc) +{ + mCTFCoder.updateTimeDependentParams(pc, true); + if (pc.services().get().globalRunNumberChanged) { + const auto ctpcfg = pc.inputs().get("ctpconfig"); + if (mCTFCoder.getDecodeInps()) { + const auto ctpcfg = pc.inputs().get("ctpconfig"); + if (ctpcfg != nullptr) { + mCTFCoder.setCTPConfig(*ctpcfg); + LOG(info) << "ctpconfig for run done:" << mCTFCoder.getCTPConfig().getRunNumber(); + } + } + } +} -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "CTP", "DIGITS", 0, Lifetime::Timeframe}, - OutputSpec{{"lumi"}, "CTP", "LUMI", 0, Lifetime::Timeframe}, + OutputSpec{{"CTPLumi"}, "CTP", "LUMI", 0, Lifetime::Timeframe}, OutputSpec{{"ctfrep"}, "CTP", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "CTP", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); - inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); + inputs.emplace_back("ctf_CTP", "CTP", "CTFDATA", sspec, Lifetime::Timeframe); + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_CTP", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); + } + inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); + inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", 1)); return DataProcessorSpec{ "ctp-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ignore-ctpinputs-decoding-ctf", VariantType::Bool, false, {"Inputs alignment: false - CTF decoder - has to be compatible with reco: allowed options: 10,01,00"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx index ba091891fbd5c..902fe22dadcc9 100644 --- a/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace ctp { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, bool nolumi) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR), mNoLumi(nolumi) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, bool nolumi, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR), mNoLumi(nolumi) { mTimer.Stop(); mTimer.Reset(); @@ -50,11 +49,21 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) mTimer.Start(false); mCTFCoder.updateTimeDependentParams(pc, true); auto digits = pc.inputs().get>("digits"); + static LumiInfo lumiPrev; + const int maxDumRep = 5; + static int dumRep = 0; LumiInfo lumi{}; if (!mNoLumi) { - lumi = pc.inputs().get("lumi"); + if (pc.inputs().get>("CTPLumi").size() == sizeof(LumiInfo)) { + lumiPrev = lumi = pc.inputs().get("CTPLumi"); + } else { + if (dumRep < maxDumRep && lumiPrev.nHBFCounted == 0 && lumiPrev.nHBFCountedFV0 == 0) { + LOGP(alarm, "Previous TF lumi used to substitute dummy input is empty, warning {} of {}", ++dumRep, maxDumRep); + } + lumi = lumiPrev; + } } - auto& buffer = pc.outputs().make>(Output{"CTP", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"CTP", "CTFDATA", 0}); auto iosize = mCTFCoder.encode(buffer, digits, lumi); pc.outputs().snapshot({"ctfrep", 0}, iosize); mTimer.Stop(); @@ -67,14 +76,17 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR, bool nolumi) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, bool nolumi, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "CTP", "DIGITS", 0, Lifetime::Timeframe); if (!nolumi) { - inputs.emplace_back("lumi", "CTP", "LUMI", 0, Lifetime::Timeframe); + inputs.emplace_back("CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe); + } + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); } - inputs.emplace_back("ctfdict", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -82,12 +94,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR, bool nolumi) "ctp-entropy-encoder", inputs, Outputs{{"CTP", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "CTP", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR, nolumi)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, nolumi, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/src/RawDecoderSpec.cxx b/Detectors/CTP/workflow/src/RawDecoderSpec.cxx index cc965d05621ef..44c8a20cea129 100644 --- a/Detectors/CTP/workflow/src/RawDecoderSpec.cxx +++ b/Detectors/CTP/workflow/src/RawDecoderSpec.cxx @@ -11,44 +11,115 @@ #include #include -#include "CommonDataFormat/InteractionRecord.h" #include "Framework/InputRecordWalker.h" #include "Framework/DataRefUtils.h" -#include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamRegistry.h" #include "DetectorsRaw/RDHUtils.h" -#include "DPLUtils/DPLRawParser.h" #include "CTPWorkflow/RawDecoderSpec.h" #include "CommonUtils/VerbosityConfig.h" +#include "Framework/InputRecord.h" +#include "DataFormatsCTP/TriggerOffsetsParam.h" +#include "Framework/CCDBParamSpec.h" +#include "DataFormatsCTP/Configuration.h" using namespace o2::ctp::reco_workflow; void RawDecoderSpec::init(framework::InitContext& ctx) { + mCheckConsistency = ctx.options().get("check-consistency"); + mDecoder.setCheckConsistency(mCheckConsistency); + mDecodeinputs = ctx.options().get("ctpinputs-decoding"); + mDecoder.setDecodeInps(mDecodeinputs); mNTFToIntegrate = ctx.options().get("ntf-to-average"); mVerbose = ctx.options().get("use-verbose-mode"); - LOG(info) << "CTP reco init done"; + int maxerrors = ctx.options().get("print-errors-num"); + mDecoder.setVerbose(mVerbose); + mDecoder.setDoLumi(mDoLumi); + mDecoder.setDoDigits(mDoDigits); + mDecoder.setMAXErrors(maxerrors); + std::string lumiinp1 = ctx.options().get("lumi-inp1"); + std::string lumiinp2 = ctx.options().get("lumi-inp2"); + int inp1 = mDecoder.setLumiInp(1, lumiinp1); + int inp2 = mDecoder.setLumiInp(2, lumiinp2); + mOutputLumiInfo.inp1 = inp1; + mOutputLumiInfo.inp2 = inp2; + mMaxInputSize = ctx.options().get("max-input-size"); + mMaxInputSizeFatal = ctx.options().get("max-input-size-fatal"); + LOG(info) << "CTP reco init done. Inputs decoding here:" << mDecodeinputs << " DoLumi:" << mDoLumi << " DoDigits:" << mDoDigits << " NTF:" << mNTFToIntegrate << " Lumi inputs:" << lumiinp1 << ":" << inp1 << " " << lumiinp2 << ":" << inp2 << " Max errors:" << maxerrors << " Max input size:" << mMaxInputSize << " MaxInputSizeFatal:" << mMaxInputSizeFatal << " CheckConsistency:" << mCheckConsistency; + // mOutputLumiInfo.printInputs(); +} +void RawDecoderSpec::endOfStream(framework::EndOfStreamContext& ec) +{ + auto clsEA = mDecoder.getClassErrorsA(); + auto clsEB = mDecoder.getClassErrorsB(); + auto cntCA = mDecoder.getClassCountersA(); + auto cntCB = mDecoder.getClassCountersB(); + int totClasses = 0; + for (int i = 0; i < o2::ctp::CTP_NCLASSES; i++) { + mClsEA[i] += clsEA[i]; + mClsEB[i] += clsEB[i]; + mClsA[i] += cntCA[i]; + mClsB[i] += cntCB[i]; + totClasses += cntCA[i]; + } + auto& TFOrbits = mDecoder.getTFOrbits(); + std::sort(TFOrbits.begin(), TFOrbits.end()); + size_t l = TFOrbits.size(); + uint32_t o0 = 0; + if (l) { + o0 = TFOrbits[0]; + } + int nmiss = 0; + int nprt = 0; + std::cout << "Missing orbits:"; + for (int i = 1; i < l; i++) { + if ((TFOrbits[i] - o0) > 0x20) { + if (nprt < 20) { + std::cout << " " << o0 << "-" << TFOrbits[i]; + } + nmiss += (TFOrbits[i] - o0) / 0x20; + nprt++; + } + o0 = TFOrbits[i]; + } + std::cout << std::endl; + LOG(info) << "Number of non continous TF:" << nmiss << std::endl; + LOG(info) << "Lost in shiftInputs:" << mLostDueToShiftInps; + LOG(info) << "Lost in addDigit Inputs:" << mIRRejected << " Classes:" << mTCRRejected; + if (mErrorIR || mErrorTCR) { + LOG(error) << "# of IR errors:" << mErrorIR << " TCR errors:" << mErrorTCR << std::endl; + } + if (mCheckConsistency) { + LOG(info) << "Lost due to the shift Consistency Checker:" << mDecoder.getLostDueToShiftCls(); + LOG(info) << "Total classes:" << totClasses; + auto ctpcfg = mDecoder.getCTPConfig(); + for (int i = 0; i < o2::ctp::CTP_NCLASSES; i++) { + std::string name = ctpcfg.getClassNameFromIndex(i); + if (mClsEA[i]) { + LOG(error) << " Class without inputs:"; + } + LOG(important) << "CLASS:" << name << ":" << i << " Cls=>Inp:" << mClsA[i] << " Inp=>Cls:" << mClsB[i] << " ErrorsCls=>Inps:" << mClsEA[i] << " MissingInps=>Cls:" << mClsEB[i]; + } + } } - void RawDecoderSpec::run(framework::ProcessingContext& ctx) { + updateTimeDependentParams(ctx); mOutputDigits.clear(); std::map digits; using InputSpec = o2::framework::InputSpec; using ConcreteDataTypeMatcher = o2::framework::ConcreteDataTypeMatcher; using Lifetime = o2::framework::Lifetime; - // setUpDummyLink auto& inputs = ctx.inputs(); auto dummyOutput = [&ctx, this]() { if (this->mDoDigits) { - ctx.outputs().snapshot(o2::framework::Output{"CTP", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, this->mOutputDigits); + ctx.outputs().snapshot(o2::framework::Output{"CTP", "DIGITS", 0}, this->mOutputDigits); } if (this->mDoLumi) { - ctx.outputs().snapshot(o2::framework::Output{"CTP", "LUMI", 0, o2::framework::Lifetime::Timeframe}, this->mOutputLumiInfo); + ctx.outputs().snapshot(o2::framework::Output{"CTP", "LUMI", 0}, this->mOutputLumiInfo); } }; - // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow { @@ -71,151 +142,47 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) contDeadBeef = 0; // if good data, reset the counter } // - std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{"CTP", "RAWDATA"}, Lifetime::Timeframe}}; - o2::framework::DPLRawParser parser(ctx.inputs(), filter); std::vector lumiPointsHBF1; - uint64_t countsMBT = 0; - uint64_t countsMBV = 0; - uint32_t payloadCTP; - uint32_t orbit0 = 0; - bool first = true; - gbtword80_t remnant = 0; - uint32_t size_gbt = 0; - for (auto it = parser.begin(); it != parser.end(); ++it) { - const o2::header::RDHAny* rdh = nullptr; - try { - rdh = reinterpret_cast(it.raw()); - mPadding = (o2::raw::RDHUtils::getDataFormat(rdh) == 0); - } catch (std::exception& e) { - LOG(error) << "Failed to extract RDH, abandoning TF sending dummy output, exception was: " << e.what(); - dummyOutput(); - return; - } - auto triggerOrbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); - uint32_t stopBit = o2::raw::RDHUtils::getStop(rdh); - uint32_t packetCounter = o2::raw::RDHUtils::getPageCounter(rdh); - uint32_t version = o2::raw::RDHUtils::getVersion(rdh); - // LOG(info) << "RDH version:" << version << " Padding:" << mPadding; - // std::cout << "==================>" << std::hex << triggerOrbit << std::endl; - if (first) { - orbit0 = triggerOrbit; - first = false; - } - auto feeID = o2::raw::RDHUtils::getFEEID(rdh); // 0 = IR, 1 = TCR - auto linkCRU = (feeID & 0xf00) >> 8; - if (linkCRU == o2::ctp::GBTLinkIDIntRec) { - payloadCTP = o2::ctp::NIntRecPayload; - } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { - payloadCTP = o2::ctp::NClassPayload; - if (!mDoDigits) { // Do not do TCR if only lumi - continue; - } - } else { - LOG(error) << "Unxpected CTP CRU link:" << linkCRU; - } - LOG(debug) << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << triggerOrbit << " stopbit:" << stopBit << " packet:" << packetCounter; - // LOG(info) << "remnant :" << remnant.count(); - gbtword80_t pldmask = 0; - for (uint32_t i = 0; i < payloadCTP; i++) { - pldmask[12 + i] = 1; - } - // TF in 128 bits words - if (orbit0 != triggerOrbit) { - if (mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) { // create lumi per HB - lumiPointsHBF1.emplace_back(LumiInfo{triggerOrbit, 0, 0, countsMBT, countsMBV}); - countsMBT = 0; - countsMBV = 0; - } - remnant = 0; - size_gbt = 0; - orbit0 = triggerOrbit; - } - // Create 80 bit words - gsl::span payload(it.data(), it.size()); - gbtword80_t gbtWord80; - gbtWord80.set(); - int wordCount = 0; - int wordSize = 10; - std::vector gbtwords80; - // mPadding = 0; - if (mPadding == 1) { - wordSize = 16; - } - // LOG(info) << "payload size:" << payload.size(); - for (auto payloadWord : payload) { - int wc = wordCount % wordSize; - // LOG(info) << wordCount << ":" << wc << " payload:" << int(payloadWord); - if ((wc == 0) && (wordCount != 0)) { - if (gbtWord80.count() != 80) { - gbtwords80.push_back(gbtWord80); - LOG(debug) << "w80:" << gbtWord80; - } - gbtWord80.set(); - } - if (wc < 10) { - for (int i = 0; i < 8; i++) { - gbtWord80[wc * 8 + i] = bool(int(payloadWord) & (1 << i)); - } - } - wordCount++; - } - if ((gbtWord80.count() != 80) && (gbtWord80.count() > 0)) { - gbtwords80.push_back(gbtWord80); - LOG(debug) << "w80l:" << gbtWord80; - } - // decode 80 bits payload - gbtword80_t bcmask = std::bitset<80>("111111111111"); - for (auto word : gbtwords80) { - std::vector diglets; - gbtword80_t gbtWord = word; - makeGBTWordInverse(diglets, gbtWord, remnant, size_gbt, payloadCTP); - for (auto diglet : diglets) { - if (mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) { - gbtword80_t pld = (diglet >> 12) & mTVXMask; - if (pld.count() != 0) { - countsMBT++; - } - pld = (diglet >> 12) & mVBAMask; - if (pld.count() != 0) { - countsMBV++; - } - } - if (!mDoDigits) { - continue; - } - LOG(debug) << "diglet:" << diglet << " " << (diglet & bcmask).to_ullong(); - addCTPDigit(linkCRU, triggerOrbit, diglet, pldmask, digits); - } + std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{"CTP", "RAWDATA"}, Lifetime::Timeframe}}; + bool fatal_flag = 0; + if (mMaxInputSize > 0) { + size_t payloadSize = 0; + for (const auto& ref : o2::framework::InputRecordWalker(inputs, filter)) { + const auto dh = o2::framework::DataRefUtils::getHeader(ref); + payloadSize += o2::framework::DataRefUtils::getPayloadSize(ref); } - // if ((remnant.count() > 0) && stopBit) { - if (remnant.count() > 0) { - if (mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) { - gbtword80_t pld = (remnant >> 12) & mTVXMask; - if (pld.count() != 0) { - countsMBT++; - } - pld = (remnant >> 12) & mVBAMask; - if (pld.count() != 0) { - countsMBV++; - } - } - if (!mDoDigits) { - continue; + if (payloadSize > (size_t)mMaxInputSize) { + if (mMaxInputSizeFatal) { + fatal_flag = 1; + LOG(error) << "Input data size bigger than threshold: " << mMaxInputSize << " < " << payloadSize << " decoding TF and exiting."; + // LOG(fatal) << "Input data size:" << payloadSize; - fatal issued in decoder + } else { + LOG(error) << "Input data size:" << payloadSize << " sending dummy output"; + dummyOutput(); + return; } - addCTPDigit(linkCRU, triggerOrbit, remnant, pldmask, digits); - LOG(debug) << "diglet:" << remnant << " " << (remnant & bcmask).to_ullong(); - remnant = 0; } } + int ret = 0; + if (fatal_flag) { + ret = mDecoder.decodeRawFatal(inputs, filter); + } else { + ret = mDecoder.decodeRaw(inputs, filter, mOutputDigits, lumiPointsHBF1); + } + if (ret == 1) { + dummyOutput(); + return; + } if (mDoDigits) { - for (auto const digmap : digits) { - mOutputDigits.push_back(digmap.second); - } - LOG(info) << "[CTPRawToDigitConverter - run] Writing " << mOutputDigits.size() << " digits. IR rejected:" << mIRRejected << " TCR rejected:" << mTCRRejected; - ctx.outputs().snapshot(o2::framework::Output{"CTP", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mOutputDigits); + LOG(info) << "[CTPRawToDigitConverter - run] Writing " << mOutputDigits.size() << " digits. IR rejected:" << mDecoder.getIRRejected() << " TCR rejected:" << mDecoder.getTCRRejected(); + ctx.outputs().snapshot(o2::framework::Output{"CTP", "DIGITS", 0}, mOutputDigits); + mLostDueToShiftInps += mDecoder.getLostDueToShiftInp(); + mErrorIR += mDecoder.getErrorIR(); + mErrorTCR += mDecoder.getErrorTCR(); + mIRRejected += mDecoder.getIRRejected(); + mTCRRejected += mDecoder.getTCRRejected(); } if (mDoLumi) { - lumiPointsHBF1.emplace_back(LumiInfo{orbit0, 0, 0, countsMBT, countsMBV}); uint32_t tfCountsT = 0; uint32_t tfCountsV = 0; for (auto const& lp : lumiPointsHBF1) { @@ -246,115 +213,16 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) mOutputLumiInfo.orbit = lumiPointsHBF1[0].orbit; } mOutputLumiInfo.counts = mCountsT; + mOutputLumiInfo.countsFV0 = mCountsV; mOutputLumiInfo.nHBFCounted = mNHBIntegratedT; mOutputLumiInfo.nHBFCountedFV0 = mNHBIntegratedV; if (mVerbose) { - LOGP(info, "Orbit {}: {}/{} counts T/V in {}/{} HBFs -> lumiT = {:.3e}+-{:.3e} lumiV = {:.3e}+-{:.3e}", mOutputLumiInfo.orbit, mCountsT, mCountsV, mNHBIntegratedT, mNHBIntegratedV, mOutputLumiInfo.getLumi(), mOutputLumiInfo.getLumiError(), mOutputLumiInfo.getLumiFV0(), mOutputLumiInfo.getLumiFV0Error()); - } - ctx.outputs().snapshot(o2::framework::Output{"CTP", "LUMI", 0, o2::framework::Lifetime::Timeframe}, mOutputLumiInfo); - } -} - -// Inverse of Digits2Raw::makeGBTWord -void RawDecoderSpec::makeGBTWordInverse(std::vector& diglets, gbtword80_t& GBTWord, gbtword80_t& remnant, uint32_t& size_gbt, uint32_t Npld) -{ - gbtword80_t diglet = remnant; - uint32_t i = 0; - while (i < (NGBT - Npld)) { - std::bitset masksize = 0; - for (uint32_t j = 0; j < (Npld - size_gbt); j++) { - masksize[j] = 1; - } - diglet |= (GBTWord & masksize) << (size_gbt); - diglets.push_back(diglet); - diglet = 0; - i += Npld - size_gbt; - GBTWord = GBTWord >> (Npld - size_gbt); - size_gbt = 0; - } - size_gbt = NGBT - i; - remnant = GBTWord; -} -int RawDecoderSpec::addCTPDigit(uint32_t linkCRU, uint32_t triggerOrbit, gbtword80_t& diglet, gbtword80_t& pldmask, std::map& digits) -{ - gbtword80_t pld = (diglet & pldmask); - if (pld.count() == 0) { - return 0; - } - pld >>= 12; - CTPDigit digit; - const gbtword80_t bcidmask = 0xfff; - uint16_t bcid = (diglet & bcidmask).to_ulong(); - LOG(debug) << bcid << " pld:" << pld; - o2::InteractionRecord ir = {bcid, triggerOrbit}; - int32_t BCShiftCorrection = o2::ctp::TriggerOffsetsParam::Instance().customOffset[o2::detectors::DetID::CTP]; - if (linkCRU == o2::ctp::GBTLinkIDIntRec) { - LOG(debug) << "InputMaskCount:" << digits[ir].CTPInputMask.count(); - LOG(debug) << "ir ir ori:" << ir; - if ((int32_t)ir.bc < BCShiftCorrection) { - // LOG(warning) << "Loosing ir:" << ir; - mIRRejected++; - return 0; - } - ir -= BCShiftCorrection; - LOG(debug) << "ir ir corrected:" << ir; - digit.intRecord = ir; - if (digits.count(ir) == 0) { - digit.setInputMask(pld); - digits[ir] = digit; - LOG(debug) << bcid << " inputs case 0 bcid orbit " << triggerOrbit << " pld:" << pld; - } else if (digits.count(ir) == 1) { - if (digits[ir].CTPInputMask.count() == 0) { - digits[ir].setInputMask(pld); - LOG(debug) << bcid << " inputs bcid vase 1 orbit " << triggerOrbit << " pld:" << pld; - } else { - LOG(error) << "Two CTP IRs with the same timestamp:" << ir.bc << " " << ir.orbit; - } - } else { - LOG(error) << "Two digits with the same rimestamp:" << ir.bc << " " << ir.orbit; - } - } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { - int32_t offset = BCShiftCorrection + o2::ctp::TriggerOffsetsParam::Instance().LM_L0 + o2::ctp::TriggerOffsetsParam::Instance().L0_L1 - 1; - LOG(debug) << "tcr ir ori:" << ir; - // if(0) { - if ((int32_t)ir.bc < offset) { - // LOG(warning) << "Loosing tclass:" << ir; - mTCRRejected++; - return 0; - } - ir -= offset; - LOG(debug) << "tcr ir corrected:" << ir; - digit.intRecord = ir; - if (digits.count(ir) == 0) { - digit.setClassMask(pld); - digits[ir] = digit; - LOG(debug) << bcid << " class bcid case 0 orbit " << triggerOrbit << " pld:" << pld; - } else if (digits.count(ir) == 1) { - if (digits[ir].CTPClassMask.count() == 0) { - digits[ir].setClassMask(pld); - LOG(debug) << bcid << " class bcid case 1 orbit " << triggerOrbit << " pld:" << pld; - } else { - LOG(error) << "Two CTP Class masks for same timestamp"; - } - } else { + mOutputLumiInfo.printInputs(); + LOGP(info, "Orbit {}: {}/{} counts inp1/inp2 in {}/{} HBFs -> lumi_inp1 = {:.3e}+-{:.3e} lumi_inp2 = {:.3e}+-{:.3e}", mOutputLumiInfo.orbit, mCountsT, mCountsV, mNHBIntegratedT, mNHBIntegratedV, mOutputLumiInfo.getLumi(), mOutputLumiInfo.getLumiError(), mOutputLumiInfo.getLumiFV0(), mOutputLumiInfo.getLumiFV0Error()); } - } else { - LOG(error) << "Unxpected CTP CRU link:" << linkCRU; + ctx.outputs().snapshot(o2::framework::Output{"CTP", "LUMI", 0}, mOutputLumiInfo); } - return 0; -} -// -// 000 pos2 00000 pos1 0000 -std::bitset<80> RawDecoderSpec::subbitset(int pos, int len, gbtword128_t& bs, int shift) -{ - gbtword80_t word; - std::string str = bs.to_string(); - pos = 128 - pos - len; - str = str.substr(pos, len); - word = std::bitset<80>(str); - word = word << shift; - return word; } o2::framework::DataProcessorSpec o2::ctp::reco_workflow::getRawDecoderSpec(bool askDISTSTF, bool digits, bool lumi) { @@ -362,12 +230,14 @@ o2::framework::DataProcessorSpec o2::ctp::reco_workflow::getRawDecoderSpec(bool throw std::runtime_error("all outputs were disabled"); } std::vector inputs; - inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"CTP", "RAWDATA"}, o2::framework::Lifetime::Optional); + inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"CTP", "RAWDATA"}, o2::framework::Lifetime::Timeframe); if (askDISTSTF) { inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe); } std::vector outputs; + inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("CTP/Config/Config", 1)); + inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("CTP/Config/TriggerOffsets")); if (digits) { outputs.emplace_back("CTP", "DIGITS", 0, o2::framework::Lifetime::Timeframe); } @@ -381,5 +251,25 @@ o2::framework::DataProcessorSpec o2::ctp::reco_workflow::getRawDecoderSpec(bool o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(digits, lumi)}, o2::framework::Options{ {"ntf-to-average", o2::framework::VariantType::Int, 90, {"Time interval for averaging luminosity in units of TF"}}, - {"use-verbose-mode", o2::framework::VariantType::Bool, false, {"Verbose logging"}}}}; + {"print-errors-num", o2::framework::VariantType::Int, 3, {"Max number of errors to print"}}, + {"lumi-inp1", o2::framework::VariantType::String, "TVX", {"The first input used for online lumi. Name in capital."}}, + {"lumi-inp2", o2::framework::VariantType::String, "VBA", {"The second input used for online lumi. Name in capital."}}, + {"use-verbose-mode", o2::framework::VariantType::Bool, false, {"Verbose logging"}}, + {"max-input-size", o2::framework::VariantType::Int, 0, {"Do not process input if bigger than max size, 0 - do not check"}}, + {"max-input-size-fatal", o2::framework::VariantType::Bool, false, {"If true issue fatal error otherwise error only"}}, + {"check-consistency", o2::framework::VariantType::Bool, false, {"If true checks digits consistency using ctp config"}}, + {"ctpinputs-decoding", o2::framework::VariantType::Bool, false, {"Inputs alignment: true - raw decoder - has to be compatible with CTF decoder: allowed options: 10,01,00"}}}}; +} +void RawDecoderSpec::updateTimeDependentParams(framework::ProcessingContext& pc) +{ + if (pc.services().get().globalRunNumberChanged) { + pc.inputs().get("trigoffset"); + const auto& trigOffsParam = o2::ctp::TriggerOffsetsParam::Instance(); + LOG(info) << "updateing TroggerOffsetsParam: inputs L0_L1:" << trigOffsParam.L0_L1 << " classes L0_L1:" << trigOffsParam.L0_L1_classes; + const auto ctpcfg = pc.inputs().get("ctpconfig"); + if (ctpcfg != nullptr) { + mDecoder.setCTPConfig(*ctpcfg); + LOG(info) << "ctpconfig for run done:" << mDecoder.getCTPConfig().getRunNumber(); + } + } } diff --git a/Detectors/CTP/workflow/src/ctp-raw-decoder.cxx b/Detectors/CTP/workflow/src/ctp-raw-decoder.cxx index c024ac1c954f8..31b9647972c79 100644 --- a/Detectors/CTP/workflow/src/ctp-raw-decoder.cxx +++ b/Detectors/CTP/workflow/src/ctp-raw-decoder.cxx @@ -50,7 +50,7 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co !cfgc.options().get("no-digits"), !cfgc.options().get("no-lumi"))); if (!cfgc.options().get("disable-root-output")) { - specs.emplace_back(o2::ctp::getDigitWriterSpec(true)); + specs.emplace_back(o2::ctp::getDigitWriterSpec(!cfgc.options().get("no-lumi"))); } return specs; } diff --git a/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx index 1fcaa89be9888..c2b324a4b3bfa 100644 --- a/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"no-lumi-input", VariantType::Bool, false, {"Lumi info not available"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; @@ -38,6 +39,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::ctp::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("no-lumi-input"))); + wf.emplace_back(o2::ctp::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("no-lumi-input"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/CTP/workflowIO/CMakeLists.txt b/Detectors/CTP/workflowIO/CMakeLists.txt index 1fc171d536d55..e1db635515cac 100644 --- a/Detectors/CTP/workflowIO/CMakeLists.txt +++ b/Detectors/CTP/workflowIO/CMakeLists.txt @@ -1,23 +1,29 @@ -# Copyright CERN and copyright holders of ALICE O2. This software is distributed -# under the terms of the GNU General Public License v3 (GPL Version 3), copied -# verbatim in the file "COPYING". +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. # -# See http://alice-o2.web.cern.ch/license for full licensing information. +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". # # In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization or -# submit itself to any jurisdiction. +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. o2_add_library(CTPWorkflowIO - SOURCES src/DigitReaderSpec.cxx - src/DigitWriterSpec.cxx - PUBLIC_LINK_LIBRARIES O2::Framework - O2::DataFormatsCTP - O2::DPLUtils - O2::DetectorsRaw - O2::Algorithm) + SOURCES src/DigitReaderSpec.cxx + src/DigitWriterSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsCTP + O2::DPLUtils + O2::DetectorsRaw + O2::Algorithm) o2_add_executable(digit-writer - COMPONENT_NAME ctp - SOURCES src/digits-writer-workflow.cxx - PUBLIC_LINK_LIBRARIES O2::CTPWorkflowIO) + COMPONENT_NAME ctp + SOURCES src/digits-writer-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CTPWorkflowIO) + +o2_add_executable(digit-reader + COMPONENT_NAME ctp + SOURCES src/digits-reader-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CTPWorkflowIO) \ No newline at end of file diff --git a/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx b/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx index 59f608bef2efb..81e6f53f42dcc 100644 --- a/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx +++ b/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx @@ -17,7 +17,10 @@ #include "DataFormatsCTP/LumiInfo.h" #include "Headers/DataHeader.h" #include "DetectorsCommonDataFormats/DetID.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "CommonUtils/NameConf.h" +#include "CommonUtils/IRFrameSelector.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "Framework/ControlService.h" @@ -50,6 +53,7 @@ class DigitReader : public Task std::unique_ptr mTree; bool mUseMC = false; // use MC truth + bool mUseIRFrames = false; // selected IRFrames mode std::string mDigTreeName = "o2sim"; std::string mDigitBranchName = "CTPDigits"; std::string mLumiBranchName = "CTPLumi"; @@ -58,7 +62,7 @@ class DigitReader : public Task DigitReader::DigitReader(bool useMC) { if (useMC) { - LOG(info) << "CTP does not support MC truth at the moment"; + LOG(info) << "CTP : truth = data as CTP inputs are already digital"; } } @@ -66,21 +70,70 @@ void DigitReader::init(InitContext& ic) { auto filename = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), ic.options().get("ctp-digit-infile")); + if (ic.options().hasOption("ignore-irframes") && !ic.options().get("ignore-irframes")) { + mUseIRFrames = true; + } connectTree(filename); } void DigitReader::run(ProcessingContext& pc) { - auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen - - mTree->GetEntry(ent); - LOG(info) << "DigitReader pushes " << mDigits.size() << " digits at entry " << ent; - pc.outputs().snapshot(Output{"CTP", "DIGITS", 0, Lifetime::Timeframe}, mDigits); - pc.outputs().snapshot(Output{"CTP", "LUMI", 0, Lifetime::Timeframe}, mLumi); - if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { - pc.services().get().endOfStream(); - pc.services().get().readyToQuit(QuitRequest::Me); + gsl::span irFrames{}; + // LOG(info) << "Using IRs:" << mUseIRFrames; + if (mUseIRFrames) { + irFrames = pc.inputs().get>("driverInfo"); + } + auto ent = mTree->GetReadEntry(); + if (!mUseIRFrames) { + ent++; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "DigitReader pushes " << mDigits.size() << " digits at entry " << ent; + pc.outputs().snapshot(Output{"CTP", "DIGITS", 0}, mDigits); + pc.outputs().snapshot(Output{"CTP", "LUMI", 0}, mLumi); + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } + } else { + std::vector digitSel; + if (irFrames.size()) { // we assume the IRFrames are in the increasing order + if (ent < 0) { + ent++; + } + o2::utils::IRFrameSelector irfSel; + // MC digits are already aligned + irfSel.setSelectedIRFrames(irFrames, 0, 0, 0, true); + const auto irMin = irfSel.getIRFrames().front().getMin(); // use processed IRframes for rough comparisons (possible shift!) + const auto irMax = irfSel.getIRFrames().back().getMax(); + LOGP(info, "Selecting IRFrame {}-{}", irMin.asString(), irMax.asString()); + while (ent < mTree->GetEntries()) { + if (ent > mTree->GetReadEntry()) { + mTree->GetEntry(ent); + } + if (mDigits.front().intRecord <= irMax && mDigits.back().intRecord >= irMin) { // THere is overlap + for (int i = 0; i < (int)mDigits.size(); i++) { + const auto& dig = mDigits[i]; + // if(irfSel.check(dig.intRecord)) { // adding selected digit + if (dig.intRecord >= irMin && dig.intRecord <= irMax) { + digitSel.push_back(dig); + LOG(info) << "adding:" << dig.intRecord << " ent:" << ent; + } + } + } + if (mDigits.back().intRecord < irMax) { // need to check the next entry + ent++; + continue; + } + break; // push collected data + } + } + pc.outputs().snapshot(Output{"CTP", "DIGITS", 0}, digitSel); + pc.outputs().snapshot(Output{"CTP", "LUMI", 0}, mLumi); // add full lumi for this TF + if (!irFrames.size() || irFrames.back().isLast()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } } } diff --git a/Detectors/CTP/workflowIO/src/DigitWriterSpec.cxx b/Detectors/CTP/workflowIO/src/DigitWriterSpec.cxx index 10318698309c7..a65b94d16f43c 100644 --- a/Detectors/CTP/workflowIO/src/DigitWriterSpec.cxx +++ b/Detectors/CTP/workflowIO/src/DigitWriterSpec.cxx @@ -37,7 +37,7 @@ framework::DataProcessorSpec getDigitWriterSpec(bool raw) return MakeRootTreeWriterSpec("ctp-digit-writer-dec", "ctpdigits.root", MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with CTP digits/Lumi"}, BranchDefinition>{InputSpec{"digit", "CTP", "DIGITS", 0}, "CTPDigits", logger}, - BranchDefinition{InputSpec{"lumi", "CTP", "LUMI", 0}, "CTPLumi"})(); + BranchDefinition{InputSpec{"CTPLumi", "CTP", "LUMI", 0}, "CTPLumi"})(); } // MC digits case, no lumi available return MakeRootTreeWriterSpec("ctp-digit-writer", "ctpdigits.root", diff --git a/Detectors/CTP/workflowIO/src/digits-reader-workflow.cxx b/Detectors/CTP/workflowIO/src/digits-reader-workflow.cxx new file mode 100644 index 0000000000000..fe9104fcf9978 --- /dev/null +++ b/Detectors/CTP/workflowIO/src/digits-reader-workflow.cxx @@ -0,0 +1,48 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"inputfile", o2::framework::VariantType::String, "ctpdigits.root", {"File with CTP digits"}}, + {"disable-mc", o2::framework::VariantType::Bool, false, {"Do not reed MC truth branch"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" +#include "Framework/WorkflowSpec.h" +#include "CTPWorkflowIO/DigitReaderSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + WorkflowSpec specs; + specs.emplace_back(o2::ctp::getDigitsReaderSpec(!configcontext.options().get("disable-mc"), configcontext.options().get("inputfile"))); + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + return std::move(specs); +} diff --git a/Detectors/CTP/workflowScalers/CMakeLists.txt b/Detectors/CTP/workflowScalers/CMakeLists.txt index 716da7e596482..f02a7f33e2abd 100644 --- a/Detectors/CTP/workflowScalers/CMakeLists.txt +++ b/Detectors/CTP/workflowScalers/CMakeLists.txt @@ -1,12 +1,44 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. +o2_add_library(CTPWorkflowScalers + SOURCES src/ctpCCDBManager.cxx + SOURCES src/RunManager.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsCTP + AliceO2::BookkeepingApi) +o2_target_root_dictionary(CTPWorkflowScalers HEADERS + include/CTPWorkflowScalers/ctpCCDBManager.h) o2_add_executable( proxy COMPONENT_NAME ctp SOURCES src/ctp-proxy.cxx PUBLIC_LINK_LIBRARIES O2::DCStestWorkflow - O2::DataFormatsCTP) + O2::CTPWorkflowScalers) o2_add_executable( qc-proxy COMPONENT_NAME ctp SOURCES src/ctp-qc-proxy.cxx PUBLIC_LINK_LIBRARIES O2::DCStestWorkflow O2::DataFormatsCTP) +o2_add_executable( + ccdb-orbit + COMPONENT_NAME ctp + SOURCES src/ctp-ccdb-orbit.cxx + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + Boost::program_options) +o2_add_executable( + bk-write + COMPONENT_NAME ctp + SOURCES src/ctp-bk-write.cxx + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CTPWorkflowScalers + AliceO2::BookkeepingApi + Boost::program_options) diff --git a/Detectors/CTP/workflowScalers/README.md b/Detectors/CTP/workflowScalers/README.md index 4b109f779dc1d..df8df2c55cc44 100644 --- a/Detectors/CTP/workflowScalers/README.md +++ b/Detectors/CTP/workflowScalers/README.md @@ -1,3 +1,7 @@ + + How to generate c++ proto files ? On the computer where project https://gitlab.cern.ch/aliceCTP3/ctp3-ipbus/-/tree/master diff --git a/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/RunManager.h b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/RunManager.h new file mode 100644 index 0000000000000..6d2172e3da165 --- /dev/null +++ b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/RunManager.h @@ -0,0 +1,79 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RunManager.h +/// \brief Managing runs for config and scalers +/// \author Roman Lietava +#ifndef _CTP_RUNMANAGER_H_ +#define _CTP_RUNMANAGER_H_ +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "DataFormatsCTP/Configuration.h" +#include "BookkeepingApi/BkpClientFactory.h" +#include "BookkeepingApi/BkpClient.h" + +using namespace o2::bkp::api; +namespace o2 +{ +namespace ctp +{ +typedef std::map> counters_t; +typedef std::map> counters64_t; +struct CTPActiveRun { + CTPActiveRun() = default; + long timeStart; + long timeStop; + CTPConfiguration cfg; + CTPRunScalers scalers; + void initBK(); + int send2BK(std::unique_ptr& BKClient, size_t ts, bool start); + // + counters_t cnts0; // first counters in run + counters_t cntslast0; // last minus one read counters needed for overflow correction + counters_t cntslast; // last read counters + counters64_t overflows; + // QC + int qcwpcount = 0; +}; +class CTPRunManager : public ctpCCDBManager +{ + public: + CTPRunManager() = default; + void init(); + int loadRun(const std::string& cfg); + int setRunConfigBK(uint32_t runNumber, const std::string& cfg); + int stopRun(uint32_t irun, long timeStamp); + int addScalers(uint32_t irun, std::time_t time, bool start = 0); + int processMessage(std::string& topic, const std::string& message); + void printActiveRuns() const; + int loadScalerNames(); + int getNRuns(); + void setBKHost(std::string host) { mBKHost = host; }; + void setQCWritePeriod(int period) { mQCWritePeriod = period; }; + uint64_t checkOverflow(uint32_t lcnt0, uint32_t lcnt1, uint64_t lcntcor); + void printCounters(); + + private: + /// Database constants + std::string mBKHost = ""; + std::array mActiveRuns; + std::array mActiveRunNumbers; + std::array mCounters; + std::map mScalerName2Position; + std::map mRunsLoaded; + std::unique_ptr mBKClient; + int mEOX = 0; // redundancy check + int mNew = 1; // 1 - no CCDB: used for QC + int mQCWritePeriod = 3; // Time in 10secs between two writes to QCCD + ClassDefNV(CTPRunManager, 8); +}; +} // namespace ctp +} // namespace o2 +#endif //_CTP_RUNMANAGER_H_ diff --git a/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h new file mode 100644 index 0000000000000..df2aa79d18697 --- /dev/null +++ b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h @@ -0,0 +1,59 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RunManager.h +/// \brief Managing runs for config and scalers +/// \author Roman Lietava +#ifndef _CTP_CTPCCDB_H_ +#define _CTP_CTPCCDB_H_ +#include "DataFormatsCTP/Configuration.h" + +namespace o2 +{ +namespace ctp +{ +class ctpCCDBManager +{ + public: + ctpCCDBManager() = default; + int saveRunScalersToCCDB(CTPRunScalers& scalers, long timeStart, long timeStop); + int saveRunScalersToQCDB(CTPRunScalers& scalers, long timeStart, long timeStop); + int saveRunConfigToCCDB(CTPConfiguration* cfg, long timeStart); + int saveSoxOrbit(uint32_t runNumber, uint32_t soxOrbit, long timeStart); + int saveOrbitReset(long timeStamp); + int saveCtpCfg(uint32_t runNumber, long timeStamp); + static CTPConfiguration getConfigFromCCDB(long timestamp, std::string run, bool& ok); + CTPConfiguration getConfigFromCCDB(long timestamp, std::string run); + CTPRunScalers getScalersFromCCDB(long timestamp, std::string run, bool& ok); + static CTPRunScalers getScalersFromCCDB(long timestamp, std::string, std::string path, bool& ok); + static void setCCDBHost(std::string host) { mCCDBHost = host; }; + static void setQCDBHost(std::string host) { mQCDBHost = host; }; + void setCtpCfgDir(std::string& ctpcfgdir) { mCtpCfgDir = ctpcfgdir; }; + + protected: + /// Database constants + // std::string mCCDBHost = "http://ccdb-test.cern.ch:8080"; + // std::string mQCDBHost = "http://ali-qcdb.cern.ch:8083"; + static std::string mCCDBHost; + static std::string mQCDBHost; + const std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; + // std::string mCCDBPathCTPConfig = "CTP/Config/Config"; - in Configuration.h + const std::string mQCDBPathCTPScalers = "qc/CTP/Scalers"; + const std::string mCCDBPathSoxOrbit = "CTP/Calib/FirstRunOrbit"; + const std::string mCCDBPathOrbitReset = "CTP/Calib/OrbitReset"; + const std::string mCCDBPathCtpCfg = "CTP/Config/CtpCfg"; + std::string mCtpCfgDir; + + ClassDefNV(ctpCCDBManager, 2); +}; +} // namespace ctp +} // namespace o2 +#endif //_CTP_CTPCCDB_H_ diff --git a/Detectors/CTP/workflowScalers/py/createCnts.py b/Detectors/CTP/workflowScalers/py/createCnts.py index 11d35a1591ba5..4773065cfca43 100644 --- a/Detectors/CTP/workflowScalers/py/createCnts.py +++ b/Detectors/CTP/workflowScalers/py/createCnts.py @@ -8,9 +8,21 @@ socket.bind("tcp://*:%s" % port) time.sleep(1) # +path = "/home/rl/countersLHCo/" +# run = "527345" -path = "/home/rl/counters/" filename = path + "20221014.cc" +# +#run = "528543" +#run = "528537" +#filename = path + "20221102.cc" +# +#run = "527963" +#filename = path + "20221024.cc" +# +#run = "527349" +#filename = path + "20221015.cc" +# filecfg = path + run + ".rcfg" # if len(sys.argv) == 2: @@ -42,7 +54,8 @@ def senddata(header, messagedata): print("Sending:",header, data) data = str(messagedata).encode('UTF-8') header = str(header).encode('UTF-8') - msg = [header, data] + test = str("test").encode('UTF-8') + msg = [header, data,test] socket.send_multipart(msg) time.sleep(1) ########################## diff --git a/Detectors/CTP/workflowScalers/py/pub_server.py b/Detectors/CTP/workflowScalers/py/pub_server.py index 088835ff62a41..fb26d2e3929f1 100644 --- a/Detectors/CTP/workflowScalers/py/pub_server.py +++ b/Detectors/CTP/workflowScalers/py/pub_server.py @@ -3,7 +3,7 @@ import sys import time -port = "500901" +port = "50090" if len(sys.argv) > 1: port = sys.argv[1] int(port) @@ -16,19 +16,24 @@ topic = random.randrange(0,2**32) header = "CTP" messagedata = str(topic)+"1111111111111111st" +tag = "tag" print("Sending:",header, messagedata) data = str(messagedata).encode() header = str(header).encode() +tag = str(tag).encode() socket.send(memoryview(header),zmq.SNDMORE) -socket.send(memoryview(data)) +socket.send(memoryview(data),zmq.SNDMORE) +socket.send(memoryview(tag)) time.sleep(1) while True: topic = random.randrange(0,2**32) header = "CTP" - messagedata = str(topic)+"1 2 3 4 5" - print("Sending:",header, messagedata) + #messagedata = str(topic)+"1 2 3 4 5+6 uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuukkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk" + messagedata = "x "*1072 + #print("Sending:",header, messagedata) data = str(messagedata).encode() header = str(header).encode() socket.send(memoryview(header),zmq.SNDMORE) - socket.send(memoryview(data)) + socket.send(memoryview(data),zmq.SNDMORE) + socket.send(memoryview(tag)) time.sleep(1) diff --git a/Detectors/CTP/workflowScalers/py/scalersCTP_v3.txt b/Detectors/CTP/workflowScalers/py/scalersCTP_v3.txt new file mode 100644 index 0000000000000..ccad42af3f38d --- /dev/null +++ b/Detectors/CTP/workflowScalers/py/scalersCTP_v3.txt @@ -0,0 +1,1085 @@ +names: "runn0" +names: "runn1" +names: "runn2" +names: "runn3" +names: "runn4" +names: "runn5" +names: "runn6" +names: "runn7" +names: "runn8" +names: "runn9" +names: "runn10" +names: "runn11" +names: "runn12" +names: "runn13" +names: "runn14" +names: "runn15" +names: "ltg1_ORB" +names: "ltg1_HB" +names: "ltg1_HBr" +names: "ltg1_HC" +names: "ltg1_PH" +names: "ltg1_PP" +names: "ltg1_CAL" +names: "ltg1_SOT" +names: "ltg1_EOT" +names: "ltg1_SOC" +names: "ltg1_EOC" +names: "ltg1_TF" +names: "ltg1_FERST" +names: "ltg1_RT" +names: "ltg1_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg1_GAP1" +names: "ltg1_GAP2" +names: "ltg1_TPC_sync" +names: "ltg1_TPC_rst" +names: "ltg1_TOF" +names: "ltg2_ORB" +names: "ltg2_HB" +names: "ltg2_HBr" +names: "ltg2_HC" +names: "ltg2_PH" +names: "ltg2_PP" +names: "ltg2_CAL" +names: "ltg2_SOT" +names: "ltg2_EOT" +names: "ltg2_SOC" +names: "ltg2_EOC" +names: "ltg2_TF" +names: "ltg2_FERST" +names: "ltg2_RT" +names: "ltg2_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg2_GAP1" +names: "ltg2_GAP2" +names: "ltg2_TPC_sync" +names: "ltg2_TPC_rst" +names: "ltg2_TOF" +names: "ltg3_ORB" +names: "ltg3_HB" +names: "ltg3_HBr" +names: "ltg3_HC" +names: "ltg3_PH" +names: "ltg3_PP" +names: "ltg3_CAL" +names: "ltg3_SOT" +names: "ltg3_EOT" +names: "ltg3_SOC" +names: "ltg3_EOC" +names: "ltg3_TF" +names: "ltg3_FERST" +names: "ltg3_RT" +names: "ltg3_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg3_GAP1" +names: "ltg3_GAP2" +names: "ltg3_TPC_sync" +names: "ltg3_TPC_rst" +names: "ltg3_TOF" +names: "ltg4_ORB" +names: "ltg4_HB" +names: "ltg4_HBr" +names: "ltg4_HC" +names: "ltg4_PH" +names: "ltg4_PP" +names: "ltg4_CAL" +names: "ltg4_SOT" +names: "ltg4_EOT" +names: "ltg4_SOC" +names: "ltg4_EOC" +names: "ltg4_TF" +names: "ltg4_FERST" +names: "ltg4_RT" +names: "ltg4_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg4_GAP1" +names: "ltg4_GAP2" +names: "ltg4_TPC_sync" +names: "ltg4_TPC_rst" +names: "ltg4_TOF" +names: "ltg5_ORB" +names: "ltg5_HB" +names: "ltg5_HBr" +names: "ltg5_HC" +names: "ltg5_PH" +names: "ltg5_PP" +names: "ltg5_CAL" +names: "ltg5_SOT" +names: "ltg5_EOT" +names: "ltg5_SOC" +names: "ltg5_EOC" +names: "ltg5_TF" +names: "ltg5_FERST" +names: "ltg5_RT" +names: "ltg5_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg5_GAP1" +names: "ltg5_GAP2" +names: "ltg5_TPC_sync" +names: "ltg5_TPC_rst" +names: "ltg5_TOF" +names: "ltg6_ORB" +names: "ltg6_HB" +names: "ltg6_HBr" +names: "ltg6_HC" +names: "ltg6_PH" +names: "ltg6_PP" +names: "ltg6_CAL" +names: "ltg6_SOT" +names: "ltg6_EOT" +names: "ltg6_SOC" +names: "ltg6_EOC" +names: "ltg6_TF" +names: "ltg6_FERST" +names: "ltg6_RT" +names: "ltg6_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg6_GAP1" +names: "ltg6_GAP2" +names: "ltg6_TPC_sync" +names: "ltg6_TPC_rst" +names: "ltg6_TOF" +names: "ltg7_ORB" +names: "ltg7_HB" +names: "ltg7_HBr" +names: "ltg7_HC" +names: "ltg7_PH" +names: "ltg7_PP" +names: "ltg7_CAL" +names: "ltg7_SOT" +names: "ltg7_EOT" +names: "ltg7_SOC" +names: "ltg7_EOC" +names: "ltg7_TF" +names: "ltg7_FERST" +names: "ltg7_RT" +names: "ltg7_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg7_GAP1" +names: "ltg7_GAP2" +names: "ltg7_TPC_sync" +names: "ltg7_TPC_rst" +names: "ltg7_TOF" +names: "ltg8_ORB" +names: "ltg8_HB" +names: "ltg8_HBr" +names: "ltg8_HC" +names: "ltg8_PH" +names: "ltg8_PP" +names: "ltg8_CAL" +names: "ltg8_SOT" +names: "ltg8_EOT" +names: "ltg8_SOC" +names: "ltg8_EOC" +names: "ltg8_TF" +names: "ltg8_FERST" +names: "ltg8_RT" +names: "ltg8_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg8_GAP1" +names: "ltg8_GAP2" +names: "ltg8_TPC_sync" +names: "ltg8_TPC_rst" +names: "ltg8_TOF" +names: "ltg9_ORB" +names: "ltg9_HB" +names: "ltg9_HBr" +names: "ltg9_HC" +names: "ltg9_PH" +names: "ltg9_PP" +names: "ltg9_CAL" +names: "ltg9_SOT" +names: "ltg9_EOT" +names: "ltg9_SOC" +names: "ltg9_EOC" +names: "ltg9_TF" +names: "ltg9_FERST" +names: "ltg9_RT" +names: "ltg9_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg9_GAP1" +names: "ltg9_GAP2" +names: "ltg9_TPC_sync" +names: "ltg9_TPC_rst" +names: "ltg9_TOF" +names: "ltg10_ORB" +names: "ltg10_HB" +names: "ltg10_HBr" +names: "ltg10_HC" +names: "ltg10_PH" +names: "ltg10_PP" +names: "ltg10_CAL" +names: "ltg10_SOT" +names: "ltg10_EOT" +names: "ltg10_SOC" +names: "ltg10_EOC" +names: "ltg10_TF" +names: "ltg10_FERST" +names: "ltg10_RT" +names: "ltg10_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg10_GAP1" +names: "ltg10_GAP2" +names: "ltg10_TPC_sync" +names: "ltg10_TPC_rst" +names: "ltg10_TOF" +names: "ltg11_ORB" +names: "ltg11_HB" +names: "ltg11_HBr" +names: "ltg11_HC" +names: "ltg11_PH" +names: "ltg11_PP" +names: "ltg11_CAL" +names: "ltg11_SOT" +names: "ltg11_EOT" +names: "ltg11_SOC" +names: "ltg11_EOC" +names: "ltg11_TF" +names: "ltg11_FERST" +names: "ltg11_RT" +names: "ltg11_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg11_GAP1" +names: "ltg11_GAP2" +names: "ltg11_TPC_sync" +names: "ltg11_TPC_rst" +names: "ltg11_TOF" +names: "ltg12_ORB" +names: "ltg12_HB" +names: "ltg12_HBr" +names: "ltg12_HC" +names: "ltg12_PH" +names: "ltg12_PP" +names: "ltg12_CAL" +names: "ltg12_SOT" +names: "ltg12_EOT" +names: "ltg12_SOC" +names: "ltg12_EOC" +names: "ltg12_TF" +names: "ltg12_FERST" +names: "ltg12_RT" +names: "ltg12_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg12_GAP1" +names: "ltg12_GAP2" +names: "ltg12_TPC_sync" +names: "ltg12_TPC_rst" +names: "ltg12_TOF" +names: "ltg13_ORB" +names: "ltg13_HB" +names: "ltg13_HBr" +names: "ltg13_HC" +names: "ltg13_PH" +names: "ltg13_PP" +names: "ltg13_CAL" +names: "ltg13_SOT" +names: "ltg13_EOT" +names: "ltg13_SOC" +names: "ltg13_EOC" +names: "ltg13_TF" +names: "ltg13_FERST" +names: "ltg13_RT" +names: "ltg13_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg13_GAP1" +names: "ltg13_GAP2" +names: "ltg13_TPC_sync" +names: "ltg13_TPC_rst" +names: "ltg13_TOF" +names: "ltg14_ORB" +names: "ltg14_HB" +names: "ltg14_HBr" +names: "ltg14_HC" +names: "ltg14_PH" +names: "ltg14_PP" +names: "ltg14_CAL" +names: "ltg14_SOT" +names: "ltg14_EOT" +names: "ltg14_SOC" +names: "ltg14_EOC" +names: "ltg14_TF" +names: "ltg14_FERST" +names: "ltg14_RT" +names: "ltg14_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg14_GAP1" +names: "ltg14_GAP2" +names: "ltg14_TPC_sync" +names: "ltg14_TPC_rst" +names: "ltg14_TOF" +names: "ltg15_ORB" +names: "ltg15_HB" +names: "ltg15_HBr" +names: "ltg15_HC" +names: "ltg15_PH" +names: "ltg15_PP" +names: "ltg15_CAL" +names: "ltg15_SOT" +names: "ltg15_EOT" +names: "ltg15_SOC" +names: "ltg15_EOC" +names: "ltg15_TF" +names: "ltg15_FERST" +names: "ltg15_RT" +names: "ltg15_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg15_GAP1" +names: "ltg15_GAP2" +names: "ltg15_TPC_sync" +names: "ltg15_TPC_rst" +names: "ltg15_TOF" +names: "ltg16_ORB" +names: "ltg16_HB" +names: "ltg16_HBr" +names: "ltg16_HC" +names: "ltg16_PH" +names: "ltg16_PP" +names: "ltg16_CAL" +names: "ltg16_SOT" +names: "ltg16_EOT" +names: "ltg16_SOC" +names: "ltg16_EOC" +names: "ltg16_TF" +names: "ltg16_FERST" +names: "ltg16_RT" +names: "ltg16_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg16_GAP1" +names: "ltg16_GAP2" +names: "ltg16_TPC_sync" +names: "ltg16_TPC_rst" +names: "ltg16_TOF" +names: "ltg17_ORB" +names: "ltg17_HB" +names: "ltg17_HBr" +names: "ltg17_HC" +names: "ltg17_PH" +names: "ltg17_PP" +names: "ltg17_CAL" +names: "ltg17_SOT" +names: "ltg17_EOT" +names: "ltg17_SOC" +names: "ltg17_EOC" +names: "ltg17_TF" +names: "ltg17_FERST" +names: "ltg17_RT" +names: "ltg17_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg17_GAP1" +names: "ltg17_GAP2" +names: "ltg17_TPC_sync" +names: "ltg17_TPC_rst" +names: "ltg17_TOF" +names: "ltg18_ORB" +names: "ltg18_HB" +names: "ltg18_HBr" +names: "ltg18_HC" +names: "ltg18_PH" +names: "ltg18_PP" +names: "ltg18_CAL" +names: "ltg18_SOT" +names: "ltg18_EOT" +names: "ltg18_SOC" +names: "ltg18_EOC" +names: "ltg18_TF" +names: "ltg18_FERST" +names: "ltg18_RT" +names: "ltg18_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg18_GAP1" +names: "ltg18_GAP2" +names: "ltg18_TPC_sync" +names: "ltg18_TPC_rst" +names: "ltg18_TOF" +names: "bc40" +names: "clk240" +names: "extorb" +names: "PLSRin" +names: "FastLMin" +names: "BUSYin" +names: "SPAREin" +names: "inp1" +names: "inp2" +names: "inp3" +names: "inp4" +names: "inp5" +names: "inp6" +names: "inp7" +names: "inp8" +names: "inp9" +names: "inp10" +names: "inp11" +names: "inp12" +names: "inp13" +names: "inp14" +names: "inp15" +names: "inp16" +names: "inp17" +names: "inp18" +names: "inp19" +names: "inp20" +names: "inp21" +names: "inp22" +names: "inp23" +names: "inp24" +names: "inp25" +names: "inp26" +names: "inp27" +names: "inp28" +names: "inp29" +names: "inp30" +names: "inp31" +names: "inp32" +names: "inp33" +names: "inp34" +names: "inp35" +names: "inp36" +names: "inp37" +names: "inp38" +names: "inp39" +names: "inp40" +names: "inp41" +names: "inp42" +names: "inp43" +names: "inp44" +names: "inp45" +names: "inp46" +names: "inp47" +names: "inp48" +names: "clamb1" +names: "clamb2" +names: "clamb3" +names: "clamb4" +names: "clamb5" +names: "clamb6" +names: "clamb7" +names: "clamb8" +names: "clamb9" +names: "clamb10" +names: "clamb11" +names: "clamb12" +names: "clamb13" +names: "clamb14" +names: "clamb15" +names: "clamb16" +names: "clamb17" +names: "clamb18" +names: "clamb19" +names: "clamb20" +names: "clamb21" +names: "clamb22" +names: "clamb23" +names: "clamb24" +names: "clamb25" +names: "clamb26" +names: "clamb27" +names: "clamb28" +names: "clamb29" +names: "clamb30" +names: "clamb31" +names: "clamb32" +names: "clamb33" +names: "clamb34" +names: "clamb35" +names: "clamb36" +names: "clamb37" +names: "clamb38" +names: "clamb39" +names: "clamb40" +names: "clamb41" +names: "clamb42" +names: "clamb43" +names: "clamb44" +names: "clamb45" +names: "clamb46" +names: "clamb47" +names: "clamb48" +names: "clamb49" +names: "clamb50" +names: "clamb51" +names: "clamb52" +names: "clamb53" +names: "clamb54" +names: "clamb55" +names: "clamb56" +names: "clamb57" +names: "clamb58" +names: "clamb59" +names: "clamb60" +names: "clamb61" +names: "clamb62" +names: "clamb63" +names: "clamb64" +names: "clama1" +names: "clama2" +names: "clama3" +names: "clama4" +names: "clama5" +names: "clama6" +names: "clama7" +names: "clama8" +names: "clama9" +names: "clama10" +names: "clama11" +names: "clama12" +names: "clama13" +names: "clama14" +names: "clama15" +names: "clama16" +names: "clama17" +names: "clama18" +names: "clama19" +names: "clama20" +names: "clama21" +names: "clama22" +names: "clama23" +names: "clama24" +names: "clama25" +names: "clama26" +names: "clama27" +names: "clama28" +names: "clama29" +names: "clama30" +names: "clama31" +names: "clama32" +names: "clama33" +names: "clama34" +names: "clama35" +names: "clama36" +names: "clama37" +names: "clama38" +names: "clama39" +names: "clama40" +names: "clama41" +names: "clama42" +names: "clama43" +names: "clama44" +names: "clama45" +names: "clama46" +names: "clama47" +names: "clama48" +names: "clama49" +names: "clama50" +names: "clama51" +names: "clama52" +names: "clama53" +names: "clama54" +names: "clama55" +names: "clama56" +names: "clama57" +names: "clama58" +names: "clama59" +names: "clama60" +names: "clama61" +names: "clama62" +names: "clama63" +names: "clama64" +names: "cla0b1" +names: "cla0b2" +names: "cla0b3" +names: "cla0b4" +names: "cla0b5" +names: "cla0b6" +names: "cla0b7" +names: "cla0b8" +names: "cla0b9" +names: "cla0b10" +names: "cla0b11" +names: "cla0b12" +names: "cla0b13" +names: "cla0b14" +names: "cla0b15" +names: "cla0b16" +names: "cla0b17" +names: "cla0b18" +names: "cla0b19" +names: "cla0b20" +names: "cla0b21" +names: "cla0b22" +names: "cla0b23" +names: "cla0b24" +names: "cla0b25" +names: "cla0b26" +names: "cla0b27" +names: "cla0b28" +names: "cla0b29" +names: "cla0b30" +names: "cla0b31" +names: "cla0b32" +names: "cla0b33" +names: "cla0b34" +names: "cla0b35" +names: "cla0b36" +names: "cla0b37" +names: "cla0b38" +names: "cla0b39" +names: "cla0b40" +names: "cla0b41" +names: "cla0b42" +names: "cla0b43" +names: "cla0b44" +names: "cla0b45" +names: "cla0b46" +names: "cla0b47" +names: "cla0b48" +names: "cla0b49" +names: "cla0b50" +names: "cla0b51" +names: "cla0b52" +names: "cla0b53" +names: "cla0b54" +names: "cla0b55" +names: "cla0b56" +names: "cla0b57" +names: "cla0b58" +names: "cla0b59" +names: "cla0b60" +names: "cla0b61" +names: "cla0b62" +names: "cla0b63" +names: "cla0b64" +names: "cla0a1" +names: "cla0a2" +names: "cla0a3" +names: "cla0a4" +names: "cla0a5" +names: "cla0a6" +names: "cla0a7" +names: "cla0a8" +names: "cla0a9" +names: "cla0a10" +names: "cla0a11" +names: "cla0a12" +names: "cla0a13" +names: "cla0a14" +names: "cla0a15" +names: "cla0a16" +names: "cla0a17" +names: "cla0a18" +names: "cla0a19" +names: "cla0a20" +names: "cla0a21" +names: "cla0a22" +names: "cla0a23" +names: "cla0a24" +names: "cla0a25" +names: "cla0a26" +names: "cla0a27" +names: "cla0a28" +names: "cla0a29" +names: "cla0a30" +names: "cla0a31" +names: "cla0a32" +names: "cla0a33" +names: "cla0a34" +names: "cla0a35" +names: "cla0a36" +names: "cla0a37" +names: "cla0a38" +names: "cla0a39" +names: "cla0a40" +names: "cla0a41" +names: "cla0a42" +names: "cla0a43" +names: "cla0a44" +names: "cla0a45" +names: "cla0a46" +names: "cla0a47" +names: "cla0a48" +names: "cla0a49" +names: "cla0a50" +names: "cla0a51" +names: "cla0a52" +names: "cla0a53" +names: "cla0a54" +names: "cla0a55" +names: "cla0a56" +names: "cla0a57" +names: "cla0a58" +names: "cla0a59" +names: "cla0a60" +names: "cla0a61" +names: "cla0a62" +names: "cla0a63" +names: "cla0a64" +names: "cla1b1" +names: "cla1b2" +names: "cla1b3" +names: "cla1b4" +names: "cla1b5" +names: "cla1b6" +names: "cla1b7" +names: "cla1b8" +names: "cla1b9" +names: "cla1b10" +names: "cla1b11" +names: "cla1b12" +names: "cla1b13" +names: "cla1b14" +names: "cla1b15" +names: "cla1b16" +names: "cla1b17" +names: "cla1b18" +names: "cla1b19" +names: "cla1b20" +names: "cla1b21" +names: "cla1b22" +names: "cla1b23" +names: "cla1b24" +names: "cla1b25" +names: "cla1b26" +names: "cla1b27" +names: "cla1b28" +names: "cla1b29" +names: "cla1b30" +names: "cla1b31" +names: "cla1b32" +names: "cla1b33" +names: "cla1b34" +names: "cla1b35" +names: "cla1b36" +names: "cla1b37" +names: "cla1b38" +names: "cla1b39" +names: "cla1b40" +names: "cla1b41" +names: "cla1b42" +names: "cla1b43" +names: "cla1b44" +names: "cla1b45" +names: "cla1b46" +names: "cla1b47" +names: "cla1b48" +names: "cla1b49" +names: "cla1b50" +names: "cla1b51" +names: "cla1b52" +names: "cla1b53" +names: "cla1b54" +names: "cla1b55" +names: "cla1b56" +names: "cla1b57" +names: "cla1b58" +names: "cla1b59" +names: "cla1b60" +names: "cla1b61" +names: "cla1b62" +names: "cla1b63" +names: "cla1b64" +names: "cla1a1" +names: "cla1a2" +names: "cla1a3" +names: "cla1a4" +names: "cla1a5" +names: "cla1a6" +names: "cla1a7" +names: "cla1a8" +names: "cla1a9" +names: "cla1a10" +names: "cla1a11" +names: "cla1a12" +names: "cla1a13" +names: "cla1a14" +names: "cla1a15" +names: "cla1a16" +names: "cla1a17" +names: "cla1a18" +names: "cla1a19" +names: "cla1a20" +names: "cla1a21" +names: "cla1a22" +names: "cla1a23" +names: "cla1a24" +names: "cla1a25" +names: "cla1a26" +names: "cla1a27" +names: "cla1a28" +names: "cla1a29" +names: "cla1a30" +names: "cla1a31" +names: "cla1a32" +names: "cla1a33" +names: "cla1a34" +names: "cla1a35" +names: "cla1a36" +names: "cla1a37" +names: "cla1a38" +names: "cla1a39" +names: "cla1a40" +names: "cla1a41" +names: "cla1a42" +names: "cla1a43" +names: "cla1a44" +names: "cla1a45" +names: "cla1a46" +names: "cla1a47" +names: "cla1a48" +names: "cla1a49" +names: "cla1a50" +names: "cla1a51" +names: "cla1a52" +names: "cla1a53" +names: "cla1a54" +names: "cla1a55" +names: "cla1a56" +names: "cla1a57" +names: "cla1a58" +names: "cla1a59" +names: "cla1a60" +names: "cla1a61" +names: "cla1a62" +names: "cla1a63" +names: "cla1a64" +names: "l0_trigger" +names: "l1_trigger" +names: "l2_trigger" +names: "clum1" +names: "clum2" +names: "clum3" +names: "clum4" +names: "clum5" +names: "clum6" +names: "clu01" +names: "clu02" +names: "clu03" +names: "clu04" +names: "clu05" +names: "clu06" +names: "clu11" +names: "clu12" +names: "clu13" +names: "clu14" +names: "clu15" +names: "clu16" +names: "ltg1_busy" +names: "ltg2_busy" +names: "ltg3_busy" +names: "ltg4_busy" +names: "ltg5_busy" +names: "ltg6_busy" +names: "ltg7_busy" +names: "ltg8_busy" +names: "ltg9_busy" +names: "ltg10_busy" +names: "ltg11_busy" +names: "ltg12_busy" +names: "ltg13_busy" +names: "ltg14_busy" +names: "ltg15_busy" +names: "ltg16_busy" +names: "ltg17_busy" +names: "ltg18_busy" +names: "orbitid" +names: "clum7" +names: "clum8" +names: "clu07" +names: "clu08" +names: "clu17" +names: "clu18" +names: "clu_busy1" +names: "clu_busy2" +names: "clu_busy3" +names: "clu_busy4" +names: "clu_busy5" +names: "clu_busy6" +names: "clu_busy7" +names: "clu_busy8" diff --git a/Detectors/CTP/workflowScalers/py/watchProcess.py b/Detectors/CTP/workflowScalers/py/watchProcess.py new file mode 100755 index 0000000000000..5ac9a98833706 --- /dev/null +++ b/Detectors/CTP/workflowScalers/py/watchProcess.py @@ -0,0 +1,117 @@ +#!/usr/bin/python3 +import requests,re +import os,sys,time,math +import time +from subprocess import Popen, PIPE +def send2(text = "ahoj"): + headers = { 'Content-Type':'application/json',} + data = { + 'text':text, + 'username':'alicetrg', + } + response = requests.post('https://mattermost.web.cern.ch/hooks/75949oimoinr9b47uhp8c1oomh',headers=headers,json=data) + print(response) +# +def send(): + """ + does not work always return can not parse data + """ + Hpar = '\\"Content-Type:application/json\\"' + texth = '\\"text\\"' + text = '\\"lalal\\"' + usernameh = '\\"username\\"' + username = '\\"alicetrg\\"' + dpar = '{}:{},{}:{}'.format(texth,text,usernameh,username) + dpar = "{"+dpar+"}" + dpar = '"'+dpar+'"' + #print(dpar) + cmd = 'curl -g -i -X POST -H {} -d {} https://mattermost.web.cern.ch/hooks/75949oimoinr9b47uhp8c1oomh'.format(Hpar,dpar) + #cmd = 'curl -i -X POST -H {} https://mattermost.web.cern.ch/hooks/75949oimoinr9b47uhp8c1oomh'.format(Hpar) + #cmd='curl -s -o /dev/null {} -F blob=@datafile.root {}/CTP/Calib/{}/{}/{}'.format(wpar, ccdb, actid, tval_s, tval_e) + process = Popen(cmd.split(), stdout = PIPE, stderr = PIPE) + stdo, stde = process.communicate() + print(cmd) + print(stdo) + print("====") + print(stde) +def getLog(service): + #cmd = "journalctl --no-hostname --user-unit "+service + cmd = 'journalctl --no-hostname --user-unit --since "1 day ago" '+service + print(cmd,cmd.split()) + process = Popen(cmd.split(), stdout = PIPE, stderr = PIPE) + stdo, stde= process.communicate() + stdo_str = stdo.decode("utf-8") + if stdo_str.find("No entries") != -1: + print("No entries for service:",service) + return None + #print(stdo) + return stdo_str +NMAX = 3 +def parseLog(log): + nsent = 0 + lines = log.split('\n') + print("# lines:",len(lines)) + print(lines[len(lines)-2]) + for line in lines: + if line.find("ERROR") != -1: + print(line) + if nsent < NMAX: + send2(line) + nsent += 1 + if line.find("ALARM") != -1: + print(line) + if nsent < NMAX: + send2(line) + nsent += 1 +# +nWarn = 0 +nAlarm = 0 +nError = 0 +def printNew(list,n,send = 0): + print(n,len(list)) + sendnow = 0 + if len(list) > n : + sendnow = 1 + for i in list[n:]: + print(i) + n = len(list) + if sendnow and send: + print("sending to mm:",line) + send2(line) + return n +def getLogFile(): + global nWarn, nAlarm,nError + nWarnList = [] + nAlarmList = [] + nErrorList = [] + MAX = 3 + file = "/home/rl/WORK/ctpproxy110424.log" + f = open(file,"r") + nsent = 0 + for line in f: + if line.find("ERROR") != -1: + print(line) + if line.find("ALARM") != -1: + #print(line) + nAlarmList.append(line) + #send2(line) + #nsent += 1 + if line.find("WARN") != -1: + #print(line) + #items = line.split("\]\[") + #items = re.split('\[|\]',line) + nWarnList.append(line) + #print(nWarn) + nWarn = printNew(nWarnList,nWarn) + nAlarm = printNew(nAlarmList,nAlarm,1) + f.close() +if __name__ =="__main__": + #send2("uj0"); + while 1: + now = time.localtime() + current_time = time.strftime("%H:%M:%S",now) + print("===> Time =", current_time) + log = getLogFile() + time.sleep(15) + + #parseLog(log) diff --git a/Detectors/CTP/workflowScalers/src/CTPWorkflowScalersLinkDef.h b/Detectors/CTP/workflowScalers/src/CTPWorkflowScalersLinkDef.h new file mode 100644 index 0000000000000..284e64a5222b1 --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/CTPWorkflowScalersLinkDef.h @@ -0,0 +1,18 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::ctp::ctpCCDBManager + ; +#endif diff --git a/Detectors/CTP/workflowScalers/src/RunManager.cxx b/Detectors/CTP/workflowScalers/src/RunManager.cxx new file mode 100644 index 0000000000000..778677bec2ec9 --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/RunManager.cxx @@ -0,0 +1,488 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RunManager.cxx +/// \author Roman Lietava + +#include "CTPWorkflowScalers/RunManager.h" +#include +#include +#include +#include "CommonUtils/StringUtils.h" +#include + +using namespace o2::ctp; +/// +/// Active run to keep cfg and saclers of active runs +/// Also used for Bookkeeping counters managment; +/// +void CTPActiveRun::initBK() +{ + std::vector clslist = cfg.getTriggerClassList(); + for (auto const& cls : clslist) { + cntslast[cls] = {0, 0, 0, 0, 0, 0}; + cntslast0[cls] = {0, 0, 0, 0, 0, 0}; + overflows[cls] = {0, 0, 0, 0, 0, 0}; + } +} +int CTPActiveRun::send2BK(std::unique_ptr& BKClient, size_t ts, bool start) +{ + int runNumber = cfg.getRunNumber(); + // LOG(info) << "BK Filling run:" << runNumber; + // int runOri = runNumber; + // runNumber = 123; + if (start) { + for (auto const& cls : cntslast) { + for (int i = 0; i < 6; i++) { + cnts0[cls.first][i] = cls.second[i]; + } + } + } + std::array cntsbk{0}; + for (auto const& cls : cntslast) { + for (int i = 0; i < 6; i++) { + if (cls.second[i] < cntslast0[cls.first][i]) { + overflows[cls.first][i]++; + } + cntslast0[cls.first][i] = cls.second[i]; + cntsbk[i] = (uint64_t)cls.second[i] + 0xffffffffull * overflows[cls.first][i] - (uint64_t)cnts0[cls.first][i]; + } + std::string clsname = cfg.getClassNameFromHWIndex(cls.first); + // clsname = std::to_string(runOri) + "_" + clsname; + try { + BKClient->ctpTriggerCounters()->createOrUpdateForRun(runNumber, clsname, ts, cntsbk[0], cntsbk[1], cntsbk[2], cntsbk[3], cntsbk[4], cntsbk[5]); + } catch (std::runtime_error& error) { + std::cerr << "An error occurred: " << error.what() << std::endl; + return 1; + } + LOG(debug) << "Run BK:" << runNumber << " class:" << clsname << " cls.first" << cls.first << " ts:" << ts << " cnts:" << cntsbk[0] << " " << cntsbk[1] << " " << cntsbk[2] << " " << cntsbk[3] << " " << cntsbk[4] << " " << cntsbk[5]; + } + return 0; +} +/// +/// Run Manager to manage Config and Scalers +/// +void CTPRunManager::init() +{ + for (uint32_t i = 0; i < NRUNS; i++) { + mActiveRuns[i] = nullptr; + } + loadScalerNames(); + if (mBKHost != "none") { + mBKClient = BkpClientFactory::create(mBKHost); + LOG(info) << "BK Client created with:" << mBKHost; + } else { + LOG(info) << "BK not sent"; + } + setQCDBHost(mQCDBHost); + LOG(info) << "QC host:" << mQCDBHost; + LOG(info) << "QCDB writing every:" << mQCWritePeriod << " 10 secs"; + LOG(info) << "CCDB host:" << mCCDBHost; + LOG(info) << "CTP vNew cfg:" << mNew; + LOG(info) << "ctp.cfg dir:" << mCtpCfgDir; + LOG(info) << "CTPRunManager initialised."; +} +int CTPRunManager::loadRun(const std::string& cfg) +{ + LOG(info) << "Loading run: " << cfg; + auto now = std::chrono::system_clock::now(); + long timeStamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + LOG(info) << "Timestamp real time:" << timeStamp; + size_t pos = cfg.find(" "); + std::string cfgmod = cfg; + if (pos == std::string::npos) { + LOG(warning) << "Space not found in CTP config"; + } else { + std::string f = cfg.substr(0, pos); + if (f.find("run") == std::string::npos) { + double_t tt = std::stold(f); + timeStamp = (tt * 1000.); + LOG(info) << "Timestamp file:" << timeStamp; + cfgmod = cfg.substr(pos, cfg.size()); + LOG(info) << "ctpconfig: using ctp time"; + } + } + CTPActiveRun* activerun = new CTPActiveRun; + activerun->timeStart = timeStamp; + activerun->cfg.loadConfigurationRun3(cfgmod); + activerun->cfg.printStream(std::cout); + // + uint32_t runnumber = activerun->cfg.getRunNumber(); + activerun->scalers.setRunNumber(runnumber); + activerun->scalers.setClassMask(activerun->cfg.getTriggerClassMask()); + o2::detectors::DetID::mask_t detmask = activerun->cfg.getDetectorMask(); + activerun->scalers.setDetectorMask(detmask); + // + mRunsLoaded[runnumber] = activerun; + saveRunConfigToCCDB(&activerun->cfg, timeStamp); + if (mCtpCfgDir != "none") { + saveCtpCfg(runnumber, timeStamp); + } + return 0; +} +int CTPRunManager::setRunConfigBK(uint32_t runNumber, const std::string& cfg) +{ + std::cout << "Printing run:" << runNumber << " cfg:" << cfg << std::endl; + if (mBKClient) { + try { + mBKClient->run()->setRawCtpTriggerConfiguration(runNumber, cfg); + } catch (std::runtime_error& error) { + std::cerr << "An error occurred: " << error.what() << std::endl; + return 1; + } + LOG(info) << "Run BK:" << runNumber << " CFG:" << cfg; + } + return 0; +} +int CTPRunManager::stopRun(uint32_t irun, long timeStamp) +{ + LOG(info) << "Stopping run index: " << irun; + if (mActiveRuns[irun] == nullptr) { + LOG(error) << "No config for run index:" << irun; + return 1; + } + // const auto now = std::chrono::system_clock::now(); + // const long timeStamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + mActiveRuns[irun]->timeStop = timeStamp * 1000.; + saveRunScalersToCCDB(mActiveRuns[irun]->scalers, mActiveRuns[irun]->timeStart, mActiveRuns[irun]->timeStop); + saveRunScalersToQCDB(mActiveRuns[irun]->scalers, mActiveRuns[irun]->timeStart, mActiveRuns[irun]->timeStop); + delete mActiveRuns[irun]; + mActiveRuns[irun] = nullptr; + return 0; +} +int CTPRunManager::addScalers(uint32_t irun, std::time_t time, bool start) +{ + if (mActiveRuns[irun] == nullptr) { + LOG(error) << "No config for run index:" << irun; + return 1; + } + std::string orb = "extorb"; + std::string orbitid = "orbitid"; + CTPScalerRecordRaw scalrec; + scalrec.epochTime = time; + std::vector clslist = mActiveRuns[irun]->cfg.getTriggerClassList(); + for (auto const& cls : clslist) { + std::string cmb = "clamb" + std::to_string(cls + 1); + std::string cma = "clama" + std::to_string(cls + 1); + std::string c0b = "cla0b" + std::to_string(cls + 1); + std::string c0a = "cla0a" + std::to_string(cls + 1); + std::string c1b = "cla1b" + std::to_string(cls + 1); + std::string c1a = "cla1a" + std::to_string(cls + 1); + CTPScalerRaw scalraw; + scalraw.classIndex = (uint32_t)cls; + // std::cout << "cls:" << cls << " " << scalraw.classIndex << std::endl; + scalraw.lmBefore = mCounters[mScalerName2Position[cmb]]; + scalraw.lmAfter = mCounters[mScalerName2Position[cma]]; + scalraw.l0Before = mCounters[mScalerName2Position[c0b]]; + scalraw.l0After = mCounters[mScalerName2Position[c0a]]; + scalraw.l1Before = mCounters[mScalerName2Position[c1b]]; + scalraw.l1After = mCounters[mScalerName2Position[c1a]]; + // std::cout << "positions:" << cmb << " " << mScalerName2Position[cmb] << std::endl; + // std::cout << "positions:" << cma << " " << mScalerName2Position[cma] << std::endl; + scalrec.scalers.push_back(scalraw); + // BK scalers to be corrected for overflow + if (mBKClient) { + CTPActiveRun* ar = mActiveRuns[irun]; + ar->cntslast[cls][0] = scalraw.lmBefore; + ar->cntslast[cls][1] = scalraw.lmAfter; + ar->cntslast[cls][2] = scalraw.l0Before; + ar->cntslast[cls][3] = scalraw.l0After; + ar->cntslast[cls][4] = scalraw.l1Before; + ar->cntslast[cls][5] = scalraw.l1After; + } + } + mActiveRuns[irun]->send2BK(mBKClient, time, start); + // + uint32_t NINPS = 48; + int offset = 599; + for (uint32_t i = 0; i < NINPS; i++) { + uint32_t inpcount = mCounters[offset + i]; + scalrec.scalersInps.push_back(inpcount); + // LOG(info) << "Scaler for input:" << CTPRunScalers::scalerNames[offset+i] << ":" << inpcount; + } + // + if (mNew == 0) { + scalrec.intRecord.orbit = mCounters[mScalerName2Position[orb]]; + } else { + scalrec.intRecord.orbit = mCounters[mScalerName2Position[orbitid]]; + } + scalrec.intRecord.bc = 0; + mActiveRuns[irun]->scalers.addScalerRacordRaw(scalrec); + LOG(debug) << "Adding scalers for orbit:" << scalrec.intRecord.orbit; + // scalrec.printStream(std::cout); + // printCounters(); + return 0; +} +int CTPRunManager::processMessage(std::string& topic, const std::string& message) +{ + LOG(debug) << "Processing message with topic:" << topic; + std::string firstcounters; + if (topic.find("clear") != std::string::npos) { + mRunsLoaded.clear(); + LOG(info) << "Loaded runs cleared."; + return 0; + } + if (topic.find("ctpconfig") != std::string::npos) { + LOG(info) << "ctpconfig received"; + loadRun(message); + return 0; + } + if (topic.find("soxorbit") != std::string::npos) { + std::vector tokens = o2::utils::Str::tokenize(message, ' '); + int ret = 0; + if (tokens.size() == 3) { + long timestamp = std::stol(tokens[0]); + uint32_t runnumber = std::stoul(tokens[1]); + uint32_t orbit = std::stoul(tokens[2]); + ret = saveSoxOrbit(runnumber, orbit, timestamp); + std::string logmessage; + if (ret) { + logmessage = "Failed to update CCDB with SOX orbit."; + } else { + logmessage = "CCDB updated with SOX orbit."; + } + LOG(important) << logmessage << " run:" << runnumber << " sox orbit:" << orbit << " ts:" << timestamp; + } else { + LOG(error) << "Topic soxorbit dize !=3: " << message << " token size:" << tokens.size(); + ret = 1; + } + return ret; + } + if (topic.find("orbitreset") != std::string::npos) { + std::vector tokens = o2::utils::Str::tokenize(message, ' '); + int ret = 0; + if (tokens.size() == 1) { + long timestamp = std::stol(tokens[0]); + ret = saveOrbitReset(timestamp); + std::string logmessage; + if (ret) { + logmessage = "Failed to update CCDB with orbitreset. "; + } else { + logmessage = "CCDB updated with orbitreset. "; + } + LOG(important) << logmessage << timestamp; + } else { + LOG(error) << "Topic orbit reset != 2: " << message << " token size:" << tokens.size(); + ret = 1; + } + return ret; + } + if (topic.find("rocnts") != std::string::npos) { + return 0; + } + static int nerror = 0; + if (topic.find("sox") != std::string::npos) { + // get config + size_t irun = message.find("run"); + if (irun == std::string::npos) { + LOG(debug) << "run keyword not found in SOX"; + irun = message.size(); + } + LOG(info) << "SOX received, Run keyword position:" << irun; + std::string cfg = message.substr(irun, message.size() - irun); + firstcounters = message.substr(0, irun); + } else if (topic.find("eox") != std::string::npos) { + LOG(info) << "EOX received"; + mEOX = 1; + } else if (topic.find("cnts") != std::string::npos) { + // just continue + } else { + if (nerror < 10) { + LOG(warning) << "Skipping topic:" << topic; + nerror++; + } + return 0; + } + // + std::vector tokens; + if (firstcounters.size() > 0) { + tokens = o2::utils::Str::tokenize(firstcounters, ' '); + } else { + tokens = o2::utils::Str::tokenize(message, ' '); + } + if (tokens.size() != (CTPRunScalers::NCOUNTERS + 1)) { + if (tokens.size() == (CTPRunScalers::NCOUNTERSv2 + 1)) { + mNew = 0; + LOG(warning) << "v2 scaler size"; + } else { + LOG(warning) << "Scalers size wrong:" << tokens.size() << " expected:" << CTPRunScalers::NCOUNTERS + 1 << " or " << CTPRunScalers::NCOUNTERSv2 + 1; + return 1; + } + } + double timeStamp = std::stold(tokens.at(0)); + std::time_t tt = timeStamp; + LOG(debug) << "Processing scalers, all good, time:" << tokens.at(0) << " " << std::asctime(std::localtime(&tt)); + for (uint32_t i = 1; i < tokens.size(); i++) { + mCounters[i - 1] = std::stoull(tokens.at(i)); + if (i < (NRUNS + 1)) { + // std::cout << mCounters[i - 1] << " "; + } + } + // std::cout << std::endl; + LOG(debug) << "Counter size:" << tokens.size(); + // + for (uint32_t i = 0; i < NRUNS; i++) { + if ((mCounters[i] == 0) && (mActiveRunNumbers[i] == 0)) { + // not active + } else if ((mCounters[i] != 0) && (mActiveRunNumbers[i] == mCounters[i])) { + // active , do scalers + LOG(debug) << "Run continue:" << mCounters[i]; + addScalers(i, tt); + // LOG(info) << " QC period:" << mActiveRunNumbers[i] << " " << mActiveRuns[i]->qcwpcount << " " << mQCWritePeriod; + if (mActiveRuns[i]->qcwpcount > mQCWritePeriod) { + saveRunScalersToQCDB(mActiveRuns[i]->scalers, tt * 1000, tt * 1000); + mActiveRuns[i]->qcwpcount = 0; + } + mActiveRuns[i]->qcwpcount++; + } else if ((mCounters[i] != 0) && (mActiveRunNumbers[i] == 0)) { + LOG(info) << "Run started:" << mCounters[i]; + auto run = mRunsLoaded.find(mCounters[i]); + if (run != mRunsLoaded.end()) { + mActiveRunNumbers[i] = mCounters[i]; + mActiveRuns[i] = run->second; + mRunsLoaded.erase(run); + setRunConfigBK(mActiveRuns[i]->cfg.getRunNumber(), mActiveRuns[i]->cfg.getConfigString()); + addScalers(i, tt, 1); + saveRunScalersToQCDB(mActiveRuns[i]->scalers, tt * 1000, tt * 1000); + } else { + LOG(error) << "Trying to start run which is not loaded:" << mCounters[i]; + } + } else if ((mCounters[i] == 0) && (mActiveRunNumbers[i] != 0)) { + if (mEOX != 1) { + LOG(error) << "Internal error in processMessage: mEOX != 1 expected 0: mEOX:" << mEOX; + } + LOG(info) << "Run stopped:" << mActiveRunNumbers[i]; + addScalers(i, tt); + mActiveRunNumbers[i] = 0; + mEOX = 0; + stopRun(i, tt); + } + } + mEOX = 0; + // printActiveRuns(); + return 0; +} +void CTPRunManager::printActiveRuns() const +{ + std::cout << "Active runs:"; + for (auto const& arun : mActiveRunNumbers) { + std::cout << arun << " "; + } + std::cout << " #loaded runs:" << mRunsLoaded.size(); + for (auto const& lrun : mRunsLoaded) { + std::cout << " " << lrun.second->cfg.getRunNumber(); + } + std::cout << std::endl; +} +int CTPRunManager::loadScalerNames() +{ + if (CTPRunScalers::NCOUNTERS != CTPRunScalers::scalerNames.size()) { + LOG(fatal) << "NCOUNTERS:" << CTPRunScalers::NCOUNTERS << " different from names vector:" << CTPRunScalers::scalerNames.size(); + return 1; + } + // try to open files of no success use default + for (uint32_t i = 0; i < CTPRunScalers::scalerNames.size(); i++) { + mScalerName2Position[CTPRunScalers::scalerNames[i]] = i; + } + return 0; +} +int CTPRunManager::getNRuns() +{ + int n = 0; + for (int i = 0; i < NRUNS; i++) { + if (mActiveRuns[i] != nullptr) { + n++; + } + } + return n; +} +void CTPRunManager::printCounters() +{ + int NDET = 18; + int NINPS = 48; + // int NCLKFP = 7; + int NLTG_start = NRUNS; + int NCLKFP_start = NLTG_start + NDET * 32; + int NINPS_start = NCLKFP_start + 7; + int NCLS_start = NINPS_start + NINPS; + std::cout << "====> CTP counters:" << std::endl; + std::cout << "RUNS:" << std::endl; + int ipos = 0; + for (uint32_t i = 0; i < NRUNS; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + for (int i = 0; i < NDET; i++) { + std::cout << "LTG" << i + 1 << std::endl; + for (int j = NLTG_start + i * 32; j < NLTG_start + (i + 1) * 32; j++) { + std::cout << ipos << ":" << mCounters[j] << " "; + ipos++; + } + std::cout << std::endl; + } + std::cout << "BC40,BC240,Orbit,pulser, fastlm, busy,spare" << std::endl; + for (int i = NCLKFP_start; i < NCLKFP_start + 7; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "INPUTS:" << std::endl; + for (int i = NINPS_start; i < NINPS_start + NINPS; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS M Before" << std::endl; + for (int i = NCLS_start; i < NCLS_start + 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS M After" << std::endl; + for (int i = NCLS_start + 64; i < NCLS_start + 2 * 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS 0 Before" << std::endl; + for (int i = NCLS_start + 2 * 64; i < NCLS_start + 3 * 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS 0 After" << std::endl; + for (int i = NCLS_start + 3 * 64; i < NCLS_start + 4 * 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS 1 Before" << std::endl; + for (int i = NCLS_start + 4 * 64; i < NCLS_start + 5 * 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS 1 After" << std::endl; + for (int i = NCLS_start + 5 * 64; i < NCLS_start + 6 * 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << " REST:" << std::endl; + for (uint32_t i = NCLS_start + 6 * 64; i < mCounters.size(); i++) { + if ((ipos % 10) == 0) { + std::cout << std::endl; + std::cout << ipos << ":"; + } + std::cout << mCounters[i] << " "; + } +} diff --git a/Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx b/Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx new file mode 100644 index 0000000000000..8460c07dcc896 --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx @@ -0,0 +1,170 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// example to run: +// +#include +#include +#include +#include +#include "CommonUtils/StringUtils.h" +#include +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "BookkeepingApi/BkpClientFactory.h" +#include "BookkeepingApi/BkpClient.h" +#include +#include +#include +#include +namespace bpo = boost::program_options; +// +// Test in the lab +// o2-ctp-bk-write -r 37 -s 1 -c 1 --ccdb='http://acsl-ccdb.cern.ch:8083' -b 'acsl-aliecs.cern.ch:4001' -t 1753185071753 +// +int main(int argc, char** argv) +{ + const std::string testCCDB = "http://ccdb-test.cern.ch:8080"; + // std::string prodCCDB = "http://o2-ccdb.internal"; + const std::string aliceCCDB = "http://alice-ccdb.cern.ch"; + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " Write ctp config or scalers to BK\n"); + bpo::options_description opt_hidden(""); + bpo::options_description opt_all; + bpo::positional_options_description opt_pos; + try { + auto add_option = opt_general.add_options(); + add_option("help,h", "Print this help message"); + add_option("input-file,f", bpo::value()->default_value("none"), "input file name, none - do not read file"); + add_option("bkhost,b", bpo::value()->default_value("none"), "bk web address"); + add_option("ccdb", bpo::value()->default_value("alice"), "choose databse: test- test ccdb; prod - production ccdb; alice - alice ccdb; else ccdb parameter"); + add_option("run-number,r", bpo::value()->default_value(0), "run number"); + add_option("timestamp,t", bpo::value()->default_value(0), "timestamp; if 0 timestamp is calulated inside this code"); + add_option("cfg,c", bpo::value()->default_value(0), "Do cfg"); + add_option("scalers,s", bpo::value()->default_value(0), "Do scalers"); + // + opt_all.add(opt_general).add(opt_hidden); + bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); + if (vm.count("help")) { + std::cout << opt_general << std::endl; + exit(0); + } + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << "ERROR: " << e.what() << std::endl + << std::endl; + std::cerr << opt_general << std::endl; + exit(1); + } catch (std::exception& e) { + std::cerr << e.what() << ", application will now exit" << std::endl; + exit(2); + } + uint64_t timestamp = vm["timestamp"].as(); + // + int ret = 0; + std::vector runs; + int32_t run = vm["run-number"].as(); + std::cout << "run:" << run << std::endl; + if (run) { + std::cout << "pushing" << std::endl; + runs.push_back(std::to_string(run)); + } + // read input file + std::string filename = vm["input-file"].as(); + if (filename != "none") { + std::ifstream file(filename); + if (!file.is_open()) { + LOG(fatal) << "Cannot open file:" << filename << std::endl; + } else { + std::string line; + while (std::getline(file, line)) { + std::cout << line << "\n"; + std::vector tokens = o2::utils::Str::tokenize(line, ' '); + // int run = std::stoi(tokens[0]); + runs.push_back(tokens[0]); + } + } + } + bool cfg = vm["cfg"].as(); + bool scalers = vm["scalers"].as(); + std::cout << "Doing: cfg:" << cfg << " scal:" << scalers << std::endl; + if (cfg || scalers) { + std::string bkhost = vm["bkhost"].as(); + std::unique_ptr mBKClient = o2::bkp::api::BkpClientFactory::create(bkhost); + // get from ccdb + std::string ccdbAddress; + if (vm["ccdb"].as() == "prod") { + // ccdbAddress = prodCCDB; + } else if (vm["ccdb"].as() == "test") { + ccdbAddress = testCCDB; + } else if (vm["ccdb"].as() == "alice") { + ccdbAddress = aliceCCDB; + } else { + ccdbAddress = vm["ccdb"].as(); + } + o2::ctp::ctpCCDBManager::setCCDBHost(ccdbAddress); + std::cout << "CCDB: " << vm["ccdb"].as() << " " << ccdbAddress << std::endl; + std::map metadata; + for (auto const& run : runs) { + metadata["runNumber"] = run; + bool ok; + int runNumber = std::stoi(run); + auto ctpcfg = o2::ctp::ctpCCDBManager::getConfigFromCCDB(timestamp, run, ok); + + if (cfg) { + std::string ctpcfgstr = ctpcfg.getConfigString(); + try { + mBKClient->run()->setRawCtpTriggerConfiguration(runNumber, ctpcfgstr); + } catch (std::runtime_error& error) { + std::cerr << "An error occurred: " << error.what() << std::endl; + // return 1; + } + LOG(info) << "Run BK:" << run << " CFG:" << cfg; + } + if (scalers) { + auto ctpcnts = o2::ctp::ctpCCDBManager::getScalersFromCCDB(timestamp, run, "CTP/Calib/Scalers", ok); + ctpcnts.convertRawToO2(); + std::vector clsinds = ctpcnts.getClassIndexes(); + long ts = ctpcnts.getTimeLimit().second; + int i = 0; + for (auto const& ind : clsinds) { + std::array cntsbk = ctpcnts.getIntegralForClass(i); + std::string clsname = ctpcfg.getClassNameFromHWIndex(cntsbk[0]); + try { + mBKClient->ctpTriggerCounters()->createOrUpdateForRun(runNumber, clsname, ts, cntsbk[1], cntsbk[2], cntsbk[3], cntsbk[4], cntsbk[5], cntsbk[6]); + std::cout << runNumber << " clsname: " << cntsbk[0] << " " << clsname << " t:" << ts << " cnts:" << cntsbk[1] << " " << cntsbk[2] << " " << cntsbk[3] << " " << cntsbk[4] << " " << cntsbk[5] << " " << cntsbk[6] << std::endl; + ; + + } catch (std::runtime_error& error) { + std::cerr << "An error occurred: " << error.what() << std::endl; + // return 1; + } + LOG(debug) << "Run BK scalers ok"; + i++; + } + } + } + // add to bk + } + std::cout << "o2-ctp-bk-write done" << std::endl; + return ret; +} diff --git a/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx b/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx new file mode 100644 index 0000000000000..aa953e89264ef --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx @@ -0,0 +1,151 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// example to run: +// +#include +#include +#include +#include +#include "CommonUtils/StringUtils.h" +#include +#include +#include +#include +namespace bpo = boost::program_options; +// +// get object from ccdb +// auto & cc = o2::ccdb::BasicCCDNManager::instance(); +// auto pp = ccdbMgr.getSpecific>("CTP/Calib/OrbitResetTest") +// std::cout << (*pp)[0] << std::endl; +int main(int argc, char** argv) +{ + const std::string testCCDB = "http://ccdb-test.cern.ch:8080"; + const std::string prodCCDB = "http://o2-ccdb.internal"; + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " Write orbit staff to ccdb\n"); + bpo::options_description opt_hidden(""); + bpo::options_description opt_all; + bpo::positional_options_description opt_pos; + try { + auto add_option = opt_general.add_options(); + add_option("help,h", "Print this help message"); + add_option("output-file,o", bpo::value()->default_value("none"), "output file name, none - file not created"); + add_option("output-dir,d", bpo::value()->default_value("./"), "output dir"); + add_option("ccdb", bpo::value()->default_value("test"), "choose databse: test- test ccdb; prod - production ccdb; else ccdb parameter"); + add_option("action,a", bpo::value()->default_value(""), "sox - first orbit otherwise orbit reset"); + add_option("run-number,r", bpo::value()->default_value(123), "run number"); + add_option("testReset,t", bpo::value()->default_value(0), "0 = CTP/Calib/OrbitReset; 1 = CTP/Calib/OrbitResetTest"); + add_option("sox-orbit,x", bpo::value()->default_value(0), "SOX orbit"); + add_option("timestamp,s", bpo::value()->default_value(0), "timestamp of SOX/orbit reading; if 0 timestamp is calulated inside this code"); + + // + opt_all.add(opt_general).add(opt_hidden); + bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); + if (vm.count("help")) { + std::cout << opt_general << std::endl; + exit(0); + } + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << "ERROR: " << e.what() << std::endl + << std::endl; + std::cerr << opt_general << std::endl; + exit(1); + } catch (std::exception& e) { + std::cerr << e.what() << ", application will now exit" << std::endl; + exit(2); + } + int ret = 0; + std::string action = vm["action"].as(); + std::vector vect; + std::string ccdbPath; + long tt = vm["timestamp"].as(); + if (tt == 0) { + auto now = std::chrono::system_clock::now(); + tt = std::chrono::duration_cast(now.time_since_epoch()).count(); + } + vect.push_back(tt); + if (action == "sox") { + // write to CTP/Calib/FirstRunOrbit + std::cout << "===> FirsRunOrbit" << std::endl; + vect.push_back(vm["run-number"].as()); + vect.push_back(vm["sox-orbit"].as()); + ccdbPath = "CTP/Calib/FirstRunOrbit"; + } else { + // write to CTP/Calib/OrbitReset + std::cout << "===> ResetOrbit" << std::endl; + ccdbPath = "CTP/Calib/OrbitReset"; + if (vm["testReset"].as()) { + ccdbPath += "Test"; + } + } + // + std::string ccdbAddress; + if (vm["ccdb"].as() == "prod") { + ccdbAddress = prodCCDB; + } else if (vm["ccdb"].as() == "test") { + ccdbAddress = testCCDB; + } else { + ccdbAddress = vm["ccdb"].as(); + } + std::cout << " Writing to db:" << ccdbAddress << std::endl; + if (ccdbAddress != "none") { + // auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + o2::ccdb::CcdbApi api; + api.init(ccdbAddress.c_str()); + std::map metadata; + long tmin = tt / 1000; + long tmax = tmin + 381928219; + if (action == "sox") { + int64_t runnum = vm["run-number"].as(); + metadata["runNumber"] = std::to_string(runnum); + std::cout << "Storing:" << ccdbPath << " " << metadata["runNumber"] << " tmin:" << tmin << " tmax:" << tmax << " ts:" << tt << std::endl; + ret = api.storeAsTFileAny(&(vect), ccdbPath, metadata, tmin, tmax); + } else { + std::cout << "Storing:" << ccdbPath << " tmin:" << tmin << " tmax:" << tmax << " ts:" << tt << std::endl; + std::string filename = "orbitReset.root"; + auto classname = "std::vector"; + metadata["adjustableEOV"] = "true"; + ret = api.storeAsTFileAny(&(vect), ccdbPath, metadata, tmin, tmax); + o2::ccdb::CcdbObjectInfo oi(ccdbPath, classname, filename, metadata, tmin, tmax); + adjustOverriddenEOV(api, oi); + } + } + // + if (vm["output-file"].as() != "none") { + std::string file = vm["output-dir"].as() + vm["output-file"].as(); + TFile* f = TFile::Open(file.c_str(), "RECREATE"); + if (f == nullptr) { + std::cout << "Error: File" << file << " could not be open for writing !!!" << std::endl; + ret++; + } else { + std::cout << "File" << file << " being writen." << std::endl; + f->WriteObject(&vect, "ccdb_object"); + f->Close(); + } + } else { + std::cout << "No file created" << std::endl; + } + return ret; +} diff --git a/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx b/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx index d7c2ec5e19047..391d1b5ccf58b 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx @@ -29,6 +29,7 @@ #include "Framework/WorkflowSpec.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" +#include "Framework/RawDeviceService.h" #include "Framework/ControlService.h" #include "Framework/Logger.h" #include "Framework/Lifetime.h" @@ -39,30 +40,44 @@ #include #include #include "CommonUtils/StringUtils.h" -#include "DataFormatsCTP/Configuration.h" +#include "CTPWorkflowScalers/RunManager.h" #include #include - +#include "BookkeepingApi/BkpClient.h" using namespace o2::framework; using DetID = o2::detectors::DetID; -InjectorFunction dcs2dpl(std::string& ccdbhost) -// InjectorFunction dcs2dpl() +InjectorFunction dcs2dpl(std::string& ccdbhost, std::string& bkhost, std::string& qchost, int qcwriteperiod, std::string& ctpcfgdir) { auto runMgr = std::make_shared(); runMgr->setCCDBHost(ccdbhost); + runMgr->setBKHost(bkhost); + runMgr->setQCDBHost(qchost); + runMgr->setQCWritePeriod(qcwriteperiod); + runMgr->setCtpCfgDir(ctpcfgdir); runMgr->init(); - return [runMgr](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId) { + // runMgr->setClient(client); + static int nprint = 0; + return [runMgr](TimingInfo&, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) -> bool { // FIXME: Why isn't this function using the timeslice index? // make sure just 2 messages received - if (parts.Size() != 2) { - LOG(error) << "received " << parts.Size() << " instead of 2 expected"; - return; - } + // if (parts.Size() != 2) { + // LOG(error) << "received " << parts.Size() << " instead of 2 expected"; + // return; + //} std::string messageHeader{static_cast(parts.At(0)->GetData()), parts.At(0)->GetSize()}; size_t dataSize = parts.At(1)->GetSize(); std::string messageData{static_cast(parts.At(1)->GetData()), parts.At(1)->GetSize()}; - LOG(info) << "received message " << messageHeader << " of size " << dataSize; // << " Payload:" << messageData; + nprint++; + int nlimit = 60; + int nrange = 8; + if (nprint > nlimit && nprint < (nlimit + nrange + 1)) { + LOG(info) << "received message " << messageHeader << " of size " << dataSize << " # parts:" << parts.Size(); // << " Payload:" << messageData; + if (nprint == (nlimit + nrange)) { + nprint = 0; + } + } runMgr->processMessage(messageHeader, messageData); + return true; }; } @@ -71,6 +86,10 @@ void customize(std::vector& workflowOptions) { workflowOptions.push_back(ConfigParamSpec{"subscribe-to", VariantType::String, "type=sub,method=connect,address=tcp://188.184.30.57:5556,rateLogging=10,transport=zeromq", {"channel subscribe to"}}); workflowOptions.push_back(ConfigParamSpec{"ccdb-host", VariantType::String, "http://o2-ccdb.internal:8080", {"ccdb host"}}); + workflowOptions.push_back(ConfigParamSpec{"bk-host", VariantType::String, "none", {"bk host"}}); + workflowOptions.push_back(ConfigParamSpec{"qc-host", VariantType::String, "none", {"qc host"}}); + workflowOptions.push_back(ConfigParamSpec{"ctpcfg-dir", VariantType::String, "none", {"ctp.cfg file directory"}}); + workflowOptions.push_back(ConfigParamSpec{"qc-writeperiod", VariantType::Int, 30, {"Period of writing to QCDB in units of 10secs, default = 30 (5 mins)"}}); } #include "Framework/runDataProcessing.h" @@ -93,6 +112,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) const std::string devName = "ctp-proxy"; auto chan = config.options().get("subscribe-to"); std::string ccdbhost = config.options().get("ccdb-host"); + std::string bkhost = config.options().get("bk-host"); + std::string qchost = config.options().get("qc-host"); + int qcwriteperiod = config.options().get("qc-writeperiod"); + std::string ctpcfgdir = config.options().get("ctpcfg-dir"); if (chan.empty()) { throw std::runtime_error("input channel is not provided"); } @@ -107,7 +130,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) std::move(ctpCountersOutputs), // this is just default, can be overriden by --ctp-config-proxy '--channel-config..' chan.c_str(), - dcs2dpl(ccdbhost)); + dcs2dpl(ccdbhost, bkhost, qchost, qcwriteperiod, ctpcfgdir)); + ctpProxy.labels.emplace_back(DataProcessorLabel{"input-proxy"}); LOG(info) << "===> Proxy done"; WorkflowSpec workflow; workflow.emplace_back(ctpProxy); diff --git a/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx b/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx index 96694934b755a..0a4eeb64cf3af 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx @@ -28,6 +28,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" #include "Framework/ControlService.h" +#include "Framework/RawDeviceService.h" #include "Framework/Logger.h" #include "Framework/Lifetime.h" #include "Framework/ConfigParamSpec.h" @@ -45,22 +46,18 @@ using DetID = o2::detectors::DetID; InjectorFunction dcs2dpl() // InjectorFunction dcs2dpl() { - return [](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId) { - // make sure just 2 messages received - if (parts.Size() != 2) { - LOG(error) << "received " << parts.Size() << " instead of 2 expected"; - return; - } + return [](TimingInfo&, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) -> bool { + auto* device = services.get().device(); std::string messageHeader{static_cast(parts.At(0)->GetData()), parts.At(0)->GetSize()}; size_t dataSize = parts.At(1)->GetSize(); std::string messageData{static_cast(parts.At(1)->GetData()), parts.At(1)->GetSize()}; - LOG(info) << "received message " << messageHeader << " of size " << dataSize; // << " Payload:" << messageData; + LOG(info) << "received message " << messageHeader << " of size " << dataSize << "#parts:" << parts.Size(); // << " Payload:" << messageData; o2::header::DataHeader hdrF("CTP_COUNTERS", o2::header::gDataOriginCTP, 0); OutputSpec outsp{hdrF.dataOrigin, hdrF.dataDescription, hdrF.subSpecification}; auto channel = channelRetriever(outsp, newTimesliceId); if (channel.empty()) { LOG(error) << "No output channel found for OutputSpec " << outsp; - return; + return false; } hdrF.tfCounter = newTimesliceId; // this also hdrF.payloadSerializationMethod = o2::header::gSerializationMethodNone; @@ -69,7 +66,7 @@ InjectorFunction dcs2dpl() hdrF.payloadSize = parts.At(0)->GetSize() + parts.At(1)->GetSize() + 1; hdrF.firstTForbit = 0; // this should be irrelevant for Counters ? Orbit is in payload - auto fmqFactory = device.GetChannel(channel).Transport(); + auto fmqFactory = device->GetChannel(channel).Transport(); o2::header::Stack headerStackF{hdrF, DataProcessingHeader{newTimesliceId, 1}}; auto hdMessageF = fmqFactory->CreateMessage(headerStackF.size(), fair::mq::Alignment{64}); @@ -92,8 +89,9 @@ InjectorFunction dcs2dpl() fair::mq::Parts outParts; outParts.AddPart(std::move(hdMessageF)); outParts.AddPart(std::move(plMessageF)); - sendOnChannel(device, outParts, channel, newTimesliceId); + sendOnChannel(*device, outParts, channel, newTimesliceId); LOG(info) << "Sent CTP counters DPL message" << std::flush; + return true; }; } @@ -137,6 +135,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) // this is just default, can be overriden by --ctp-config-proxy '--channel-config..' chan.c_str(), dcs2dpl()); + ctpProxy.labels.emplace_back(DataProcessorLabel{"input-proxy"}); LOG(info) << "===> Proxy done"; WorkflowSpec workflow; workflow.emplace_back(ctpProxy); diff --git a/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx b/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx new file mode 100644 index 0000000000000..74d4a905c93e2 --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx @@ -0,0 +1,274 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ctpCCDBManager.cxx +/// \author Roman Lietava +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "DataFormatsCTP/Configuration.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include +#include +#include "CommonUtils/StringUtils.h" +#include +using namespace o2::ctp; +std::string ctpCCDBManager::mCCDBHost = "http://o2-ccdb.internal"; +std::string ctpCCDBManager::mQCDBHost = "http://ali-qcdb.cern.ch:8083"; +// std::string ctpCCDBManager::mQCDBHost = "none"; +// +int ctpCCDBManager::saveRunScalersToCCDB(CTPRunScalers& scalers, long timeStart, long timeStop) +{ + // data base + if (mCCDBHost == "none") { + LOG(debug) << "Scalers not written to CCDB none"; + return 0; + } + // CTPActiveRun* run = mActiveRuns[i]; + using namespace std::chrono_literals; + std::chrono::seconds days3 = 259200s; + std::chrono::seconds min10 = 600s; + long time3days = std::chrono::duration_cast(days3).count(); + long time10min = std::chrono::duration_cast(min10).count(); + long tmin = timeStart - time10min; + long tmax = timeStop + time3days; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + metadata["runNumber"] = std::to_string(scalers.getRunNumber()); + api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation + // store abitrary user object in strongly typed manner + int ret = api.storeAsTFileAny(&(scalers), mCCDBPathCTPScalers, metadata, tmin, tmax); + if (ret == 0) { + LOG(info) << "CTP scalers saved in ccdb:" << mCCDBHost << " run:" << scalers.getRunNumber() << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(fatal) << "Problem writing to database ret:" << ret; + } + return ret; +} +int ctpCCDBManager::saveRunScalersToQCDB(CTPRunScalers& scalers, long timeStart, long timeStop) +{ + // data base + if (mQCDBHost == "none") { + LOG(debug) << "Scalers not written to QCDB none"; + return 0; + } + // CTPActiveRun* run = mActiveRuns[i];q + using namespace std::chrono_literals; + std::chrono::seconds days3 = 259200s; + std::chrono::seconds min10 = 600s; + long time3days = std::chrono::duration_cast(days3).count(); + long time10min = std::chrono::duration_cast(min10).count(); + long tmin = timeStart - time10min; + long tmax = timeStop + time3days; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + metadata["runNumber"] = std::to_string(scalers.getRunNumber()); + api.init(mQCDBHost.c_str()); // or http://localhost:8080 for a local installation + // store abitrary user object in strongly typed manner + int ret = api.storeAsTFileAny(&(scalers), mQCDBPathCTPScalers, metadata, tmin, tmax); + if (ret == 0) { + LOG(info) << "CTP scalers saved in qcdb:" << mQCDBHost << " run:" << scalers.getRunNumber() << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(fatal) << "CTP scalers Problem writing to database qcdb ret:" << ret; + } + return ret; +} +int ctpCCDBManager::saveRunConfigToCCDB(CTPConfiguration* cfg, long timeStart) +{ + // data base + if (mCCDBHost == "none") { + LOG(info) << "CTP config not written to CCDB none"; + return 0; + } + using namespace std::chrono_literals; + std::chrono::seconds days3 = 259200s; + std::chrono::seconds min10 = 600s; + long time3days = std::chrono::duration_cast(days3).count(); + long time10min = std::chrono::duration_cast(min10).count(); + long tmin = timeStart - time10min; + long tmax = timeStart + time3days; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + metadata["runNumber"] = std::to_string(cfg->getRunNumber()); + api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation + // store abitrary user object in strongly typed manner + int ret = api.storeAsTFileAny(cfg, CCDBPathCTPConfig, metadata, tmin, tmax); + if (ret == 0) { + LOG(info) << "CTP config saved in ccdb:" << mCCDBHost << " run:" << cfg->getRunNumber() << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(fatal) << "CTPConfig: Problem writing to database ret:" << ret; + } + return ret; +} +int ctpCCDBManager::saveSoxOrbit(uint32_t runNumber, uint32_t soxOrbit, long timestamp) +{ + // data base + if (mCCDBHost == "none") { + LOG(info) << "SOX Orbit not written to CCDB none"; + return 0; + } + std::vector vect; + if (timestamp == 0) { + auto now = std::chrono::system_clock::now(); + timestamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + } + vect.push_back(timestamp); + vect.push_back((uint64_t)runNumber); + vect.push_back((uint64_t)soxOrbit); + long tmin = timestamp / 1000; + long tmax = tmin + 381928219; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + metadata["runNumber"] = std::to_string(runNumber); + api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation + + // store abitrary user object in strongly typed manner + int ret = api.storeAsTFileAny(&vect, mCCDBPathSoxOrbit, metadata, tmin, tmax); + if (ret == 0) { + LOG(info) << "SOX orbit saved in ccdb:" << mCCDBHost << " run:" << runNumber << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(fatal) << "SOX orbit Problem writing to database ret:" << ret; + } + return 0; +} +int ctpCCDBManager::saveOrbitReset(long timeStamp) +{ + // data base + if (mCCDBHost == "none") { + LOG(info) << "Orbit Reset not written to CCDB none"; + return 0; + } + std::vector vect; + if (timeStamp == 0) { + auto now = std::chrono::system_clock::now(); + timeStamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + LOG(warn) << "Received timestamp = 0 , using current time:" << timeStamp; + } + vect.push_back(timeStamp); + long tmin = timeStamp / 1000; + long tmax = tmin + 381928219; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation + // int ret = api.storeAsTFileAny(&vect, mCCDBPathOrbitReset, metadata, tmin, tmax); + std::cout << "Storing:" << mCCDBPathOrbitReset << " tmin:" << tmin << " tmax:" << tmax << " ts:" << timeStamp << std::endl; + std::string filename = "orbitReset.root"; + auto classname = "std::vector"; + metadata["adjustableEOV"] = "true"; + int ret = api.storeAsTFileAny(&(vect), mCCDBPathOrbitReset, metadata, tmin, tmax); + o2::ccdb::CcdbObjectInfo oi(mCCDBPathOrbitReset, classname, filename, metadata, tmin, tmax); + adjustOverriddenEOV(api, oi); + if (ret == 0) { + LOG(info) << "Orbit reset saved in ccdb:" << mCCDBHost << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(fatal) << "Orbit reset Problem writing to database ret:" << ret; + } + return 0; +} +int ctpCCDBManager::saveCtpCfg(uint32_t runNumber, long timeStart) +{ + if (mCCDBHost == "none") { + LOG(info) << "CtpCfg not written to CCDB none"; + return 0; + } + CtpCfg ctpcfg; + int ret = ctpcfg.readAndSave(mCtpCfgDir); + if (ret == 0) { + using namespace std::chrono_literals; + std::chrono::seconds days3 = 259200s; + std::chrono::seconds min10 = 600s; + long time3days = std::chrono::duration_cast(days3).count(); + long time10min = std::chrono::duration_cast(min10).count(); + long tmin = timeStart - time10min; + long tmax = timeStart + time3days; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + metadata["runNumber"] = std::to_string(runNumber); + api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation + // store abitrary user object in strongly typed manner + ret = api.storeAsTFileAny(&ctpcfg, mCCDBPathCtpCfg, metadata, tmin, tmax); + if (ret == 0) { + LOG(info) << "CtpCfg saved in ccdb:" << mCCDBHost << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(error) << "CtpCfg Problem writing to database ret:" << ret; + } + } + return ret; +} +CTPConfiguration ctpCCDBManager::getConfigFromCCDB(long timestamp, std::string run, bool& ok) +{ + + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(mCCDBHost); + std::map metadata; // can be empty + metadata["runNumber"] = run; + if (timestamp == 0) { + // Timestamp + auto soreor = mgr.getRunDuration(std::stoi(run)); + timestamp = (soreor.second - soreor.first) / 2 + soreor.first; + } + auto ctpconfigdb = mgr.getSpecific(CCDBPathCTPConfig, timestamp, metadata); + if (ctpconfigdb == nullptr) { + LOG(info) << "CTP config not in database, timestamp:" << timestamp; + ok = 0; + } else { + // ctpconfigdb->printStream(std::cout); + LOG(info) << "CTP config found. Run:" << run; + ok = 1; + } + return *ctpconfigdb; +} +CTPConfiguration ctpCCDBManager::getConfigFromCCDB(long timestamp, std::string run) +{ + bool ok; + auto ctpconfig = getConfigFromCCDB(timestamp, run, ok); + if (ok == 0) { + LOG(error) << "CTP config not in CCDB"; + return CTPConfiguration(); + } + return ctpconfig; +} +CTPRunScalers ctpCCDBManager::getScalersFromCCDB(long timestamp, std::string run, bool& ok) +{ + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(mCCDBHost); + std::map metadata; // can be empty + metadata["runNumber"] = run; + auto ctpscalers = mgr.getSpecific(mCCDBPathCTPScalers, timestamp, metadata); + if (ctpscalers == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timestamp; + ok = 0; + } else { + // ctpscalers->printStream(std::cout); + ok = 1; + } + return *ctpscalers; +} +CTPRunScalers ctpCCDBManager::getScalersFromCCDB(long timestamp, std::string run, std::string path, bool& ok) +{ + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(mCCDBHost); + std::map metadata; // can be empty + metadata["runNumber"] = run; + if (timestamp == 0) { + // Timestamp + auto soreor = mgr.getRunDuration(std::stoi(run)); + timestamp = (soreor.second - soreor.first) / 2 + soreor.first; + } + auto ctpscalers = mgr.getSpecific(path, timestamp, metadata); + if (ctpscalers == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timestamp; + ok = 0; + } else { + // ctpscalers->printStream(std::cout); + ok = 1; + } + return *ctpscalers; +} \ No newline at end of file diff --git a/Detectors/Calibration/README.md b/Detectors/Calibration/README.md index 88bd201dab975..be519be405c07 100644 --- a/Detectors/Calibration/README.md +++ b/Detectors/Calibration/README.md @@ -42,6 +42,13 @@ In order to prepare only one CCDB object at the end of the run you can use `setU See e.g. LHCClockCalibrator.h/cxx in AliceO2/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h and AliceO2/Detectors/TOF/calibration/srcLHCClockCalibrator.cxx +Sometimes it might be useful to define the 1st slot of the run shorter than the nominal slot length, e.g. to not rely to long on the previous run or default calibration. In this case one can impose to the +calibration class an offset using method `setStartOffsetFrac(float offset)`, where `offset` is a fractional of the nominal slot length to be subtracted from the nominal boundaries of all slots (except the +very 1st one, whose startTF is set to 0). The fractional offset should be in `[0:0.95)` range, any value outside this range will be overridden to the nearest boundary. +This feature is supported only for the finite slot-length calibrations. E.g. if the nominal slot length is 10 minutes `(==~210000 TFs)`, setting `setStartOffsetFrac(float 0.7)` will lead to 1st slot +finalized after the first 3 minutes, while the rest of the slots will be defined with nominal 10 minutes coverage. If statistics of this 1st short slot is insufficient, it will be merged as usual +with the next slot (note this if this happens, in the example above the 1st calibration will be available in 13 minutes...). + ## TimeSlot The TimeSlot is a templated class which takes as input type the Container that will hold the calibration data needed to produce the calibration objects (histograms, vectors, array...). Each calibration device could implement its own Container, according to its needs. @@ -154,7 +161,11 @@ o2-calibration-ccdb-populator-workflow --sspec-min 0 --sspec-max 1 -b then the `ObjA` will be uploaded only to the default server (`http://alice-ccdb.cern.ch`), `ObjB` will be uploaded to both default and `local` server and `ObjC` will be uploaded to the `local` server only. -By default the ccdb-populator-workflow will not produce `fatal` on failed upload. To require it an option `--fatal-on-failure` can be used. +By default the `ccdb-populator-workflow` will not produce `fatal` on failed upload. To require it an option `--fatal-on-failure` can be used. + +By default the `ccdb-populator-workflow` uploads objects as it gets them. In case there is a danger that objects of the same URL will arrive in the order not sorted in SOV +(which may lead to screaning of the object with later SOV by other object if earlier ROF) one can use an option `--ordering-latency ` of the `ccdb-populator-workflow`. +Then every incoming object will be buffered and uploaded only if no object with the same CCDB path and earlier start of validity was received in preceding N milliseconds. All remaining cached objects are uploaded at EOR (or stop() method call). + +# FT0 Base + +## Geometry + +The `o2::ft0::Geometry` class contains dimensions and information used for constructing the FT0 geometry as used in simulation. It also provides utility methods for retrieving the center locations of the detector cells. See the below example for how to use the `Geometry` class to query the FT0 cell locations. Note that these are the ideal locations, and that any misalignment is not considered. + +```cpp +o2::ft0::Geometry ft0Geometry; +ft0Geometry.calculateChannelCenter(); +TVector3 cellPosition = ft0Geometry.getChannelCenter(chId); +float x = cellPosition.X(); +float y = cellPosition.Y(); +float z = cellPosition.Z(); +``` diff --git a/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h b/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h index fb8b6492354f1..bcdb9f0386ee3 100644 --- a/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h +++ b/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file FT0DigParam.h +/// \file FT0DigParam.h /// \brief Configurable digitization parameters #ifndef ALICEO2_FT0_DIG_PARAM @@ -29,25 +29,29 @@ struct FT0DigParam : o2::conf::ConfigurableParamHelper { float mAmp_trsh = 100; // [ph.e] float mAmpRecordLow = -4; // integrate charge from float mAmpRecordUp = 15; // to [ns] - float mC_side_cable_cmps = 2.86; // ns - float mA_side_cable_cmps = 11.110; // ns - int mtrg_central_trh = 600.; // channels - int mtrg_semicentral_trh = 300.; // channels + float hitTimeOffsetA = 0; ///< hit time offset on the A side [ns] + float hitTimeOffsetC = 0; ///< hit time offset on the C side [ns] + int mtrg_central_trh = 40; // Tclu units (40 for pp and 1433 for PbPb in Run3) + int mtrg_semicentral_trh = 20; // Tclu units (20 for pp and 35 for PbPb in Run3) float mMip_in_V = 7; // MIP to mV float mPe_in_mip = 0.004; // invserse Np.e. in MIP 1./250. float mCfdShift = 1.66; // ns - float mCFDShiftPos = 1.47; //// shift positive part of CFD signal; distance between 0.3 of max amplitude to max + float mCFDShiftPos = 1.47; // shift positive part of CFD signal; distance between 0.3 of max amplitude to max float mCFDdeadTime = 15.6; // ns float mCharge2amp = 0.22; - float mNoiseVar = 0.1; // noise level - float mNoisePeriod = 1 / 0.9; // GHz low frequency noise period; - static constexpr short mTime_trg_gate = 153; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) - static constexpr float mAmpThresholdForReco = 5; // only channels with amplitude higher will participate in calibration and collision time: 0.3 MIP - static constexpr short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time - - static constexpr float mMV_2_Nchannels = 2.2857143; // amplitude channel 7 mV ->16channels - static constexpr float mMV_2_NchannelsInverse = 0.437499997; // inverse amplitude channel 7 mV ->16channels + float mNoiseVar = 0.1; // noise level + float mNoisePeriod = 1 / 0.9; // GHz low frequency noise period; + short mTime_trg_gate = 153; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) + short mTime_trg_vertex_gate = 100; // #channels as in TCM for VTX trigger + float mAmpThresholdForReco = 5; // only channels with amplitude higher will participate in calibration and collision time: 0.3 MIP + short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time + + float mMV_2_Nchannels = 2.; // amplitude channel 7 mV ->14channels + float mMV_2_NchannelsInverse = 0.5; // inverse amplitude channel 7 mV ->14channels (nowhere used) + + float Cross_Talk_Frac = 0.10f; // Crosstalk between channels + float mAmpThresholdForCrossTalkDigit = 5.f; // Treshold for low crosstalk signals O2ParamDef(FT0DigParam, "FT0DigParam"); }; diff --git a/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h b/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h index 0365e730828cb..83be3af4d5b71 100644 --- a/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h +++ b/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h @@ -65,11 +65,34 @@ class Geometry return o2::base::GeometryManager::getPNEntry(getDetID(), index); } + // NEW: + // Calculate the coordinates of all the channels. + void calculateChannelCenter(); + // Get the coordinates of the center of the channel channelId. + TVector3 getChannelCenter(UInt_t channelId) const { return mChannelCenter[channelId]; } + private: TVector3 mMCP[52]; TVector3 mAngles[28]; + std::array mChannelCenter; ///< NEW: Center of each channel in FT0 A (96) and C (112) combined. + + // Convert the local ordering of the channels to the official one and apply it to the channel map. + // localChannelOrder[local channel index] = official channel. + Int_t localChannelOrder[Nchannels] = { + 58, 56, 59, 57, 54, 52, 55, 53, 50, 49, 51, 48, 47, 45, 46, 44, 43, 42, 41, 40, + 61, 60, 63, 62, 14, 12, 15, 13, 10, 9, 11, 8, 7, 6, 5, 4, 39, 38, 37, 36, + 65, 64, 66, 67, 17, 16, 18, 19, 3, 2, 0, 1, 35, 34, 32, 33, + 68, 69, 70, 71, 20, 21, 22, 23, 24, 27, 25, 26, 29, 31, 28, 30, 94, 95, 92, 93, + 72, 73, 74, 75, 76, 78, 77, 79, 80, 83, 81, 82, 85, 87, 84, 86, 89, 91, 88, 90, + 173, 172, 175, 174, 206, 207, 204, 205, 169, 168, 171, 170, 202, 203, 200, 201, + 117, 116, 119, 118, 142, 143, 140, 141, 114, 112, 115, 113, 137, 139, 136, 138, + 166, 164, 167, 165, 197, 199, 196, 198, 110, 108, 111, 109, 133, 135, 132, 134, + 162, 160, 163, 161, 193, 195, 192, 194, 107, 105, 106, 104, 128, 130, 129, 131, + 159, 157, 158, 156, 188, 190, 189, 191, 99, 98, 97, 96, 120, 121, 122, 123, + 103, 102, 101, 100, 124, 125, 126, 127, 155, 153, 154, 152, 184, 186, 185, 187, + 147, 146, 145, 144, 176, 177, 178, 179, 151, 150, 149, 148, 180, 181, 182, 183}; - ClassDefNV(Geometry, 2); + ClassDefNV(Geometry, 3); }; } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/base/src/Geometry.cxx b/Detectors/FIT/FT0/base/src/Geometry.cxx index de9b3ddad4c21..4c5fa004f63bb 100644 --- a/Detectors/FIT/FT0/base/src/Geometry.cxx +++ b/Detectors/FIT/FT0/base/src/Geometry.cxx @@ -117,3 +117,61 @@ void Geometry::setCsideModules() mMCP[i + NCellsA].SetXYZ(xc2[i], yc2[i], zc2[i]); } } +void Geometry::calculateChannelCenter() +{ + // This method calculates the position of each channel center composing both FT0-A + // and FT0-C, based on the position of their corresponding modules given by + // "Geometry::setAsideModules()" and "Geometry::setCsideModules()". + + // Ensure the positions of the modules are well defined. + setAsideModules(); + setCsideModules(); + + // Calculate first the positions for the channels for FT0-A. These correspond to the + // channels 0-95 in the modules mMCP[0-23]. + Double_t delta = 5.3 / 4.; // Half-channel width (TODO: ask if it actually corresponds to ChannelWidth) + Double_t xLocalChannels[Nchannels]; // x-positions of all channels ordered according to xi and internal numbering. + Double_t yLocalChannels[Nchannels]; // y-positions of all channels ordered according to yi and internal numbering. + Double_t zLocalChannels[Nchannels]; // z-positions of all channels ordered according to zi and internal numbering. + // INFO: We assume here the modules are perpendicular to z, so z(channel) = z(module). + + for (int iModA = 0; iModA < NCellsA; iModA++) { + xLocalChannels[4 * iModA + 0] = mMCP[iModA].X() - delta; + xLocalChannels[4 * iModA + 1] = mMCP[iModA].X() + delta; + xLocalChannels[4 * iModA + 2] = mMCP[iModA].X() - delta; + xLocalChannels[4 * iModA + 3] = mMCP[iModA].X() + delta; + + yLocalChannels[4 * iModA + 0] = mMCP[iModA].Y() + delta; + yLocalChannels[4 * iModA + 1] = mMCP[iModA].Y() + delta; + yLocalChannels[4 * iModA + 2] = mMCP[iModA].Y() - delta; + yLocalChannels[4 * iModA + 3] = mMCP[iModA].Y() - delta; + + zLocalChannels[4 * iModA + 0] = mMCP[iModA].Z(); + zLocalChannels[4 * iModA + 1] = mMCP[iModA].Z(); + zLocalChannels[4 * iModA + 2] = mMCP[iModA].Z(); + zLocalChannels[4 * iModA + 3] = mMCP[iModA].Z(); + } + + // Calculate then the positions for the channels for FT0-C, corresponding to the + // channels 96-207 in the modules mMCP[24-51]. + for (int iModC = 0; iModC < NCellsC; iModC++) { + xLocalChannels[4 * (iModC + NCellsA) + 0] = mMCP[iModC + NCellsA].X() - delta; + xLocalChannels[4 * (iModC + NCellsA) + 1] = mMCP[iModC + NCellsA].X() + delta; + xLocalChannels[4 * (iModC + NCellsA) + 2] = mMCP[iModC + NCellsA].X() - delta; + xLocalChannels[4 * (iModC + NCellsA) + 3] = mMCP[iModC + NCellsA].X() + delta; + + yLocalChannels[4 * (iModC + NCellsA) + 0] = mMCP[iModC + NCellsA].Y() + delta; + yLocalChannels[4 * (iModC + NCellsA) + 1] = mMCP[iModC + NCellsA].Y() + delta; + yLocalChannels[4 * (iModC + NCellsA) + 2] = mMCP[iModC + NCellsA].Y() - delta; + yLocalChannels[4 * (iModC + NCellsA) + 3] = mMCP[iModC + NCellsA].Y() - delta; + + zLocalChannels[4 * (iModC + NCellsA) + 0] = mMCP[iModC + NCellsA].Z(); + zLocalChannels[4 * (iModC + NCellsA) + 1] = mMCP[iModC + NCellsA].Z(); + zLocalChannels[4 * (iModC + NCellsA) + 2] = mMCP[iModC + NCellsA].Z(); + zLocalChannels[4 * (iModC + NCellsA) + 3] = mMCP[iModC + NCellsA].Z(); + } + + for (int iChannel = 0; iChannel < Nchannels; iChannel++) { + mChannelCenter[localChannelOrder[iChannel]].SetXYZ(xLocalChannels[iChannel], yLocalChannels[iChannel], zLocalChannels[iChannel]); + } +} diff --git a/Detectors/FIT/FT0/calibration/CMakeLists.txt b/Detectors/FIT/FT0/calibration/CMakeLists.txt index ac341d1a3db12..bee0493d300c1 100644 --- a/Detectors/FIT/FT0/calibration/CMakeLists.txt +++ b/Detectors/FIT/FT0/calibration/CMakeLists.txt @@ -10,63 +10,50 @@ # or submit itself to any jurisdiction. o2_add_library(FT0Calibration - SOURCES - src/FT0TimeOffsetSlotContainer.cxx - src/GlobalOffsetsContainer.cxx - src/FT0CalibTimeSlewing.cxx - src/FT0CalibCollector.cxx - PUBLIC_LINK_LIBRARIES - O2::CCDB - O2::MathUtils - O2::DataFormatsFT0 - O2::CommonDataFormat - O2::DetectorsCalibration - O2::DataFormatsGlobalTracking - ROOT::Minuit - Microsoft.GSL::GSL - ) - o2_target_root_dictionary(FT0Calibration - HEADERS - include/FT0Calibration/FT0TimeOffsetSlotContainer.h - include/FT0Calibration/GlobalOffsetsContainer.h - include/FT0Calibration/FT0CalibTimeSlewing.h - include/FT0Calibration/FT0CalibCollector.h - ) - o2_add_executable(ft0-time-offset-calib - COMPONENT_NAME calibration - SOURCES testWorkflow/FT0TimeOffsetCalibration-Workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration O2::FITCalibration - ) - o2_add_executable(ft0-time-spectra-processor - COMPONENT_NAME calibration - SOURCES testWorkflow/FT0TimeSpectraProcessor-Workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) - o2_add_executable(ft0-slew-upload - COMPONENT_NAME calibration - SOURCES testWorkflow/slew_upload.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) - o2_add_executable(ft0-global-offsets - COMPONENT_NAME calibration - SOURCES testWorkflow/GlobalOffsetsCalibrationWorkflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration O2::FITCalibration - ) - o2_add_executable(ft0-collect-global-offsets-workflow - COMPONENT_NAME calibration - SOURCES testWorkflow/GlobalOffsetsCollectWorkflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) - o2_add_executable(ft0-collect-calib-workflow - COMPONENT_NAME calibration - SOURCES testWorkflow/ft0-collect-calib-workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) + SOURCES + src/FT0TimeOffsetSlotContainer.cxx + src/EventsPerBcCalibrator.cxx + PUBLIC_LINK_LIBRARIES + O2::DetectorsCalibration + O2::Framework + O2::CommonUtils + Microsoft.GSL::GSL + O2::DataFormatsFT0 + O2::CommonDataFormat + O2::Steer + O2::CCDB + ROOT::Minuit + ROOT::Hist + ) -o2_data_file(COPY files DESTINATION Detectors/FT0/) +o2_target_root_dictionary(FT0Calibration + HEADERS + include/FT0Calibration/FT0TimeOffsetSlotContainer.h + include/FT0Calibration/EventsPerBcCalibrator.h + ) + +o2_add_executable(ft0-time-offset-calib + COMPONENT_NAME calibration + SOURCES + workflow/FT0TimeOffsetCalibration-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration O2::FITCalibration + ) + +o2_add_executable(ft0-time-spectra-processor + COMPONENT_NAME calibration + SOURCES + workflow/FT0TimeSpectraProcessor-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration + ) + +o2_add_executable(ft0-events-per-bc-processor + COMPONENT_NAME calibration + SOURCES + workflow/FT0EventsPerBcProcessor-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration + O2::Framework + O2::CCDB +) \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/README.md b/Detectors/FIT/FT0/calibration/README.md new file mode 100644 index 0000000000000..78b0f980400d2 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/README.md @@ -0,0 +1,62 @@ +# Calibrations + +## Events per BC Calibration +### Description +Generates histograms of **Events per Bunch Crossing (BC)**. Events can be filtered by applying amplitude thresholds to the **A-side** and **C-side**. + +### Command-Line Options +| Option | Default | Description | +| :--- | :--- | :--- | +| `--slot-len-sec` | `3600` | Duration of each slot in seconds. | +| `--slot-len-tf` | `0` | Slot length in Time Frames (TFs). | +| `--one-object-per-run` | — | If set, the workflow creates only one calibration object per run. | +| `--min-entries-number` | `0` | Minimum number of entries required for a slot to be valid. | +| `--min-ampl-side-a` | `-2147483648` | Amplitude threshold for Side A events. | +| `--min-ampl-side-c` | `-2147483648` | Amplitude threshold for Side C events. | + +--- + +## How to Run + +### Simulation Data +First, it is important to digitize data with a non-zero run number, orbit, and timestamp. To set these parameters, one can use the `--configKeyValues` option, as shown in the example below. +``` +o2-sim-digitizer-workflow \ +--onlyDet FT0 \ +--configKeyValues="HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=128;HBFUtils.orbitFirstSampled=256;HBFUtils.runNumber=560560;HBFUtils.startTime=1768464099000" +``` + +To process simulation data, digits must first be converted to RAW format. The `o2-ft0-digi2raw` tool performs this conversion and generates the required configuration file. + +Once converted, you can run the calibration either as a single integrated workflow or by spawning as the sender and receiver components separately. + +#### Single Workflow Example +Execute the following command within the simulation directory: +``` +o2-raw-file-reader-workflow --input-conf FT0raw.cfg --loop -1 \ +| o2-ft0-flp-dpl-workflow --condition-backend=http://localhost:8080 \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080 +``` + +Sender example (in simulation directory): +``` +o2-raw-file-reader-workflow --input-conf FT0raw.cfg --loop -1 \ +| o2-ft0-flp-dpl-workflow --condition-backend=http://localhost:8080 \ +| o2-dpl-output-proxy --channel-config "name=downstream,method=connect,address=tcp://localhost:30453,type=push,transport=zeromq" --dataspec "downstream:FT0/DIGITSBC" +``` + +Receiver example: +``` +o2-dpl-raw-proxy --channel-config "name=readout-proxy,type=pull,method=bind,address=tcp://localhost:30453,rateLogging=1,transport=zeromq" --dataspec "A:FT0/DIGITSBC/0" \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10 --min-ampl-side-a=0" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080/ +``` + +### CTF Data +Example: +``` +o2-ctf-reader-workflow --ctf-input ctf.root --onlyDet FT0 \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080/ +``` \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/files/dispatcherCalib.json b/Detectors/FIT/FT0/calibration/files/dispatcherCalib.json deleted file mode 100644 index edaf486043790..0000000000000 --- a/Detectors/FIT/FT0/calibration/files/dispatcherCalib.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "dataSamplingPolicies": [ - { - "id": "sampling1", - "active": "true", - "machines": [ - "localhost" - ], - "query": "digits:FT0/DIGITSBC/0;channels:FT0/DIGITSCH/0", - "outputs": "digits_sampled:FT0/SUB_DIGITSBC/0;channels_sampled:FT0/SUB_DIGITSCH/0", - "samplingConditions": [ - { - "condition": "random", - "fraction": "0.99", - "seed": "1234" - } - ], - "blocking": "false" - } - ] -} diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h new file mode 100644 index 0000000000000..d831cc36201ab --- /dev/null +++ b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h @@ -0,0 +1,83 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_FT0TVXPERBCID +#define O2_FT0TVXPERBCID + +#include +#include +#include +#include + +#include "CommonDataFormat/FlatHisto2D.h" +#include "CommonConstants/LHCConstants.h" +#include "DataFormatsFT0/SpectraInfoObject.h" +#include "DataFormatsFT0/Digit.h" +#include "DataFormatsFT0/EventsPerBc.h" +#include "DetectorsCalibration/TimeSlotCalibration.h" +#include "DetectorsCalibration/TimeSlot.h" +#include "CommonDataFormat/TFIDInfo.h" +#include "TH1F.h" +#include "Rtypes.h" + +namespace o2::ft0 +{ +struct EventsPerBcContainer { + EventsPerBcContainer(int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude) : mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC), mMinSumOfAmplitude(minSumOfAmplitude) {} + + size_t getEntries() const { return entries; } + void print() const; + void fill(const o2::dataformats::TFIDInfo& ti, const gsl::span data); + void merge(const EventsPerBcContainer* prev); + + const int32_t mMinAmplitudeSideA; + const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; + + std::array mTvx{0.0}; + size_t entries{0}; + long startTimeStamp{0}; + long stopTimeStamp{0}; + + ClassDefNV(EventsPerBcContainer, 1); +}; + +class EventsPerBcCalibrator final : public o2::calibration::TimeSlotCalibration +{ + using Slot = o2::calibration::TimeSlot; + using TFType = o2::calibration::TFType; + using EventsHistogram = std::array; + + public: + EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude); + + bool hasEnoughData(const Slot& slot) const override; + void initOutput() override; + void finalizeSlot(Slot& slot) override; + Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend) override; + + const std::vector& getTvxPerBc() { return mTvxPerBcs; } + std::vector>& getTvxPerBcCcdbInfo() { return mTvxPerBcInfos; } + + private: + const uint32_t mMinNumberOfEntries; + const int32_t mMinAmplitudeSideA; + const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; + + std::vector mTvxPerBcs; + std::vector> mTvxPerBcInfos; + + ClassDefOverride(EventsPerBcCalibrator, 1); +}; +} // namespace o2::ft0 + +#endif diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibCollector.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibCollector.h deleted file mode 100644 index bf6f4ed6c3ad6..0000000000000 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibCollector.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef FT0_CALIB_COLLECTOR_H_ -#define FT0_CALIB_COLLECTOR_H_ - -#include "DetectorsCalibration/TimeSlotCalibration.h" -#include "DetectorsCalibration/TimeSlot.h" -#include "DataFormatsFT0/FT0CalibrationInfoObject.h" -#include "FT0Base/Geometry.h" - -#include - -namespace o2 -{ -namespace ft0 -{ - -class FT0CalibInfoSlot -{ - - using Slot = o2::calibration::TimeSlot; - using Geo = o2::ft0::Geometry; - - public: - static constexpr int NCHANNELS = Geo::Nchannels; - static constexpr int HISTO_RANGE = 200; - - FT0CalibInfoSlot() - { - for (int ch = 0; ch < NCHANNELS; ch++) { - mEntriesSlot[ch] = 0; - } - } - - ~FT0CalibInfoSlot() = default; - - void print() const; - void printEntries() const; - void fill(const gsl::span data); - void merge(const FT0CalibInfoSlot* prev); - - auto& getEntriesPerChannel() const { return mEntriesSlot; } - auto& getEntriesPerChannel() { return mEntriesSlot; } - auto& getCollectedCalibInfoSlot() { return mFT0CollectedCalibInfoSlot; } - auto& getCollectedCalibInfoSlot() const { return mFT0CollectedCalibInfoSlot; } - - private: - std::array mEntriesSlot; // vector containing number of entries per channel - std::vector mFT0CollectedCalibInfoSlot; ///< output FT0 calibration info - - ClassDefNV(FT0CalibInfoSlot, 1); -}; - -class FT0CalibCollector final : public o2::calibration::TimeSlotCalibration -{ - using TFType = o2::calibration::TFType; - using Slot = o2::calibration::TimeSlot; - static constexpr int NCHANNELS = o2::ft0::Geometry::Nchannels; - - public: - FT0CalibCollector(bool TFsendingPolicy, int maxNumOfHits) : mTFsendingPolicy(TFsendingPolicy), mMaxNumOfHits(maxNumOfHits){}; - - ~FT0CalibCollector() final = default; - - bool hasEnoughData(const Slot& slot) const final; - void initOutput() final; - void finalizeSlot(Slot& slot) final; - Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend) final; - auto& getCollectedCalibInfo() const { return mFT0CollectedCalibInfo; } - auto& getEntriesPerChannel() const { return mEntries; } - void setIsMaxNumberOfHitsAbsolute(bool absNumber) { mAbsMaxNumOfHits = absNumber; } - - private: - bool mTFsendingPolicy = false; // whether we will send information at every TF or only when we have a certain statistics - int mMaxNumOfHits = 1000000; // maximum number of hits for one single channel to trigger the sending of the information (if mTFsendingPolicy = false) - bool mAbsMaxNumOfHits = true; // to decide if the mMaxNumOfHits should be multiplied by the number of FT0 channels - std::array mEntries; // vector containing number of entries per channel - std::vector mFT0CollectedCalibInfo; ///< output FT0 calibration info - - ClassDefOverride(FT0CalibCollector, 1); -}; - -} // end namespace ft0 -} // end namespace o2 - -#endif /* FT0 CALIB COLLECTOR */ diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibTimeSlewing.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibTimeSlewing.h deleted file mode 100644 index 41de777557c6a..0000000000000 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibTimeSlewing.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CalibTimeSlewingParamFT0.h -/// \brief Class to store the output of the matching to FT0 for calibration - -#ifndef ALICEO2_FT0CALIBTIMESLEWING_H -#define ALICEO2_FT0CALIBTIMESLEWING_H - -#include -#include -#include -#include -#include -#include -#include "Rtypes.h" -#include -#include "DataFormatsFT0/FT0CalibrationInfoObject.h" -#include "FT0Base/Geometry.h" -namespace o2::ft0 -{ -static constexpr int NCHANNELS = o2::ft0::Geometry::Nchannels; -class FT0CalibTimeSlewing -{ - public: - static constexpr int HISTOGRAM_RANGE_X = 4000; - static constexpr unsigned int NUMBER_OF_HISTOGRAM_BINS_X = HISTOGRAM_RANGE_X / 4; - static constexpr int HISTOGRAM_RANGE_Y = 200; - static constexpr unsigned int NUMBER_OF_HISTOGRAM_BINS_Y = HISTOGRAM_RANGE_Y; - FT0CalibTimeSlewing(); - - FT0CalibTimeSlewing(const FT0CalibTimeSlewing& source) = default; - - FT0CalibTimeSlewing& operator=(const FT0CalibTimeSlewing& source) = default; - - float getChannelOffset(int channel, int amplitude) const; - - const TGraph getGraph(int channel) const { return mTimeSlewing[channel]; } - std::array getGraphs() const { return mTimeSlewing; } - void fillGraph(int channel, TH2F* histo); - - float getSigmaPeak(int channel) const { return mSigmaPeak[channel]; } - void setSigmaPeak(int channel, float value) { mSigmaPeak[channel] = value; } - - ///< perform all initializations - void init(); - void mergeFilesWithTree(); - void fillHistos(TTree* tr); - TH2F* getTimeAmpHist(int channel) { return mTimeAmpHist[channel]; }; - void setSingleFileName(std::string name) { mSingleFileName = name; } - void setMergedFileName(std::string name) { mMergedFileName = name; } - void setNfiles(int nfiles) { mNfiles = nfiles; }; - - FT0CalibTimeSlewing& operator+=(const FT0CalibTimeSlewing& other); - static constexpr const char* getObjectPath() { return "FT0/Calib/SlewingCorrection"; } - - private: - // FT0 channel calibrations - std::array mTimeSlewing; ///< array of TGraph wirh time -amplitude for each channel - std::array mSigmaPeak; ///< array with the sigma of the peak - TFile* mMergedFile; // file with merged tree - TH2F* mTimeAmpHist[NCHANNELS]; // historgams time vs amplitude - int mNfiles; // number of files with stored Tree with CalibrationInfoObject - std::string mSingleFileName; - std::string mMergedFileName; - - ClassDefNV(FT0CalibTimeSlewing, 1); // class for FT0 time slewing params -}; -} // namespace o2::ft0 -#endif diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0TimeOffsetSlotContainer.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0TimeOffsetSlotContainer.h index 0786b0225b52c..2d7470d94e9e8 100644 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0TimeOffsetSlotContainer.h +++ b/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0TimeOffsetSlotContainer.h @@ -17,6 +17,8 @@ #include "CommonDataFormat/FlatHisto2D.h" #include "DataFormatsFT0/SpectraInfoObject.h" +#include "DetectorsCalibration/TimeSlotCalibration.h" +#include "DetectorsCalibration/TimeSlot.h" #include "TList.h" @@ -27,12 +29,14 @@ namespace o2::ft0 class FT0TimeOffsetSlotContainer final { static constexpr int sNCHANNELS = o2::ft0::Geometry::Nchannels; + using TimeSlot = o2::calibration::TimeSlot; + using TimeSlotCalibration = o2::calibration::TimeSlotCalibration; public: FT0TimeOffsetSlotContainer(std::size_t minEntries); // constructor is needed due to current version of FITCalibration library, should be removed FT0TimeOffsetSlotContainer(FT0TimeOffsetSlotContainer const&) = default; FT0TimeOffsetSlotContainer(FT0TimeOffsetSlotContainer&&) = default; - FT0TimeOffsetSlotContainer& operator=(FT0TimeOffsetSlotContainer const&) = default; + FT0TimeOffsetSlotContainer& operator=(FT0TimeOffsetSlotContainer&) = default; FT0TimeOffsetSlotContainer& operator=(FT0TimeOffsetSlotContainer&&) = default; bool hasEnoughEntries() const; void fill(const gsl::span& data); @@ -42,6 +46,8 @@ class FT0TimeOffsetSlotContainer final TimeSpectraInfoObject generateCalibrationObject(long tsStartMS, long tsEndMS, const std::string& pathToHists) const; typedef float FlatHistoValue_t; typedef o2::dataformats::FlatHisto2D FlatHisto2D_t; + auto getHistogram() const { return mHistogram; } + auto isFirstTF() const { return mIsFirstTF; } private: // Slot number @@ -55,7 +61,8 @@ class FT0TimeOffsetSlotContainer final bool mIsReady{false}; // Once it is upper than max entry threshold it stops increasing std::array mArrEntries{}; - + // Total number of events + uint64_t mTotalNevents{0}; // Contains all information about time spectra FlatHisto2D_t mHistogram; ClassDefNV(FT0TimeOffsetSlotContainer, 1); diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/GlobalOffsetsContainer.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/GlobalOffsetsContainer.h deleted file mode 100644 index c2f4c1b51ca8f..0000000000000 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/GlobalOffsetsContainer.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_GLOBALOFFSETSCONTAINER_H -#define O2_GLOBALOFFSETSCONTAINER_H - -#include "DataFormatsFT0/GlobalOffsetsInfoObject.h" -#include "DataFormatsFT0/GlobalOffsetsCalibrationObject.h" -#include "Rtypes.h" -#include -#include -#include -#include - -namespace o2::ft0 -{ -class GlobalOffsetsContainer final -{ - static constexpr int RANGE = 1000; - static constexpr unsigned int NBINS = 2 * RANGE; - - public: - explicit GlobalOffsetsContainer(std::size_t minEntries) - { - // mHisto.resize(NBINS, 0.); - } - - bool hasEnoughEntries() const; - void fill(const gsl::span& data); - int getMeanGaussianFitValue() const; - void merge(GlobalOffsetsContainer* prev); - void print() const; - GlobalOffsetsCalibrationObject generateCalibrationObject(long, long, const std::string&) const; - void updateFirstCreation(std::uint64_t creation) - { - if (creation < mFirstCreation) { - mFirstCreation = creation; - } - } - void resetFirstCreation() - { - - mFirstCreation = std::numeric_limits::max(); - } - std::uint64_t getFirstCreation() const - { - return mFirstCreation; - } - - private: - std::uint64_t mFirstCreation = std::numeric_limits::max(); - std::size_t mMinEntries; - std::array mHisto{}; - int mEntries = 0; - - ClassDefNV(GlobalOffsetsContainer, 2); -}; - -} // namespace o2::ft0 - -#endif // O2_GLOBALOFFSETCONTAINER_H diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/LHCClockCalibrator.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/LHCClockCalibrator.h deleted file mode 100644 index 42e69c199b946..0000000000000 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/LHCClockCalibrator.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef FT0_LHCPHASE_CALIBRATION_H_ -#define FT0_LHCPHASE_CALIBRATION_H_ - -#include "DetectorsCalibration/TimeSlotCalibration.h" -#include "DetectorsCalibration/TimeSlot.h" -#include "DataFormatsFT0/FT0CalibrationInfoObject.h" -#include "DataFormatsFT0/LHCphaseCalibrationObject.h" -#include "CommonConstants/LHCConstants.h" -#include "CommonUtils/NameConf.h" -#include "CCDB/CcdbObjectInfo.h" -#include - -namespace o2 -{ -namespace ft0 -{ - -struct LHCClockDataHisto { - float range = 1000. * o2::constants::lhc::LHCBunchSpacingNS * 0.5; // BC in PS - int nbins = 1000; - float v2Bin = nbins / (2 * range); - int entries = 0; - std::vector histo{0}; - - LHCClockDataHisto(); - - LHCClockDataHisto(int nb, float r) : nbins(nb), range(r), v2Bin(0) - { - if (r <= 0. || nb < 1) { - throw std::runtime_error("Wrong initialization of the histogram"); - } - v2Bin = nbins / (2 * range); - histo.resize(nbins, 0.); - } - - size_t getEntries() const { return entries; } - void print() const; - void fill(const gsl::span& data); - void merge(const LHCClockDataHisto* prev); - - ClassDefNV(LHCClockDataHisto, 1); -}; - -class LHCClockCalibrator -{ - using TFType = o2::calibration::TFType; - using Slot = o2::calibration::TimeSlot; - using LHCphase = o2::ft0::LHCphaseCalibrationObject; - using CcdbObjectInfo = o2::ccdb::CcdbObjectInfo; - using CcdbObjectInfoVector = std::vector; - using LHCphaseVector = std::vector; - - public: - LHCClockCalibrator(int minEnt = 500, int nb = 1000, float r = 24400, const std::string path = o2::base::NameConf::getCCDBServer()) : mMinEntries(minEnt), mNBins(nb), mRange(r) {} - ~LHCClockCalibrator() = default; - bool hasEnoughData(const Slot& slot) const { return slot.getContainer()->entries >= mMinEntries; } - void initOutput(); - void finalizeSlot(Slot& slot); - Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend); - - const LHCphaseVector& getLHCphaseVector() const { return mLHCphaseVector; } - const CcdbObjectInfoVector& getLHCphaseInfoVector() const { return mInfoVector; } - CcdbObjectInfoVector& getLHCphaseInfoVector() { return mInfoVector; } - - private: - int mMinEntries = 0; - int mNBins = 0; - float mRange = 0.; - CcdbObjectInfoVector mInfoVector; // vector of CCDB Infos , each element is filled with the CCDB description of the accompanying LHCPhase - LHCphaseVector mLHCphaseVector; // vector of LhcPhase, each element is filled in "process" when we finalize one slot (multiple can be finalized during the same "process", which is why we have a vector. Each element is to be considered the output of the device, and will go to the CCDB - - ClassDef(LHCClockCalibrator, 1); -}; - -} // end namespace ft0 -} // end namespace o2 - -#endif /* FT0_LHCPHASE_CALIBRATION_H_ */ diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/RecoCalibInfoWorkflow.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/RecoCalibInfoWorkflow.h deleted file mode 100644 index b2444ae45f15a..0000000000000 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/RecoCalibInfoWorkflow.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file RecoCalibInfoWorkflow.h -///\ brief Collect data for global offsets calibration -/// \author Alla.Maevskaya@cern.ch - -#ifndef O2_RECOCALIBINFO_WORKFLOW -#define O2_RECOCALIBINFO_WORKFLOW - -#include -#include -#include -#include "Framework/DeviceSpec.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/Task.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsFT0/RecPoints.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "CommonDataFormat/TimeStamp.h" -#include "ReconstructionDataFormats/GlobalTrackID.h" -#include "ReconstructionDataFormats/PrimaryVertex.h" -#include "DataFormatsFT0/RecoCalibInfoObject.h" -#include "TStopwatch.h" -#include -#include - -using namespace o2::framework; -using DataRequest = o2::globaltracking::DataRequest; -using GID = o2::dataformats::GlobalTrackID; - -namespace o2::ft0 -{ - -class RecoCalibInfoWorkflow final : public o2::framework::Task -{ - public: - RecoCalibInfoWorkflow() {} - void run(o2::framework::ProcessingContext& pc) final; - void init(InitContext& ic) final; - void endOfStream(framework::EndOfStreamContext& ec) final; - - private: - std::shared_ptr mDataRequest; - const float cSpeed = 0.029979246f; // speed of light in TOF units - GID::mask_t mInputSources; - TStopwatch mTimer; -}; -framework::DataProcessorSpec getRecoCalibInfoWorkflow(bool useMC); - -} // namespace o2::ft0 - -#endif /* O2_RECOCALIBINFO_WORKFLOW */ diff --git a/Detectors/FIT/FT0/calibration/macros/CMakeLists.txt b/Detectors/FIT/FT0/calibration/macros/CMakeLists.txt deleted file mode 100644 index 9125746b3a695..0000000000000 --- a/Detectors/FIT/FT0/calibration/macros/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. -# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -# All rights not expressly granted are reserved. -# -# This software is distributed under the terms of the GNU General Public -# License v3 (GPL Version 3), copied verbatim in the file "COPYING". -# -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization -# or submit itself to any jurisdiction. - -o2_add_test_root_macro( - makeChannelOffsetCalibObjectInCCDB.C - PUBLIC_LINK_LIBRARIES O2::DataFormatsFT0 O2::Framework O2::CCDB O2::DetectorsCalibration - LABELS ft0) diff --git a/Detectors/FIT/FT0/calibration/macros/makeChannelOffsetCalibObjectInCCDB.C b/Detectors/FIT/FT0/calibration/macros/makeChannelOffsetCalibObjectInCCDB.C deleted file mode 100644 index f184989c75bc3..0000000000000 --- a/Detectors/FIT/FT0/calibration/macros/makeChannelOffsetCalibObjectInCCDB.C +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include -#include "TFile.h" -#include "CCDB/CcdbApi.h" -#include "CCDB/CCDBTimeStampUtils.h" -#include "CCDB/CcdbObjectInfo.h" -#include "CommonUtils/MemFileHelper.h" -#include "CCDB/BasicCCDBManager.h" -#include -#include -#include "DataFormatsFT0/FT0ChannelTimeCalibrationObject.h" -#endif - -int makeChannelOffsetCalibObjectInCCDB(const std::string url = "http://ccdb-test.cern.ch:8080") -{ - using CalibObjWithInfoType = std::pair>>; - std::array offsets; - for (int i = 0; i < 208; i++) { - offsets[i] = 0; - } - o2::ccdb::CcdbApi api; - api.init(url); - // std::map md; - CalibObjWithInfoType result; - o2::ft0::FT0ChannelTimeCalibrationObject calibrationObject; - static std::map metaData; - auto clName = o2::utils::MemFileHelper::getClassName(calibrationObject); - auto flName = o2::ccdb::CcdbApi::generateFileName(clName); - uint64_t starting = 1546300800; // 01.01.2019 - uint64_t stopping = o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP; // 1633046400; // 01.10.2021 - LOG(info) << " clName " << clName << " flName " << flName; - result.first = o2::ccdb::CcdbObjectInfo("FT0/Calib/ChannelTimeOffset", clName, flName, metaData, starting, stopping); - result.second = o2::ccdb::CcdbApi::createObjectImage(&offsets, &result.first); - LOG(info) << " start " << starting << " end " << stopping; - api.storeAsTFileAny(&calibrationObject, "FT0/Calib/ChannelTimeOffset", metaData, starting, stopping); - - return 0; -} diff --git a/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx new file mode 100644 index 0000000000000..b17c81213cd08 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx @@ -0,0 +1,81 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FT0Calibration/EventsPerBcCalibrator.h" +#include "CommonUtils/MemFileHelper.h" + +namespace o2::ft0 +{ +void EventsPerBcContainer::print() const +{ + LOG(info) << entries << " entries"; +} + +void EventsPerBcContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::span data) +{ + size_t oldEntries = entries; + for (const auto& digit : data) { + if (digit.mTriggers.getVertex() && digit.mTriggers.getAmplA() >= mMinAmplitudeSideA && digit.mTriggers.getAmplC() >= mMinAmplitudeSideC && (digit.mTriggers.getAmplA() + digit.mTriggers.getAmplC()) >= mMinSumOfAmplitude) { + mTvx[digit.mIntRecord.bc]++; + entries++; + } + } + LOG(debug) << "Container is filled with " << entries - oldEntries << " new events"; +} + +void EventsPerBcContainer::merge(const EventsPerBcContainer* prev) +{ + for (int bc = 0; bc < o2::constants::lhc::LHCMaxBunches; bc++) { + mTvx[bc] += prev->mTvx[bc]; + } + entries += prev->entries; +} + +void EventsPerBcCalibrator::initOutput() +{ + mTvxPerBcs.clear(); + mTvxPerBcInfos.clear(); +} + +EventsPerBcCalibrator::EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude) : mMinNumberOfEntries(minNumberOfEntries), mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC), mMinSumOfAmplitude(minSumOfAmplitude) +{ + LOG(info) << "Defined threshold for number of entires per slot: " << mMinNumberOfEntries; + LOG(info) << "Defined threshold for side A amplitude for event: " << mMinAmplitudeSideA; + LOG(info) << "Defined threshold for side C amplitude for event: " << mMinAmplitudeSideC; +} + +bool EventsPerBcCalibrator::hasEnoughData(const EventsPerBcCalibrator::Slot& slot) const +{ + return slot.getContainer()->entries > mMinNumberOfEntries; +} + +void EventsPerBcCalibrator::finalizeSlot(EventsPerBcCalibrator::Slot& slot) +{ + LOG(info) << "Finalizing slot from " << slot.getStartTimeMS() << " to " << slot.getEndTimeMS(); + o2::ft0::EventsPerBcContainer* data = slot.getContainer(); + mTvxPerBcs.emplace_back(data->mTvx); + + auto clName = o2::utils::MemFileHelper::getClassName(mTvxPerBcs.back()); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + + std::map metaData; + mTvxPerBcInfos.emplace_back(std::make_unique("FT0/Calib/EventsPerBc", clName, flName, metaData, slot.getStartTimeMS(), slot.getEndTimeMS())); + LOG(info) << "Created object valid from " << mTvxPerBcInfos.back()->getStartValidityTimestamp() << " to " << mTvxPerBcInfos.back()->getEndValidityTimestamp(); +} + +EventsPerBcCalibrator::Slot& EventsPerBcCalibrator::emplaceNewSlot(bool front, TFType tstart, TFType tend) +{ + auto& cont = getSlots(); + auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); + slot.setContainer(std::make_unique(mMinAmplitudeSideA, mMinAmplitudeSideC, mMinSumOfAmplitude)); + return slot; +} +} // namespace o2::ft0 \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/src/FT0CalibCollector.cxx b/Detectors/FIT/FT0/calibration/src/FT0CalibCollector.cxx deleted file mode 100644 index acff7a13ceabe..0000000000000 --- a/Detectors/FIT/FT0/calibration/src/FT0CalibCollector.cxx +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "FT0Calibration/FT0CalibCollector.h" -#include "Framework/Logger.h" -#include -#include -#include -#include -#include - -namespace o2 -{ -namespace ft0 -{ - -using Slot = o2::calibration::TimeSlot; - -//_____________________________________________ -void FT0CalibInfoSlot::fill(const gsl::span data) -{ - // fill container - // we first order the data that arrived, to improve speed when filling - int nd = data.size(); - LOG(info) << "FT0CalibInfoSlot::fill entries in incoming data = " << nd; - std::vector ord(nd); - std::iota(ord.begin(), ord.end(), 0); - std::sort(ord.begin(), ord.end(), [&data](int i, int j) { return data[i].getChannelIndex() < data[j].getChannelIndex(); }); - int chPrev = 0, offsPrev = 0; - for (int i = 0; i < nd; i++) { - if (std::abs(data[ord[i]].getTime()) > HISTO_RANGE) { - continue; - } - const auto& dti = data[ord[i]]; - auto ch = dti.getChannelIndex(); - auto offset = offsPrev; - if (ch > chPrev) { - offset += std::accumulate(mEntriesSlot.begin() + chPrev, mEntriesSlot.begin() + ch, 0); - } - offsPrev = offset; - chPrev = ch; - auto it = mFT0CollectedCalibInfoSlot.emplace(mFT0CollectedCalibInfoSlot.begin() + offset, data[ord[i]].getChannelIndex(), data[ord[i]].getTime(), data[ord[i]].getAmp(), data[ord[i]].getTimeStamp()); - mEntriesSlot[ch]++; - } -} -//_____________________________________________ -void FT0CalibInfoSlot::merge(const FT0CalibInfoSlot* prev) -{ - // merge data of 2 slots - - LOG(info) << "Merging two slots with entries: current slot -> " << mFT0CollectedCalibInfoSlot.size() << " , previous slot -> " << prev->mFT0CollectedCalibInfoSlot.size(); - - int offset = 0, offsetPrev = 0; - std::vector tmpVector; - for (int ch = 0; ch < NCHANNELS; ch++) { - if (mEntriesSlot[ch] != 0) { - for (int i = offset; i < offset + mEntriesSlot[ch]; i++) { - tmpVector.emplace_back(mFT0CollectedCalibInfoSlot[i]); - } - offset += mEntriesSlot[ch]; - } - if (prev->mEntriesSlot[ch] != 0) { - for (int i = offsetPrev; i < offsetPrev + prev->mEntriesSlot[ch]; i++) { - tmpVector.emplace_back(prev->mFT0CollectedCalibInfoSlot[i]); - } - offsetPrev += prev->mEntriesSlot[ch]; - mEntriesSlot[ch] += prev->mEntriesSlot[ch]; - } - } - mFT0CollectedCalibInfoSlot.swap(tmpVector); - LOG(debug) << "After merging the size is " << mFT0CollectedCalibInfoSlot.size(); - return; -} -//_____________________________________________ -void FT0CalibInfoSlot::print() const -{ - // to print number of entries in the tree and the channel with the max number of entries - - LOG(info) << "Total number of entries " << mFT0CollectedCalibInfoSlot.size(); - auto maxElementIndex = std::max_element(mEntriesSlot.begin(), mEntriesSlot.end()); - auto channelIndex = std::distance(mEntriesSlot.begin(), maxElementIndex); - LOG(info) << "The maximum number of entries per channel in the current mFT0CollectedCalibInfo is " << *maxElementIndex << " for channel " << channelIndex; - return; -} - -//_____________________________________________ -void FT0CalibInfoSlot::printEntries() const -{ - // to print number of entries in the tree and per channel - - LOG(debug) << "Total number of entries " << mFT0CollectedCalibInfoSlot.size(); - for (int i = 0; i < mEntriesSlot.size(); ++i) { - if (mEntriesSlot[i] != 0) { - LOG(info) << "channel " << i << " has " << mEntriesSlot[i] << " entries"; - } - } - return; -} - -//=================================================================== - -//_____________________________________________ -void FT0CalibCollector::initOutput() -{ - // emptying the vectors - - mFT0CollectedCalibInfo.clear(); - for (int ch = 0; ch < NCHANNELS; ch++) { - mEntries[ch] = 0; - } - - return; -} - -//_____________________________________________ -bool FT0CalibCollector::hasEnoughData(const Slot& slot) const -{ - // We define that we have enough data if the tree is big enough. - // each FT0CalibrationInfoObject is composed of two int8 and one int16 --> 32 bytes - // E.g. supposing that we have 500000 entries per channel --> 500 eneries per one amplitude bin - // we can check if we have 500000*o2::ft0::Geometry::NCHANNELS entries in the vector - - const o2::ft0::FT0CalibInfoSlot* c = slot.getContainer(); - LOG(info) << "we have " << c->getCollectedCalibInfoSlot().size() << " entries"; - int maxNumberOfHits = mAbsMaxNumOfHits ? mMaxNumOfHits : mMaxNumOfHits * NCHANNELS; - if (mTFsendingPolicy || c->getCollectedCalibInfoSlot().size() > maxNumberOfHits) { - return true; - } - return false; -} - -//_____________________________________________ -void FT0CalibCollector::finalizeSlot(Slot& slot) -{ - - o2::ft0::FT0CalibInfoSlot* c = slot.getContainer(); - mFT0CollectedCalibInfo = c->getCollectedCalibInfoSlot(); - LOG(info) << "vector of received with size = " << mFT0CollectedCalibInfo.size(); - mEntries = c->getEntriesPerChannel(); - return; -} - -//_____________________________________________ -Slot& FT0CalibCollector::emplaceNewSlot(bool front, TFType tstart, TFType tend) -{ - - LOG(info) << "FIT_CALIBRATOR_TYPE::emplaceNewSlot " - << " start " << tstart << " end " << tend; - auto& cont = getSlots(); - auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); - slot.setContainer(std::make_unique()); - return slot; -} - -} // end namespace ft0 -} // end namespace o2 diff --git a/Detectors/FIT/FT0/calibration/src/FT0CalibTimeSlewing.cxx b/Detectors/FIT/FT0/calibration/src/FT0CalibTimeSlewing.cxx deleted file mode 100644 index 3ea4fcb788a99..0000000000000 --- a/Detectors/FIT/FT0/calibration/src/FT0CalibTimeSlewing.cxx +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// \file FT0CalibTimeSlewing.cxx -/// \brief Class for slewing calibration object -/// -#include -#include -#include -#include -#include -#include -#include -#include "FT0Calibration/FT0CalibTimeSlewing.h" - -using namespace o2::ft0; - -FT0CalibTimeSlewing::FT0CalibTimeSlewing() -{ - for (int iCh = 0; iCh < NCHANNELS; iCh++) { - mSigmaPeak[iCh] = -1.; - mTimeAmpHist[iCh] = new TH2F(Form("hTimeAmpHist%d", iCh), Form("TimeAmp%d", iCh), - NUMBER_OF_HISTOGRAM_BINS_X, 0, HISTOGRAM_RANGE_X, - NUMBER_OF_HISTOGRAM_BINS_Y, -HISTOGRAM_RANGE_Y, HISTOGRAM_RANGE_Y); - } -} - -//______________________________________________ -float FT0CalibTimeSlewing::getChannelOffset(int channel, int amplitude) const -{ - return mTimeSlewing[channel].Eval(amplitude); -} - -//______________________________________________ -void FT0CalibTimeSlewing::fillGraph(int channel, TH2F* histo) -{ - LOG(info) << "FT0CalibTimeSlewing::fillGraph " << channel << " entries " << int(histo->GetEntries()); - double shiftchannel = 0; - TH1D* hist_Proj = histo->ProjectionY(); - TFitResultPtr res = hist_Proj->Fit("gaus", "SQ"); - if ((Int_t)res == 0) { - shiftchannel = res->Parameter(1); - } - Double_t xgr[NUMBER_OF_HISTOGRAM_BINS_X] = {}; - Double_t ygr[NUMBER_OF_HISTOGRAM_BINS_X] = {}; - TH1D* proj = nullptr; - int nbins = 0; - for (int ibin = 1; ibin < NUMBER_OF_HISTOGRAM_BINS_X; ibin++) { - xgr[ibin] = histo->GetXaxis()->GetBinCenter(ibin); - proj = histo->ProjectionY(Form("proj_px%i", ibin), ibin, ibin + 1); - if (proj->GetEntries() < 500) { - ygr[ibin] = 0; - continue; - } - TFitResultPtr r = proj->Fit("gaus", "SQ"); - if ((Int_t)r == 0) { - ygr[ibin] = r->Parameter(1) - shiftchannel; - } - nbins++; - LOG(info) << "channel " << channel << " bin " << ibin << " x " << xgr[ibin] << " y " << ygr[ibin] << " ent " << proj->GetEntries() << " sigma " << r->Parameter(2) << " shiftchannel " << shiftchannel; - } - TGraph* grTimeAmp = new TGraph(nbins + 5, xgr, ygr); - mTimeSlewing[channel] = *grTimeAmp; -} -//______________________________________________ -FT0CalibTimeSlewing& FT0CalibTimeSlewing::operator+=(const FT0CalibTimeSlewing& other) -{ - for (int i = 0; i < NCHANNELS; i++) { - mTimeSlewing[i] = other.mTimeSlewing[i]; - mSigmaPeak[i] = other.mSigmaPeak[i]; - } - return *this; -} - -//______________________________________________ -void FT0CalibTimeSlewing::mergeFilesWithTree() -{ - TFileMerger merger; - merger.OutputFile(mMergedFileName.c_str()); - for (Int_t i = 0; i < mNfiles; i++) { - TFile* file = - TFile::Open(Form("%s_%d.root", mSingleFileName.c_str(), i)); - if (file) { - merger.AddAdoptFile(file); - } - } - if (!merger.Merge()) { - LOG(fatal) << "Could not merge files"; - } - TFile mMergedFile{merger.GetOutputFileName()}; - TTree* tr = (TTree*)mMergedFile.Get("treeCollectedCalibInfo"); - if (!tr) { - LOG(fatal) << "Could not get tree with calib info"; - } - fillHistos(tr); -} - -//______________________________________________ -void FT0CalibTimeSlewing::fillHistos(TTree* tr) -{ - std::vector* localCalibInfoFT0 = nullptr; - if (!tr->GetBranch("FT0CollectedCalibInfo")) { - LOG(fatal) << "Did not find collected FT0 calib info branch in the input tree"; - } - tr->SetBranchAddress("FT0CollectedCalibInfo", &localCalibInfoFT0); - for (Int_t ievent = 0; ievent < tr->GetEntries(); ievent++) { - tr->GetEvent(ievent); - for (auto& info : *localCalibInfoFT0) { - LOG(debug) << " ch " << int(info.getChannelIndex()) << " time " << info.getTime() << " amp " << info.getAmp(); - int iCh = info.getChannelIndex(); - mTimeAmpHist[iCh]->Fill(info.getAmp(), info.getTime()); - } - } - delete localCalibInfoFT0; -} diff --git a/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h b/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h index 1facc4be739fc..11b1ce25e9353 100644 --- a/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h +++ b/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h @@ -16,14 +16,9 @@ #pragma link off all functions; #pragma link C++ class o2::ft0::FT0TimeOffsetSlotContainer + ; -#pragma link C++ class o2::ft0::GlobalOffsetsContainer + ; -#pragma link C++ class o2::ft0::FT0CalibTimeSlewing + ; -#pragma link C++ class o2::ft0::FT0CalibCollector + ; -#pragma link C++ class o2::calibration::TimeSlot < o2::ft0::FT0CalibInfoSlot>; -#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::FT0CalibInfoSlot>; +#pragma link C++ class o2::ft0::EventsPerBcCalibrator + ; #pragma link C++ class o2::calibration::TimeSlot < o2::ft0::FT0TimeOffsetSlotContainer>; #pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::FT0TimeOffsetSlotContainer>; -#pragma link C++ class o2::calibration::TimeSlot < o2::ft0::GlobalOffsetsContainer>; -#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::GlobalOffsetsContainer>; - +#pragma link C++ class o2::calibration::TimeSlot < o2::ft0::EventsPerBcContainer> + ; +#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::EventsPerBcContainer> + ; #endif diff --git a/Detectors/FIT/FT0/calibration/src/FT0TimeOffsetSlotContainer.cxx b/Detectors/FIT/FT0/calibration/src/FT0TimeOffsetSlotContainer.cxx index d7e0ae742d59b..1e31d24f8c20d 100644 --- a/Detectors/FIT/FT0/calibration/src/FT0TimeOffsetSlotContainer.cxx +++ b/Detectors/FIT/FT0/calibration/src/FT0TimeOffsetSlotContainer.cxx @@ -25,7 +25,11 @@ FT0TimeOffsetSlotContainer::FT0TimeOffsetSlotContainer(std::size_t minEntries) { bool FT0TimeOffsetSlotContainer::hasEnoughEntries() const { - if (mIsReady) { + if (mTotalNevents == 0) { + // Dummy slot, should be ignored for protection + LOG(warning) << "RESULT: Empty slot, ignoring"; + return false; + } else if (mIsReady) { // ready : bad+good == NChannels (i.e. no pending channel) LOG(info) << "RESULT: ready"; print(); @@ -78,6 +82,7 @@ void FT0TimeOffsetSlotContainer::fill(const gsl::span& data) nEntries += en; } mArrEntries[iCh] = nEntries; + mTotalNevents += nEntries; if (nEntries >= CalibParam::Instance().mMaxEntriesThreshold) { mBitsetGoodChIDs.set(iCh); } @@ -91,6 +96,14 @@ void FT0TimeOffsetSlotContainer::fill(const gsl::span& data) void FT0TimeOffsetSlotContainer::merge(FT0TimeOffsetSlotContainer* prev) { LOG(info) << "MERGING"; + if (mIsFirstTF && prev->isFirstTF()) { + // nothing to be done + return; + } else if (mIsFirstTF && !prev->isFirstTF()) { + // need to make mHistogram operational first + mHistogram.init(prev->getHistogram().getNBinsX(), prev->getHistogram().getXMin(), prev->getHistogram().getXMax(), prev->getHistogram().getNBinsY(), prev->getHistogram().getYMin(), prev->getHistogram().getYMax()); + mIsFirstTF = false; + } *this = *prev; if (mCurrentSlot == 0) { // This part should at the stage `hasEnoughData()` but it is const method diff --git a/Detectors/FIT/FT0/calibration/src/GlobalOffsetsContainer.cxx b/Detectors/FIT/FT0/calibration/src/GlobalOffsetsContainer.cxx deleted file mode 100644 index d768edc856952..0000000000000 --- a/Detectors/FIT/FT0/calibration/src/GlobalOffsetsContainer.cxx +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "Framework/Logger.h" -#include "MathUtils/fit.h" -#include "CommonUtils/MemFileHelper.h" -#include "CCDB/CcdbApi.h" -#include "DetectorsCalibration/Utils.h" -#include "FT0Calibration//GlobalOffsetsContainer.h" -#include "DataFormatsFT0/GlobalOffsetsInfoObject.h" -#include -#include -#include "MathUtils/fit.h" -#include -#include - -using namespace o2::ft0; -using o2::math_utils::fitGaus; - -bool GlobalOffsetsContainer::hasEnoughEntries() const -{ - return mEntries < mMinEntries; -} -void GlobalOffsetsContainer::fill(const gsl::span& data) -{ - // fill container - for (auto& entry : data) { - if (std::abs(entry.getT0AC()) < RANGE) { - updateFirstCreation(entry.getTimeStamp()); - auto time = entry.getT0AC(); - time += RANGE; - mHisto[(time)]++; - mEntries++; - } - } -} -void GlobalOffsetsContainer::merge(GlobalOffsetsContainer* prev) -{ - // merge data of 2 slots - for (int i = mHisto.size(); i--;) { - mHisto[i] += prev->mHisto[i]; - } - mEntries += prev->mEntries; - mFirstCreation = std::min(mFirstCreation, prev->mFirstCreation); -} - -int GlobalOffsetsContainer::getMeanGaussianFitValue() const -{ - if (!hasEnoughEntries()) { - return 0; - } - if (mEntries == 0) { - return 0; - } - float sum = 0; - for (int ic = 0; ic < NBINS; ic++) { - sum += float(ic - RANGE) * float(mHisto[ic]); - // LOG(info)<<" histo "< fitValues; - double fitres = fitGaus(NBINS, mHisto.data(), -RANGE, RANGE, fitValues); - if (fitres >= 0) { - LOG(info) << "Fit result " << fitres << " Mean = " << fitValues[1] << " Sigma = " << fitValues[2] << " size " << mEntries << " mean " << sum / mEntries; - return (static_cast(std::round(fitValues[1]))); - } else { - LOG(warning) << "Fit failed with result = " << fitres; - return 0; - } -} -GlobalOffsetsCalibrationObject GlobalOffsetsContainer::generateCalibrationObject(long, long, const std::string&) const -{ - GlobalOffsetsCalibrationObject calibrationObject; - calibrationObject.mCollisionTimeOffsets = getMeanGaussianFitValue(); - LOG(info) << "GlobalOffsetsCalibrationObjectAlgorithm generate CalibrationObject for T0" - << " = " << calibrationObject.mCollisionTimeOffsets; - return calibrationObject; -} - -void GlobalOffsetsContainer::print() const -{ - LOG(info) << "Container keep data for LHC phase calibration:"; - LOG(info) << "Gaussian mean time AC side " << getMeanGaussianFitValue() << " based on" << mEntries << " entries"; -} diff --git a/Detectors/FIT/FT0/calibration/src/RecoCalibInfoWorkflow.cxx b/Detectors/FIT/FT0/calibration/src/RecoCalibInfoWorkflow.cxx deleted file mode 100644 index 9de69b8748e60..0000000000000 --- a/Detectors/FIT/FT0/calibration/src/RecoCalibInfoWorkflow.cxx +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file RecoCalibInfoWorkflow.cxx -///\ brief Collect data for global offsets calibration -/// \author Alla.Maevskaya@cern.ch - -#include -#include -#include "Framework/DeviceSpec.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/Task.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsFT0/RecPoints.h" -#include "FT0Calibration/RecoCalibInfoWorkflow.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "CommonDataFormat/TimeStamp.h" -#include "ReconstructionDataFormats/GlobalTrackID.h" -#include "ReconstructionDataFormats/PrimaryVertex.h" -#include "DataFormatsFT0/RecoCalibInfoObject.h" -#include -#include -#include - -using namespace o2::framework; -using namespace o2::math_utils::detail; -using PVertex = o2::dataformats::PrimaryVertex; -using GIndex = o2::dataformats::VtxTrackIndex; -using DataRequest = o2::globaltracking::DataRequest; - -namespace o2::ft0 -{ -void RecoCalibInfoWorkflow::init(InitContext& ic) -{ - mTimer.Stop(); - mTimer.Reset(); -} -void RecoCalibInfoWorkflow::run(o2::framework::ProcessingContext& pc) -{ - o2::globaltracking::RecoContainer recoData; - recoData.collectData(pc, *mDataRequest); - auto primVertices = recoData.getPrimaryVertices(); - auto ft0RecPoints = recoData.getFT0RecPoints(); - std::map bcsMap; - auto& calib_data = pc.outputs().make>(o2::framework::OutputRef{"calib", 0}); - calib_data.reserve(ft0RecPoints.size()); - for (auto& vertex : primVertices) { - auto& timeStamp = vertex.getTimeStamp(); - double tsTimeStamp = timeStamp.getTimeStamp() * 1E3; // mus to ns - uint64_t globalBC = std::round(tsTimeStamp / o2::constants::lhc::LHCBunchSpacingNS); - LOG(debug) << "PrimVertices " << globalBC; - auto [iter, inserted] = bcsMap.try_emplace(globalBC, &vertex); - if (!inserted) { - iter->second = nullptr; - } - } - for (auto& ft0RecPoint : ft0RecPoints) { - uint64_t bc = ft0RecPoint.getInteractionRecord().toLong(); - auto item = bcsMap.find(bc); - LOG(debug) << " <second == nullptr) { - LOG(debug) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc; - continue; - } - auto& vertex = *item->second; - auto currentVertex = vertex.getZ(); - ushort ncont = vertex.getNContributors(); - LOG(debug) << "CurrentVertex " << currentVertex << " ncont " << int(ncont); - if (ncont < 3) { - continue; - } - auto shift = currentVertex / cSpeed; - short t0A = ft0RecPoint.getCollisionTimeA() + shift; - short t0C = ft0RecPoint.getCollisionTimeC() - shift; - short t0AC = ft0RecPoint.getCollisionTimeMean(); - LOG(debug) << " BC t0 " << bc << " shift " << shift << " A " << t0A << " C " << t0C << " AC " << t0AC; - calib_data.emplace_back(t0A, t0C, t0AC); - } - mTimer.Stop(); -} -void RecoCalibInfoWorkflow::endOfStream(EndOfStreamContext& ec) -{ - LOGF(info, "Reco calib info workflow dpl total timing: Cpu: %.3e Real: %.3e s in %d slots", - mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); -} - -DataProcessorSpec getRecoCalibInfoWorkflow(GID::mask_t src, bool useMC) -{ - auto dataRequest = std::make_shared(); - dataRequest->requestPrimaryVertertices(false); - dataRequest->requestFT0RecPoints(false); - - return DataProcessorSpec{ - "ft0-calib-reco", - dataRequest->inputs, - Outputs{ - {{"calib"}, "FT0", "CALIB_INFO"}}, - AlgorithmSpec{adaptFromTask(src, dataRequest)}, - Options{}}; -} - -}; // namespace o2::ft0 diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibCollectorWriterSpec.h b/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibCollectorWriterSpec.h deleted file mode 100644 index 217561d815c91..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibCollectorWriterSpec.h +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_CALIBRATION_FT0CALIB_COLLECTOR_WRITER_H -#define O2_CALIBRATION_FT0CALIB_COLLECTOR_WRITER_H - -/// @file FT0CalibCollectorWriterSpec.h -/// @brief Device to write to tree the information for FT0 time slewing calibration. - -#include "DataFormatsFT0/FT0CalibrationInfoObject.h" -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace calibration -{ - -class FT0CalibCollectorWriter : public o2::framework::Task -{ - - using Geo = o2::ft0::Geometry; - - public: - void createAndOpenFileAndTree() - { - TString filename = TString::Format("collFT0%d.root", mCount); - LOG(debug) << "opening file " << filename.Data(); - mfileOut.reset(TFile::Open(TString::Format("%s", filename.Data()), "RECREATE")); - mOutputTree = std::make_unique("treeCollectedCalibInfo", "Tree with FT0 calib info for Time Slewing"); - mOutputTree->Branch(mOutputBranchName.data(), &mPFT0CalibInfoOut); - LOG(info) << " @@@@@ createAndOpenFileAndTree tree set"; - } - - void init(o2::framework::InitContext& ic) final - { - mCount = 0; - createAndOpenFileAndTree(); - mFT0CalibInfoOut.reserve(1000000 * Geo::Nchannels); // tree size 216ch * 10^6 entries * 12 byte - LOG(info) << " @@@@@ init"; - } - - void run(o2::framework::ProcessingContext& pc) final - { - auto collectedInfo = pc.inputs().get>("collectedInfo"); - auto entriesPerChannel = pc.inputs().get>("entriesCh"); - int offsetStart = 0; - for (int ich = 0; ich < Geo::Nchannels; ich++) { - mFT0CalibInfoOut.clear(); - if (entriesPerChannel[ich] > 0) { - mFT0CalibInfoOut.resize(entriesPerChannel[ich]); - auto subSpanVect = collectedInfo.subspan(offsetStart, entriesPerChannel[ich]); - memcpy(&mFT0CalibInfoOut[0], subSpanVect.data(), sizeof(o2::ft0::FT0CalibrationInfoObject) * subSpanVect.size()); - const o2::ft0::FT0CalibrationInfoObject* tmp = subSpanVect.data(); - LOG(debug) << "@@@@@ run ich " << ich << " entries " << entriesPerChannel[ich]; - } - mOutputTree->Fill(); - offsetStart += entriesPerChannel[ich]; - } - sendOutput(pc.outputs()); - } - - void endOfStream(o2::framework::EndOfStreamContext& ec) final - { - mIsEndOfStream = true; - sendOutput(ec.outputs()); - } - - private: - int mCount = 0; // how many times we filled the tree - bool mIsEndOfStream = false; - std::vector mFT0CalibInfoOut, *mPFT0CalibInfoOut = &mFT0CalibInfoOut; ///< these are the object and pointer to the CalibInfo of a specific channel that we need to fill the output tree - std::unique_ptr mOutputTree; ///< tree for the collected calib FT0 info - std::string mFT0CalibInfoBranchName = "FT0CalibInfo"; ///< name of branch containing input FT0 calib infos - std::string mOutputBranchName = "FT0CollectedCalibInfo"; ///< name of branch containing output - std::unique_ptr mfileOut = nullptr; // file in which to write the output - - //________________________________________________________________ - void sendOutput(DataAllocator& output) - { - // This is to fill the tree. - // One file with an empty tree will be created at the end, because we have to have a - // tree opened before processing, since we do not know a priori if something else - // will still come. The size of this extra file is ~6.5 kB - - mfileOut->cd(); - mOutputTree->Write(); - mOutputTree->Reset(); - mCount++; - if (!mIsEndOfStream) { - createAndOpenFileAndTree(); - } - } -}; -} // namespace calibration - -namespace framework -{ - -DataProcessorSpec getFT0CalibCollectorWriterSpec() -{ - LOG(debug) << " @@@@ getFT0CalibCollectorWriterSpec "; - using device = o2::calibration::FT0CalibCollectorWriter; - std::vector inputs; - inputs.emplace_back("collectedInfo", o2::header::gDataOriginFT0, "COLLECTEDINFO"); - inputs.emplace_back("entriesCh", o2::header::gDataOriginFT0, "ENTRIESCH"); - - std::vector outputs; // empty - - return DataProcessorSpec{ - "ft0-calib-collector-writer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask()}, - Options{}}; -} - -} // namespace framework -} // namespace o2 - -#endif diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibSlewingCollectorSpec.h b/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibSlewingCollectorSpec.h deleted file mode 100644 index e01fed6f20588..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibSlewingCollectorSpec.h +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_CALIBRATION_FT0CALIB_SLEWING_COLLECTOR_H -#define O2_CALIBRATION_FT0CALIB_SLEWING_COLLECTOR_H - -/// @file FT0CalibSlewingCollectorSpec.h -/// @brief Device to collect information for FT0 time slewing calibration. - -//#include "FT0Calibration/FT0CollectCalibInfo.h" -#include "FT0Calibration/FT0CalibCollector.h" -#include "DetectorsCalibration/Utils.h" -#include "DataFormatsFT0/FT0CalibrationInfoObject.h" -#include "Framework/Task.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/WorkflowSpec.h" -#include -#include "DetectorsBase/GRPGeomHelper.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -class FT0CalibCollectorDevice : public o2::framework::Task -{ - - public: - FT0CalibCollectorDevice(std::shared_ptr req) : mCCDBRequest(req) {} - void init(o2::framework::InitContext& ic) final - { - o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); - bool isTFsendingPolicy = ic.options().get("tf-sending-policy"); - int maxEnt = ic.options().get("max-number-hits-to-fill-tree"); - auto slotL = ic.options().get("tf-per-slot"); - bool absMaxEnt = ic.options().get("is-max-number-hits-to-fill-tree-absolute"); - mCollector = std::make_unique(isTFsendingPolicy, maxEnt); - mCollector->setSlotLength(slotL); - } - - void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final - { - o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); - } - - void run(o2::framework::ProcessingContext& pc) final - { - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCollector->getCurrentTFInfo()); - auto data = pc.inputs().get>("input"); - mCollector->process(data); - sendOutput(pc.outputs()); - } - - void endOfStream(o2::framework::EndOfStreamContext& ec) final - { - mCollector->checkSlotsToFinalize(o2::calibration::INFINITE_TF); - // we force finalizing slot zero (unless everything was already finalized), no matter how many entries we had - if (mCollector->getNSlots() != 0) { - mCollector->finalizeSlot(mCollector->getSlot(0)); - } - sendOutput(ec.outputs()); - } - - private: - std::unique_ptr mCollector; - std::shared_ptr mCCDBRequest; - int mMaxNumOfHits = 0; - - //________________________________________________________________ - void sendOutput(DataAllocator& output) - { - // in output we send the calibration tree - auto& collectedInfo = mCollector->getCollectedCalibInfo(); - LOG(debug) << "In CollectorSpec sendOutput: size = " << collectedInfo.size(); - if (collectedInfo.size()) { - auto entries = collectedInfo.size(); - // this means that we are ready to send the output - auto entriesPerChannel = mCollector->getEntriesPerChannel(); - output.snapshot(Output{o2::header::gDataOriginFT0, "COLLECTEDINFO", 0, Lifetime::Timeframe}, collectedInfo); - output.snapshot(Output{o2::header::gDataOriginFT0, "ENTRIESCH", 0, Lifetime::Timeframe}, entriesPerChannel); - mCollector->initOutput(); // reset the output for the next round - } - } -}; - -} // namespace ft0 - -namespace framework -{ - -DataProcessorSpec getFT0CalibCollectorDeviceSpec() -{ - using device = o2::ft0::FT0CalibCollectorDevice; - using clbUtils = o2::calibration::Utils; - - std::vector outputs; - outputs.emplace_back(o2::header::gDataOriginFT0, "COLLECTEDINFO", 0, Lifetime::Timeframe); - outputs.emplace_back(o2::header::gDataOriginFT0, "ENTRIESCH", 0, Lifetime::Timeframe); - - std::vector inputs; - inputs.emplace_back("input", "FT0", "CALIB_INFO"); - auto ccdbRequest = std::make_shared(true, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs); - return DataProcessorSpec{ - "calib-ft0calib-collector", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ccdbRequest)}, - Options{ - {"max-number-hits-to-fill-tree", VariantType::Int, 1000, {"maximum number of entries in one channel to trigger teh filling of the tree"}}, - {"is-max-number-hits-to-fill-tree-absolute", VariantType::Bool, false, {"to decide if we want to multiply the max-number-hits-to-fill-tree by the number of channels (when set to true), or not (when set to false) for fast checks"}}, - {"tf-sending-policy", VariantType::Bool, false, {"if we are sending output at every TF; otherwise, we use the max-number-hits-to-fill-tree"}}, - {"tf-per-slot", o2::framework::VariantType::UInt32, 200000u, {"TF per slot "}}}}; -} - -} // namespace framework - -} // namespace o2 - -#endif diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/FT0SlewingCalibrationWorkflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/FT0SlewingCalibrationWorkflow.cxx deleted file mode 100644 index 9a9c8e078322a..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/FT0SlewingCalibrationWorkflow.cxx +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "FT0SlewingCalibrationSpec.h" - -void customize(std::vector& workflowOptions) -{ - //probably some option will be added -} - -#include "Framework/runDataProcessing.h" - -using namespace o2::framework; -WorkflowSpec defineDataProcessing(ConfigContext const& config) -{ - WorkflowSpec workflow; - workflow.emplace_back(o2::ft0::getFT0SlewingCalibrationSpec()); - //add calib spec here... - return workflow; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationSpec.h b/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationSpec.h deleted file mode 100644 index d8e3414ca83ff..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationSpec.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_CALIBRATION_LHCCLOCK_CALIBRATOR_H -#define O2_CALIBRATION_LHCCLOCK_CALIBRATOR_H - -/// @file LHCClockCalibratorSpec.h -/// @brief Device to calibrate LHC clock phase using FT0 data - -#include "Framework/DataProcessorSpec.h" -#include "DataFormatsFT0/GlobalOffsetsCalibrationObject.h" -#include "DataFormatsFT0/GlobalOffsetsInfoObject.h" -#include "FT0Calibration/GlobalOffsetsContainer.h" -#include "FITCalibration/FITCalibrationDevice.h" - -using namespace o2::framework; -namespace o2::ft0 -{ -o2::framework::DataProcessorSpec getGlobalOffsetsCalibrationSpec() -{ - using CalibrationDeviceType = o2::fit::FITCalibrationDevice; - LOG(info) << " getGlobalOffsetsCalibrationSpec()"; - - std::vector inputs; - std::vector outputs; - const o2::header::DataDescription inputDataDescriptor{"CALIB_INFO"}; - const o2::header::DataDescription outputDataDescriptor{"FT0_GLTIME_CALIB"}; - CalibrationDeviceType::prepareVecInputSpec(inputs, o2::header::gDataOriginFT0, inputDataDescriptor); - CalibrationDeviceType::prepareVecOutputSpec(outputs, outputDataDescriptor); - auto ccdbRequest = std::make_shared(true, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs); - return o2::framework::DataProcessorSpec{ - "ft0-global-offsets", - inputs, // o2::framework::Inputs{{"input", "FT0", "CALIBDATA"}}, - outputs, - o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(ccdbRequest, outputDataDescriptor)}, - Options{ - {"tf-per-slot", VariantType::UInt32, 55000u, {"number of TFs per calibration time slot"}}, - {"max-delay", VariantType::UInt32, 3u, {"number of slots in past to consider"}}, - {"min-entries", VariantType::Int, 500, {"minimum number of entries to fit single time slot"}}, - {"extra-info-per-slot", o2::framework::VariantType::String, "", {"Extra info for time slot(usually for debugging)"}}}}; -} - -} // namespace o2::ft0 - -#endif diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationWorkflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationWorkflow.cxx deleted file mode 100644 index f355a96a4a792..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationWorkflow.cxx +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "GlobalOffsetsCalibrationSpec.h" - -void customize(std::vector& workflowOptions) -{ - //probably some option will be added -} - -#include "Framework/runDataProcessing.h" - -using namespace o2::framework; -WorkflowSpec defineDataProcessing(ConfigContext const& config) -{ - - WorkflowSpec workflow; - workflow.emplace_back(o2::ft0::getGlobalOffsetsCalibrationSpec()); - return workflow; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCollectWorkflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCollectWorkflow.cxx deleted file mode 100644 index 3a90816f7d5c2..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCollectWorkflow.cxx +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "Framework/ConfigParamSpec.h" -#include -#include "Framework/DeviceSpec.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/Task.h" -#include "DataFormatsFT0/GlobalOffsetsInfoObject.h" -#include "DataFormatsFT0/RecPoints.h" -#include "Framework/Logger.h" -using namespace o2::framework; - -namespace o2::ft0 -{ - -class GlobalOffsetsCollectWorkflow final : public o2::framework::Task -{ - - public: - void run(o2::framework::ProcessingContext& pc) final - { - auto creationTime = pc.services().get().creation; // approximate time in ms - auto recpoints = pc.inputs().get>("recpoints"); - auto& calib_data = pc.outputs().make>(o2::framework::OutputRef{"calib", 0}); - for (const auto& recpoint : recpoints) { - short t0AC = recpoint.getCollisionTimeMean(); - if (std::abs(t0AC) < 1000) { - calib_data.emplace_back(t0AC, uint64_t(creationTime)); - } - } - } -}; - -} // namespace o2::ft0 - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - Inputs inputs{}; - inputs.push_back(InputSpec{{"recpoints"}, "FT0", "RECPOINTS"}); - - DataProcessorSpec dataProcessorSpec{ - "collect-global-offsets", - inputs, - Outputs{ - {{"calib"}, "FT0", "CALIB_INFO"}}, - AlgorithmSpec{adaptFromTask()}, - Options{}}; - - WorkflowSpec workflow; - workflow.emplace_back(dataProcessorSpec); - - return workflow; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/LHCClockCalibratorSpec.h b/Detectors/FIT/FT0/calibration/testWorkflow/LHCClockCalibratorSpec.h deleted file mode 100644 index 6dfaa636e1322..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/LHCClockCalibratorSpec.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_CALIBRATION_LHCCLOCK_CALIBRATOR_H -#define O2_CALIBRATION_LHCCLOCK_CALIBRATOR_H - -/// @file LHCClockCalibratorSpec.h -/// @brief Device to calibrate LHC clock phase using FT0 data - -#include "Framework/DataProcessorSpec.h" -#include "FT0Calibration/LHCClockDataHisto.h" -#include "FT0Calibration/LHCphaseCalibrationObject.h" -#include "FT0Calibration/FT0CalibrationInfoObject.h" -#include "FITCalibration/FITCalibrationDevice.h" - -using namespace o2::framework; -namespace o2::ft0 -{ -o2::framework::DataProcessorSpec getLHCClockCalibDeviceSpec() -{ - using CalibrationDeviceType = o2::fit::FITCalibrationDevice; - - constexpr const char* DEFAULT_INPUT_LABEL = "calib"; - - std::vector inputs; - inputs.emplace_back(DEFAULT_INPUT_LABEL, "FT0", "CALIB_INFO"); - auto ccdbRequest = std::make_shared(true, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs); - std::vector outputs; - outputs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "FIT_CALIB"}, o2::framework::Lifetime::Sporadic); - outputs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "FIT_CALIB"}, o2::framework::Lifetime::Sporadic); - return o2::framework::DataProcessorSpec{ - "ft0-lhcphase-calibration", - inputs, // o2::framework::Inputs{{"input", "FT0", "CALIBDATA"}}, - outputs, - o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(ccdbRequest, DEFAULT_INPUT_LABEL)}, - Options{ - {"tf-per-slot", VariantType::UInt32, 26000u, {"number of TFs per calibration time slot"}}, - {"max-delay", VariantType::UInt32, 3u, {"number of slots in past to consider"}}, - {"min-entries", VariantType::Int, 500, {"minimum number of entries to fit single time slot"}}}}; -} - -} // namespace o2::ft0 - -#endif diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/RecoCalibInfoWorkflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/RecoCalibInfoWorkflow.cxx deleted file mode 100644 index 1a77e43542816..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/RecoCalibInfoWorkflow.cxx +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include -#include -#include -#include "Framework/DeviceSpec.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/Task.h" -#include "DataFormatsFT0/ChannelData.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsFT0/RecPoints.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "CommonDataFormat/TimeStamp.h" -#include "ReconstructionDataFormats/GlobalTrackID.h" -#include "ReconstructionDataFormats/PrimaryVertex.h" -#include "DataFormatsFT0/RecoCalibInfoObject.h" - -using namespace o2::framework; - -namespace o2::ft0 -{ - -class RecoCalibInfoWorkflow final : public o2::framework::Task -{ - using DataRequest = o2::globaltracking::DataRequest; - - public: - /* void collectBCs(gsl::span& ft0RecPoints, */ - /* gsl::span& primVertices, */ - /* std::map& bcsMap); */ - void run(o2::framework::ProcessingContext& pc) final - { - - o2::globaltracking::RecoContainer recoData; - recoData.collectData(pc, *mDataRequest); - LOG(info) << " @@@read RecoContainer"; - auto primVertices = recoData.getPrimaryVertices(); - LOG(info) << "@@@ primVertices "; - auto ft0RecPoints = recoData.getFT0RecPoints(); - LOG(info) << "@@@@ read T0 recpoints "; - std::map bcsMap; - for (auto& vertex : primVertices) { - auto& timeStamp = vertex.getTimeStamp(); - double tsTimeStamp = timeStamp.getTimeStamp() * 1E3; // mus to ns - uint64_t globalBC = std::round(tsTimeStamp / o2::constants::lhc::LHCBunchSpacingNS); - auto [iter, inserted] = bcsMap.try_emplace(globalBC, &vertex); - if (!inserted) - iter->second = nullptr; - } - /* collectBCs(ft0RecPoints, primVertices, bcsMap); */ - - for (auto& ft0RecPoint : ft0RecPoints) { - uint64_t bc = ft0RecPoint.getInteractionRecord().toLong(); - /* uint64_t bc = globalBC; */ - auto item = bcsMap.find(bc); - if (item == bcsMap.end() || item->second == nullptr) { - LOG(fatal) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc; - continue; - } - auto& vertex = *item->second; - /* int bcID = -1; */ - /* if (item != bcsMap.end()) { */ - /* bcID = item->second; */ - auto currentVertex = vertex.getZ(); - ushort ncont = vertex.getNContributors(); - LOG(info) << "@@@ currentVertex " << currentVertex << " ncont " << int(ncont); - if (ncont == 0) - continue; - auto shift = currentVertex / TMath::C(); - LOG(info) << " BC t0 " << bc; - short t0A = ft0RecPoint.getCollisionTimeA() + shift; - short t0C = ft0RecPoint.getCollisionTimeC() - shift; - short t0AC = ft0RecPoint.getCollisionTimeMean(); - - /* auto recpoints = */ - /* pc.inputs().get>("recpoints"); */ - auto& calib_data = pc.outputs().make>(o2::framework::OutputRef{"calib", 0}); - calib_data.emplace_back(t0A, t0C, t0AC); - } - } - - private: - std::shared_ptr mDataRequest; -}; - -} // namespace o2::ft0 - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - - DataProcessorSpec dataProcessorSpec{ - "RecoCalibInfoWorkflow", - Inputs{ - {{"recpoints"}, "FT0", "FT0CLUSTER"}, - }, - Outputs{ - {{"calib"}, "FT0", "CALIB_INFO"}}, - AlgorithmSpec{adaptFromTask()}, - Options{}}; - - WorkflowSpec workflow; - workflow.emplace_back(dataProcessorSpec); - return workflow; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/RecoQAInfoWorkflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/RecoQAInfoWorkflow.cxx deleted file mode 100644 index 1a77e43542816..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/RecoQAInfoWorkflow.cxx +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include -#include -#include -#include "Framework/DeviceSpec.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/Task.h" -#include "DataFormatsFT0/ChannelData.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsFT0/RecPoints.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "CommonDataFormat/TimeStamp.h" -#include "ReconstructionDataFormats/GlobalTrackID.h" -#include "ReconstructionDataFormats/PrimaryVertex.h" -#include "DataFormatsFT0/RecoCalibInfoObject.h" - -using namespace o2::framework; - -namespace o2::ft0 -{ - -class RecoCalibInfoWorkflow final : public o2::framework::Task -{ - using DataRequest = o2::globaltracking::DataRequest; - - public: - /* void collectBCs(gsl::span& ft0RecPoints, */ - /* gsl::span& primVertices, */ - /* std::map& bcsMap); */ - void run(o2::framework::ProcessingContext& pc) final - { - - o2::globaltracking::RecoContainer recoData; - recoData.collectData(pc, *mDataRequest); - LOG(info) << " @@@read RecoContainer"; - auto primVertices = recoData.getPrimaryVertices(); - LOG(info) << "@@@ primVertices "; - auto ft0RecPoints = recoData.getFT0RecPoints(); - LOG(info) << "@@@@ read T0 recpoints "; - std::map bcsMap; - for (auto& vertex : primVertices) { - auto& timeStamp = vertex.getTimeStamp(); - double tsTimeStamp = timeStamp.getTimeStamp() * 1E3; // mus to ns - uint64_t globalBC = std::round(tsTimeStamp / o2::constants::lhc::LHCBunchSpacingNS); - auto [iter, inserted] = bcsMap.try_emplace(globalBC, &vertex); - if (!inserted) - iter->second = nullptr; - } - /* collectBCs(ft0RecPoints, primVertices, bcsMap); */ - - for (auto& ft0RecPoint : ft0RecPoints) { - uint64_t bc = ft0RecPoint.getInteractionRecord().toLong(); - /* uint64_t bc = globalBC; */ - auto item = bcsMap.find(bc); - if (item == bcsMap.end() || item->second == nullptr) { - LOG(fatal) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc; - continue; - } - auto& vertex = *item->second; - /* int bcID = -1; */ - /* if (item != bcsMap.end()) { */ - /* bcID = item->second; */ - auto currentVertex = vertex.getZ(); - ushort ncont = vertex.getNContributors(); - LOG(info) << "@@@ currentVertex " << currentVertex << " ncont " << int(ncont); - if (ncont == 0) - continue; - auto shift = currentVertex / TMath::C(); - LOG(info) << " BC t0 " << bc; - short t0A = ft0RecPoint.getCollisionTimeA() + shift; - short t0C = ft0RecPoint.getCollisionTimeC() - shift; - short t0AC = ft0RecPoint.getCollisionTimeMean(); - - /* auto recpoints = */ - /* pc.inputs().get>("recpoints"); */ - auto& calib_data = pc.outputs().make>(o2::framework::OutputRef{"calib", 0}); - calib_data.emplace_back(t0A, t0C, t0AC); - } - } - - private: - std::shared_ptr mDataRequest; -}; - -} // namespace o2::ft0 - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - - DataProcessorSpec dataProcessorSpec{ - "RecoCalibInfoWorkflow", - Inputs{ - {{"recpoints"}, "FT0", "FT0CLUSTER"}, - }, - Outputs{ - {{"calib"}, "FT0", "CALIB_INFO"}}, - AlgorithmSpec{adaptFromTask()}, - Options{}}; - - WorkflowSpec workflow; - workflow.emplace_back(dataProcessorSpec); - return workflow; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/calib-global-offsets.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/calib-global-offsets.cxx deleted file mode 100644 index 1f70538c2880b..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/calib-global-offsets.cxx +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file calib-global-offsets.cxx - -#include "FT0Calibration/GlobalOffsetsCalibInfoWorkflow.h" -#include "Framework/CompletionPolicy.h" -#include "CommonUtils/ConfigurableParam.h" -#include "GlobalTrackingWorkflowHelpers/InputHelper.h" -#include "DetectorsCommonDataFormats/DetID.h" -#include - -using namespace o2::framework; - -void customize(std::vector& workflowOptions) -{ - // option allowing to set parameters - std::vector options{ - {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, - {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; - - std::swap(workflowOptions, options); -} -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) -{ - o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - auto useMC = !configcontext.options().get("disable-mc"); - - WorkflowSpec specs; - specs.emplace_back(o2::ft0::getRecoCalibInfoWorkflow(useMC)); - - return std::move(specs); -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/ft0-collect-calib-workflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/ft0-collect-calib-workflow.cxx deleted file mode 100644 index 29c7d9719e0c1..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/ft0-collect-calib-workflow.cxx +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "FT0CalibSlewingCollectorSpec.h" -#include "FT0CalibCollectorWriterSpec.h" -#include "Framework/DataProcessorSpec.h" - -using namespace o2::framework; - -// we need to add workflow options before including Framework/runDataProcessing -void customize(std::vector& workflowOptions) -{ - // option allowing to set parameters -} - -// ------------------------------------------------------------------ - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) -{ - WorkflowSpec specs; - specs.emplace_back(getFT0CalibCollectorDeviceSpec()); - specs.emplace_back(getFT0CalibCollectorWriterSpec()); - - return specs; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/lhc-clockphase-workflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/lhc-clockphase-workflow.cxx deleted file mode 100644 index 4384bfb1c6e3c..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/lhc-clockphase-workflow.cxx +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "Framework/DataProcessorSpec.h" -#include "LHCClockCalibratorSpec.h" - -using namespace o2::framework; - -// we need to add workflow options before including Framework/runDataProcessing -void customize(std::vector& workflowOptions) -{ - // option allowing to set parameters -} - -// ------------------------------------------------------------------ - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) -{ - WorkflowSpec specs; - specs.emplace_back(o2::ft0::getLHCClockCalibDeviceSpec()); - return specs; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/slew_upload.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/slew_upload.cxx deleted file mode 100644 index b5a60c68f37f1..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/slew_upload.cxx +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file slew_upload.cxx -/// \author Alla.Maevskaya@cern.ch - -#include -#include -#include -#include -#include "Framework/Logger.h" -#include -#include -#include "CommonUtils/StringUtils.h" -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/NameConf.h" -#include "CCDB/BasicCCDBManager.h" -#include "CCDB/CcdbApi.h" -#include "FT0Calibration/FT0CalibTimeSlewing.h" -#include -#include -#include -#include -#include - -using o2::ccdb::BasicCCDBManager; -using o2::ccdb::CcdbApi; -using namespace o2::ft0; - -namespace bpo = boost::program_options; - -void slew_upload(const std::string& inFileName, const std::string& mergedFileName, int nFiles); - -int main(int argc, char** argv) -{ - bpo::variables_map vm; - bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + - "Upload FT0 slewing corrections\n"); - bpo::options_description opt_hidden(""); - bpo::options_description opt_all; - bpo::positional_options_description opt_pos; - - try { - auto add_option = opt_general.add_options(); - add_option("help,h", "Print this help message"); - add_option("input-file", bpo::value()->default_value("collFT0.root"), "verbosity level"); - add_option("merged-file", bpo::value()->default_value("FT0slewGraphs.root"), "input merged file"); - add_option("number-of-files", bpo::value()->default_value(1), "number of files to merge"); - add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); - - opt_all.add(opt_general).add(opt_hidden); - bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); - - if (vm.count("help")) { - std::cout << opt_general << std::endl; - exit(0); - } - - bpo::notify(vm); - } catch (bpo::error& e) { - std::cerr << "ERROR: " << e.what() << std::endl - << std::endl; - std::cerr << opt_general << std::endl; - exit(1); - } catch (std::exception& e) { - std::cerr << e.what() << ", application will now exit" << std::endl; - exit(2); - } - o2::conf::ConfigurableParam::updateFromString(vm["configKeyValues"].as()); - - // o2::conf::ConfigurableParam::updateFromString(vm["configKeyValues"].as()); - slew_upload(vm["input-file"].as(), - vm["merged-file"].as(), - vm["number-of-files"].as()); - - return 0; -} - -void slew_upload(const std::string& inFileName, const std::string& mergedFileName, int nFiles) -{ - TStopwatch swTot; - swTot.Start(); - - o2::ft0::FT0CalibTimeSlewing sl; - sl.setSingleFileName(inFileName); - sl.setMergedFileName(mergedFileName); - sl.setNfiles(nFiles); - sl.mergeFilesWithTree(); - for (int iCh = 0; iCh < o2::ft0 ::Geometry::Nchannels; ++iCh) { - TH2F* hist = sl.getTimeAmpHist(iCh); - if (hist->GetEntries() < 100000) { - continue; - } - sl.fillGraph(iCh, hist); - } - std::array graphs = sl.getGraphs(); - TGraph& gr = graphs.at(29); - gr.Print(); - CcdbApi api; - std::map metadata; // can be empty - api.init(o2::base::NameConf::getCCDBServer()); // or http://localhost:8080 for a local installation - // store abitrary user object in strongly typed manner - api.storeAsTFileAny(&graphs, "FT0/Calib/SlewingCorr", metadata); - - // - swTot.Stop(); - swTot.Print(); -} diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx new file mode 100644 index 0000000000000..38d634c20c828 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx @@ -0,0 +1,49 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FT0EventsPerBcSpec.h" +#include "Framework/Lifetime.h" +#include + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + using namespace o2::framework; + using o2::calibration::FT0EventsPerBcProcessor; + std::vector inputs; + inputs.emplace_back("digits", "FT0", "DIGITSBC", Lifetime::Timeframe); + auto ccdbRequest = std::make_shared(true, // orbitResetTime + false, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs); + std::vector outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "EventsPerBc"}, Lifetime::Timeframe); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "EventsPerBc"}, Lifetime::Timeframe); + DataProcessorSpec dataProcessorSpec{ + "FT0EventsPerBcProcessor", + inputs, + outputs, + AlgorithmSpec(adaptFromTask(ccdbRequest)), + Options{ + {"save-to-file", VariantType::Bool, false, {"Save calibration object to local file"}}, + {"slot-len-sec", VariantType::UInt32, 3600u, {"Duration of each slot in seconds"}}, + {"one-object-per-run", VariantType::Bool, false, {"If set, workflow creates only one calibration object per run"}}, + {"min-entries-number", VariantType::UInt32, 5000u, {"Minimum number of entries required for a slot to be valid"}}, + {"min-ampl-side-a", VariantType::Int, 0, {"Amplitude threshold for Side A events"}}, + {"min-ampl-side-c", VariantType::Int, 0, {"Amplitude threshold for Side C events"}}, + {"min-sum-of-ampl", VariantType::Int, 0, {"Amplitude threshold for sum of A-side and C-side amplitudes"}}}}; + + WorkflowSpec workflow; + workflow.emplace_back(dataProcessorSpec); + return workflow; +} diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h new file mode 100644 index 0000000000000..1d4d4a75842e8 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h @@ -0,0 +1,148 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_CALIBRATION_FT0_EVENTS_PER_BC_CALIBRATOR_H +#define O2_CALIBRATION_FT0_EVENTS_PER_BC_CALIBRATOR_H + +#include "Framework/runDataProcessing.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include +#include "Framework/DeviceSpec.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/Task.h" +#include "DetectorsCalibration/Utils.h" +#include "DetectorsBase/GRPGeomHelper.h" + +#include "DataFormatsFT0/Digit.h" +#include "FT0Calibration/EventsPerBcCalibrator.h" + +namespace o2::calibration +{ +class FT0EventsPerBcProcessor final : public o2::framework::Task +{ + public: + FT0EventsPerBcProcessor(std::shared_ptr request) : mCCDBRequest(request) {} + + void init(o2::framework::InitContext& ic) final + { + o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); + mSaveToFile = ic.options().get("save-to-file"); + + if (ic.options().hasOption("slot-len-sec")) { + mSlotLenSec = ic.options().get("slot-len-sec"); + } + if (ic.options().hasOption("one-object-per-run")) { + mOneObjectPerRun = ic.options().get("one-object-per-run"); + } + if (ic.options().hasOption("min-entries-number")) { + mMinNumberOfEntries = ic.options().get("min-entries-number"); + } + if (ic.options().hasOption("min-ampl-side-a")) { + mMinAmplitudeSideA = ic.options().get("min-ampl-side-a"); + } + if (ic.options().hasOption("min-ampl-side-c")) { + mMinAmplitudeSideC = ic.options().get("min-ampl-side-c"); + } + if (ic.options().hasOption("min-sum-of-ampl")) { + mMinSumOfAmplitude = ic.options().get("min-sum-of-ampl"); + } + + mCalibrator = std::make_unique(mMinNumberOfEntries, mMinAmplitudeSideA, mMinAmplitudeSideC, mMinSumOfAmplitude); + + if (mOneObjectPerRun) { + LOG(info) << "Only one object will be created at the end of run"; + mCalibrator->setUpdateAtTheEndOfRunOnly(); + } + if (mOneObjectPerRun == false) { + LOG(info) << "Defined slot interval to " << mSlotLenSec << " seconds"; + mCalibrator->setSlotLengthInSeconds(mSlotLenSec); + } + } + + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) + { + o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + } + + void run(o2::framework::ProcessingContext& pc) final + { + const auto& tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged || mRunNoFromDH < 1) { // new run is starting + mRunNoFromDH = tinfo.runNumber; + } + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + auto digits = pc.inputs().get>("digits"); + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); + if (digits.size() == 0) { + return; + } + mCalibrator->process(digits); + if (mOneObjectPerRun == false) { + sendOutput(pc.outputs()); + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOG(info) << "Received end-of-stream, checking for slot to finalize..."; + mCalibrator->checkSlotsToFinalize(); + sendOutput(ec.outputs()); + mCalibrator->initOutput(); + } + + void sendOutput(o2::framework::DataAllocator& output) + { + using o2::framework::Output; + const auto& tvxHists = mCalibrator->getTvxPerBc(); + auto& infos = mCalibrator->getTvxPerBcCcdbInfo(); + for (unsigned int idx = 0; idx < tvxHists.size(); idx++) { + auto& info = infos[idx]; + const auto& payload = tvxHists[idx]; + + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, info.get()); + LOG(info) << "Sending object " << info->getPath() << "/" << info->getFileName() << " of size " << image->size() + << " bytes, valid for " << info->getStartValidityTimestamp() << " : " << info->getEndValidityTimestamp(); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, "EventsPerBc", idx}, *image.get()); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "EventsPerBc", idx}, *info.get()); + if (mSaveToFile) { + std::string fnout = fmt::format("ft0eventsPerBC_run_{}_{}_{}.root", mRunNoFromDH, info->getStartValidityTimestamp(), info->getEndValidityTimestamp()); + try { + TFile flout(fnout.c_str(), "recreate"); + flout.WriteObjectAny(&payload, "o2::ft0::EventsPerBc", o2::ccdb::CcdbApi::CCDBOBJECT_ENTRY); + LOGP(info, R"(Saved to file, can upload as: o2-ccdb-upload -f {} --starttimestamp {} --endtimestamp {} -k "ccdb_object" --path {} -m "runNumber={};AdjustableEOV=true;")", + fnout, info->getStartValidityTimestamp(), info->getEndValidityTimestamp(), info->getPath(), mRunNoFromDH); + flout.Close(); + } catch (const std::exception& ex) { + LOGP(error, "failed to store object to file {}, error: {}", fnout, ex.what()); + } + } + } + + if (tvxHists.size()) { + mCalibrator->initOutput(); + } + } + + private: + std::shared_ptr mCCDBRequest; + std::unique_ptr mCalibrator; + bool mOneObjectPerRun; + bool mSaveToFile = false; + int mRunNoFromDH = 0; + uint32_t mSlotLenSec; + uint32_t mMinNumberOfEntries; + int32_t mMinAmplitudeSideA; + int32_t mMinAmplitudeSideC; + int32_t mMinSumOfAmplitude; +}; +} // namespace o2::calibration +#endif diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeOffsetCalibration-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0TimeOffsetCalibration-Workflow.cxx similarity index 100% rename from Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeOffsetCalibration-Workflow.cxx rename to Detectors/FIT/FT0/calibration/workflow/FT0TimeOffsetCalibration-Workflow.cxx diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeSpectraProcessor-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0TimeSpectraProcessor-Workflow.cxx similarity index 95% rename from Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeSpectraProcessor-Workflow.cxx rename to Detectors/FIT/FT0/calibration/workflow/FT0TimeSpectraProcessor-Workflow.cxx index 541f5a9827a4a..214d919196763 100644 --- a/Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeSpectraProcessor-Workflow.cxx +++ b/Detectors/FIT/FT0/calibration/workflow/FT0TimeSpectraProcessor-Workflow.cxx @@ -40,8 +40,8 @@ class FT0TimeSpectraProcessor final : public o2::framework::Task int mTimeWindow{153}; uint8_t mPMbitsToCheck{0b11111110}; uint8_t mPMbitsGood{0b01001000}; - uint8_t mTrgBitsToCheck{0b11110000}; - uint8_t mTrgBitsGood{0b10010000}; + uint64_t mTrgBitsToCheck{0b11110000}; + uint64_t mTrgBitsGood{0b10010000}; void init(o2::framework::InitContext& ic) final { mNbinsY = ic.options().get("number-bins"); @@ -69,7 +69,8 @@ class FT0TimeSpectraProcessor final : public o2::framework::Task auto channels = pc.inputs().get>("channels"); o2::dataformats::FlatHisto2D timeSpectraInfoObject(sNCHANNELS + nExtraSpectraSlots, 0, sNCHANNELS + nExtraSpectraSlots, mNbinsY, mMinY, mMaxY); for (const auto& digit : digits) { - if ((digit.mTriggers.getTriggersignals() & mTrgBitsToCheck) != mTrgBitsGood) { + const uint64_t trgWordExt = digit.mTriggers.getExtendedTrgWordFT0(); + if ((trgWordExt & mTrgBitsToCheck) != mTrgBitsGood) { continue; } const auto& chan = digit.getBunchChannelData(channels); @@ -101,7 +102,7 @@ class FT0TimeSpectraProcessor final : public o2::framework::Task } } - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TIME_SPECTRA", 0, o2::framework::Lifetime::Timeframe}, timeSpectraInfoObject.getBase()); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TIME_SPECTRA", 0}, timeSpectraInfoObject.getBase()); } }; diff --git a/Detectors/FIT/FT0/dcsmonitoring/macros/makeFT0CCDBEntryForDCS.C b/Detectors/FIT/FT0/dcsmonitoring/macros/makeFT0CCDBEntryForDCS.C index 73a768ce03502..a5a1441d81726 100644 --- a/Detectors/FIT/FT0/dcsmonitoring/macros/makeFT0CCDBEntryForDCS.C +++ b/Detectors/FIT/FT0/dcsmonitoring/macros/makeFT0CCDBEntryForDCS.C @@ -54,7 +54,8 @@ int makeFT0CCDBEntryForDCS(const std::string ccdbUrl = "http://localhost:8080", "FT0/Trigger4_OrC/CNT_RATE", "FT0/Trigger5_OrA/CNT_RATE", "FT0/Background/[0..9]/CNT_RATE", - "FT0/Background/[A,B,C,D,E,F,G,H]/CNT_RATE"}; + "FT0/Background/[A,B,C,D,E,F,G,H]/CNT_RATE", + "FT0/SecondaryCounter/CEplusSC/CNT_RATE"}; std::vector expAliasesHV = o2::dcs::expandAliases(aliasesHV); std::vector expAliasesADC = o2::dcs::expandAlias(aliasesADC); diff --git a/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSDataProcessor.cxx b/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSDataProcessor.cxx index c525ac8357708..c5049c3401365 100644 --- a/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSDataProcessor.cxx +++ b/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSDataProcessor.cxx @@ -47,7 +47,8 @@ std::vector o2::ft0::FT0DCSDataProcessor::getHardC "FT0/Trigger4_OrC/CNT_RATE", "FT0/Trigger5_OrA/CNT_RATE", "FT0/Background/[0..9]/CNT_RATE", - "FT0/Background/[A,B,C,D,E,F,G,H]/CNT_RATE"}; + "FT0/Background/[A,B,C,D,E,F,G,H]/CNT_RATE", + "FT0/SecondaryCounter/CEplusSC/CNT_RATE"}; std::vector expAliasesHV = o2::dcs::expandAliases(aliasesHV); std::vector expAliasesADC = o2::dcs::expandAlias(aliasesADC); std::vector expAliasesRates = o2::dcs::expandAliases(aliasesRates); diff --git a/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSMonitoringLinkDef.h b/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSMonitoringLinkDef.h index c85dd2d378ccb..50a9e7f35e25f 100644 --- a/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSMonitoringLinkDef.h +++ b/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSMonitoringLinkDef.h @@ -15,4 +15,6 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::ft0::FT0DCSConfigReader + ; + #endif diff --git a/Detectors/FIT/FT0/dcsmonitoring/workflow/FT0DCSConfigProcessorSpec.h b/Detectors/FIT/FT0/dcsmonitoring/workflow/FT0DCSConfigProcessorSpec.h index d1a3cc4f4dec1..fe6701344530b 100644 --- a/Detectors/FIT/FT0/dcsmonitoring/workflow/FT0DCSConfigProcessorSpec.h +++ b/Detectors/FIT/FT0/dcsmonitoring/workflow/FT0DCSConfigProcessorSpec.h @@ -59,12 +59,14 @@ DataProcessorSpec getFT0DCSConfigProcessorSpec() return DataProcessorSpec{ "ft0-dcs-config-processor", - Inputs{{"inputConfig", o2::header::gDataOriginFT0, "DCS_CONFIG_FILE", Lifetime::Timeframe}, - {"inputConfigFileName", o2::header::gDataOriginFT0, "DCS_CONFIG_NAME", Lifetime::Timeframe}}, + Inputs{{"inputConfig", o2::header::gDataOriginFT0, "DCS_CONFIG_FILE", Lifetime::Sporadic}, + {"inputConfigFileName", o2::header::gDataOriginFT0, "DCS_CONFIG_NAME", Lifetime::Sporadic}}, outputs, AlgorithmSpec{adaptFromTask("FT0", ddDChM)}, Options{{"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}, - {"filename-dchm", VariantType::String, "FT0-deadchannels.txt", {"Dead channel map file name"}}}}; + {"filename-dchm", VariantType::String, "FT0-deadchannels.txt", {"Dead channel map file name"}}, + {"valid-days-dchm", VariantType::UInt32, 180u, {"Dead channel map validity in days"}}, + {"no-validate", VariantType::Bool, false, {"Don't validate the CCDB uploads"}}}}; } } // namespace framework diff --git a/Detectors/FIT/FT0/dcsmonitoring/workflow/ft0-dcs-sim-workflow.cxx b/Detectors/FIT/FT0/dcsmonitoring/workflow/ft0-dcs-sim-workflow.cxx index b728a1c4c85b2..58802f0e51d16 100644 --- a/Detectors/FIT/FT0/dcsmonitoring/workflow/ft0-dcs-sim-workflow.cxx +++ b/Detectors/FIT/FT0/dcsmonitoring/workflow/ft0-dcs-sim-workflow.cxx @@ -43,6 +43,7 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co dphints.emplace_back(o2::dcs::test::DataPointHint{"FT0/Trigger5_OrA/CNT_RATE", 0, 5000000}); dphints.emplace_back(o2::dcs::test::DataPointHint{"FT0/Background/[0..9]/CNT_RATE", 0, 50000}); dphints.emplace_back(o2::dcs::test::DataPointHint{"FT0/Background/[A,B,C,D,E,F,G,H]/CNT_RATE", 0, 50000}); + dphints.emplace_back(o2::dcs::test::DataPointHint{"FT0/SecondaryCounter/CEplusSC/CNT_RATE", 0, 5000000}); o2::framework::WorkflowSpec specs; specs.emplace_back(o2::dcs::test::getDCSRandomDataGeneratorSpec(dphints, "FT0")); diff --git a/Detectors/FIT/FT0/macros/CMakeLists.txt b/Detectors/FIT/FT0/macros/CMakeLists.txt index c4ed27d2513ba..17491ca4962c1 100644 --- a/Detectors/FIT/FT0/macros/CMakeLists.txt +++ b/Detectors/FIT/FT0/macros/CMakeLists.txt @@ -1,14 +1,21 @@ -# Copyright CERN and copyright holders of ALICE O2. This software is distributed -# under the terms of the GNU General Public License v3 (GPL Version 3), copied -# verbatim in the file "COPYING". +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. # -# See http://alice-o2.web.cern.ch/license for full licensing information. +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". # # In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization or -# submit itself to any jurisdiction. +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. o2_add_test_root_macro(FT0Misaligner.C PUBLIC_LINK_LIBRARIES O2::CCDB O2::FT0Simulation LABELS ft0) + +o2_add_test_root_macro(FT0readEventsPerBc.C + PUBLIC_LINK_LIBRARIES + O2::CCDB + O2::DataFormatsFT0 + LABELS ft0) diff --git a/Detectors/FIT/FT0/macros/FT0Misaligner.C b/Detectors/FIT/FT0/macros/FT0Misaligner.C index 7585411066934..16476ae3b8ccc 100644 --- a/Detectors/FIT/FT0/macros/FT0Misaligner.C +++ b/Detectors/FIT/FT0/macros/FT0Misaligner.C @@ -1,15 +1,30 @@ +// Copyright 2021-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FT0Misaligner.C +/// \brief ROOT macro for creating an FT0 geometry alignment object. Based on ITSMisaligner.C +/// +/// \author Andreas Molander andreas.molander@cern.ch, Alla Maevskaya + #if !defined(__CLING__) || defined(__ROOTCLING__) -//#define ENABLE_UPGRADES + +#include "CCDB/CcdbApi.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "DetectorsCommonDataFormats/AlignParam.h" -#include "DetectorsBase/GeometryManager.h" -#include "CCDB/CcdbApi.h" -#include "FT0Base/Geometry.h" -#include + #include #include #include + #endif using AlgPar = std::array; @@ -23,19 +38,15 @@ void FT0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" const std::string& fileName = "FT0Alignment.root") { std::vector params; - o2::base::GeometryManager::loadGeometry("", false); - // auto geom = o2::ft0::Geometry::Instance(); AlgPar pars; bool glo = true; o2::detectors::DetID detFT0("FT0"); - // FT0 detector - //set A side std::string symNameA = "FT0A"; pars = generateMisalignment(xA, yA, zA, psiA, thetaA, phiA); params.emplace_back(symNameA.c_str(), -1, pars[0], pars[1], pars[2], pars[3], pars[4], pars[5], glo); - //set C side + std::string symNameC = "FT0C"; pars = generateMisalignment(xC, yC, zC, psiC, thetaC, phiC); params.emplace_back(symNameC.c_str(), -1, pars[0], pars[1], pars[2], pars[3], pars[4], pars[5], glo); @@ -44,7 +55,7 @@ void FT0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" std::string path = objectPath.empty() ? o2::base::DetectorNameConf::getAlignmentPath(detFT0) : objectPath; LOGP(info, "Storing alignment object on {}/{}", ccdbHost, path); o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdbHost.c_str()); // or http://localhost:8080 for a local installation // store abitrary user object in strongly typed manner api.storeAsTFileAny(¶ms, path, metadata, tmin, tmax); @@ -57,14 +68,15 @@ void FT0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" algFile.Close(); } } + AlgPar generateMisalignment(double x, double y, double z, double psi, double theta, double phi) { AlgPar pars; - pars[0] = gRandom->Gaus(0, x); - pars[1] = gRandom->Gaus(0, y); - pars[2] = gRandom->Gaus(0, z); - pars[3] = gRandom->Gaus(0, psi); - pars[4] = gRandom->Gaus(0, theta); - pars[5] = gRandom->Gaus(0, phi); + pars[0] = x; + pars[1] = y; + pars[2] = z; + pars[3] = psi; + pars[4] = theta; + pars[5] = phi; return std::move(pars); } diff --git a/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C b/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C new file mode 100644 index 0000000000000..c6afc86389b9b --- /dev/null +++ b/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C @@ -0,0 +1,52 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#endif + +#include "CCDB/CcdbApi.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "TH1F.h" +#include "DataFormatsFT0/EventsPerBc.h" +#include "Framework/Logger.h" +#include "CommonConstants/LHCConstants.h" + +std::unique_ptr hist; +std::unique_ptr canvas; + +void FT0readEventsPerBc(std::string ccdbUrl, long timestamp) +{ + o2::ccdb::CcdbApi ccdbApi; + ccdbApi.init(ccdbUrl); + const std::string ccdbPath = "FT0/Calib/EventsPerBc"; + std::map metadata; + + if (timestamp < 0) { + timestamp = o2::ccdb::getCurrentTimestamp(); + } + + std::unique_ptr events(ccdbApi.retrieveFromTFileAny(ccdbPath, metadata, timestamp)); + + if (!events) { + LOGP(fatal, "EventsPerBc object not found in {}/{} for timestamp {}.", ccdbUrl, ccdbPath, timestamp); + return; + } + + hist = std::make_unique("eventsPerBcHist", "Events per BC", o2::constants::lhc::LHCMaxBunches, 0, o2::constants::lhc::LHCMaxBunches - 1); + for (int idx = 0; idx < o2::constants::lhc::LHCMaxBunches; idx++) { + hist->Fill(idx, events->histogram[idx]); + } + canvas = std::make_unique(); + hist->Draw(); + canvas->Draw(); +} \ No newline at end of file diff --git a/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h b/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h index 716d6795e5ad2..84d2d1a0bcd3a 100644 --- a/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h +++ b/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -//file DigitBlockFT0.h class for proccessing RAW data into Digits +// file DigitBlockFT0.h class for proccessing RAW data into Digits // // Artur.Furs // afurs@cern.ch @@ -25,10 +25,10 @@ namespace o2 { namespace ft0 { -//Normal data taking mode -using DigitBlockFT0 = DigitBlockFIT; -//TCM extended data taking mode -using DigitBlockFT0ext = DigitBlockFIText; +// Normal data taking mode +using DigitBlockFT0 = o2::fit::DigitBlockFIT; +// TCM extended data taking mode +using DigitBlockFT0ext = o2::fit::DigitBlockFIText; } // namespace ft0 } // namespace o2 #endif diff --git a/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx b/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx index f08f0bc1042db..69be24e0e0f9b 100644 --- a/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx +++ b/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx @@ -10,3 +10,7 @@ // or submit itself to any jurisdiction. #include "FT0Raw/DataBlockFT0.h" + +template class o2::fit::DataBlockPM, o2::ft0::RawHeaderPM, o2::ft0::RawDataPM>; +template class o2::fit::DataBlockTCM, o2::ft0::RawHeaderTCM, o2::ft0::RawDataTCM>; +template class o2::fit::DataBlockTCMext, o2::ft0::RawHeaderTCMext, o2::ft0::RawDataTCM, o2::ft0::RawDataTCMext>; diff --git a/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx b/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx index b474afff032bd..80ecb470f0a62 100644 --- a/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx +++ b/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FT0Raw/DigitBlockFT0.h" + +template class o2::fit::DigitBlockFIT; +template class o2::fit::DigitBlockFIText; \ No newline at end of file diff --git a/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx b/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx index dea616c706ccc..1e5984c5aea3e 100644 --- a/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx +++ b/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FT0Raw/RawReaderFT0Base.h" + +template class o2::fit::RawReaderBaseFIT; +template class o2::fit::RawReaderBaseFIT; diff --git a/Detectors/FIT/FT0/raw/src/RawWriterFT0.cxx b/Detectors/FIT/FT0/raw/src/RawWriterFT0.cxx index cd57b0ea4fe0e..f563e9de8e1fc 100644 --- a/Detectors/FIT/FT0/raw/src/RawWriterFT0.cxx +++ b/Detectors/FIT/FT0/raw/src/RawWriterFT0.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FT0Raw/RawWriterFT0.h" + +template class o2::fit::RawWriterFIT; +template class o2::fit::RawWriterFIT; diff --git a/Detectors/FIT/FT0/reconstruction/CMakeLists.txt b/Detectors/FIT/FT0/reconstruction/CMakeLists.txt index e76155eebd158..9a4a14af6aca6 100644 --- a/Detectors/FIT/FT0/reconstruction/CMakeLists.txt +++ b/Detectors/FIT/FT0/reconstruction/CMakeLists.txt @@ -25,8 +25,7 @@ o2_add_library(FT0Reconstruction O2::DetectorsCalibration) o2_target_root_dictionary(FT0Reconstruction - HEADERS include/FT0Reconstruction/CollisionTimeRecoTask.h - include/FT0Reconstruction/InteractionTag.h) + HEADERS include/FT0Reconstruction/InteractionTag.h) o2_add_executable( test-raw-conversion diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h index 0566bf9883baa..41f11e303db67 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -26,7 +26,6 @@ #include "DetectorsCommonDataFormats/DetID.h" #include "FT0Base/FT0DigParam.h" #include "DetectorsBase/CTFCoderBase.h" -#include "rANS/rans.h" class TTree; @@ -35,10 +34,10 @@ namespace o2 namespace ft0 { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FT0) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FT0, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF @@ -73,15 +72,15 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& dig using MD = o2::ctf::Metadata::OptStore; // what to do which each field: see o2::ctd::Metadata explanation constexpr MD optField[CTF::getNBlocks()] = { - MD::EENCODE, // BLC_trigger - MD::EENCODE, // BLC_bcInc - MD::EENCODE, // BLC_orbitInc - MD::EENCODE, // BLC_nChan - MD::EENCODE, // BLC_status - MD::EENCODE, // BLC_idChan - MD::EENCODE, // BLC_qtcChain - MD::EENCODE, // BLC_cfdTime - MD::EENCODE // BLC_qtcAmpl + MD::EENCODE_OR_PACK, // BLC_trigger + MD::EENCODE_OR_PACK, // BLC_bcInc + MD::EENCODE_OR_PACK, // BLC_orbitInc + MD::EENCODE_OR_PACK, // BLC_nChan + MD::EENCODE_OR_PACK, // BLC_status + MD::EENCODE_OR_PACK, // BLC_idChan + MD::EENCODE_OR_PACK, // BLC_qtcChain + MD::EENCODE_OR_PACK, // BLC_cfdTime + MD::EENCODE_OR_PACK // BLC_qtcAmpl }; CompressedDigits cd; if (mExtHeader.isValidDictTimeStamp()) { @@ -102,11 +101,10 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& dig ec->setHeader(cd.header); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODEFT0(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODEFT0(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODEFT0(cd.trigger, CTF::BLC_trigger, 0); iosize += ENCODEFT0(cd.bcInc, CTF::BLC_bcInc, 0); @@ -134,7 +132,7 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VDIG& digitVec, VCHAN& checkDictVersion(hd); ec.print(getPrefix(), mVerbosity); o2::ctf::CTFIOSize iosize; -#define DECODEFT0(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODEFT0(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODEFT0(cd.trigger, CTF::BLC_trigger); iosize += DECODEFT0(cd.bcInc, CTF::BLC_bcInc); @@ -167,7 +165,9 @@ void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& cha uint32_t firstEntry = 0, clCount = 0, chipCount = 0; o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); - + if (mBCShift && ir.toLong() >= mBCShift) { + ir -= mBCShift; + } for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { // restore ROFRecord if (cd.orbitInc[idig]) { // non-0 increment => new orbit diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h index b866043afaa58..9f6cd500b9e74 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h @@ -20,6 +20,8 @@ #include "DataFormatsFT0/RecPoints.h" #include "DataFormatsFT0/FT0ChannelTimeCalibrationObject.h" #include "DataFormatsFT0/SpectraInfoObject.h" +#include "DataFormatsFT0/SlewingCoef.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include #include #include @@ -51,18 +53,22 @@ class CollisionTimeRecoTask std::vector& outChData); void FinishTask(); void SetTimeCalibObject(o2::ft0::TimeSpectraInfoObject const* timeCalibObject) { mTimeCalibObject = timeCalibObject; }; - void SetSlew(std::array* calibslew) + void SetSlewingCalibObject(o2::ft0::SlewingCoef const* calibSlew) { - LOG(info) << "@@@SetSlew " << calibslew->size(); - mCalibSlew = calibslew; + LOG(info) << "Init for slewing calib object"; + mCalibSlew = calibSlew->makeSlewingPlots(); }; + void SetDeadChannelMap(const o2::fit::DeadChannelMap* deadChannelMap) + { + LOG(info) << "Updated dead channel map for CollisionTimeRecoTask"; + mDeadChannelMap = deadChannelMap; + } float getTimeInPS(const o2::ft0::ChannelData& channelData); private: o2::ft0::TimeSpectraInfoObject const* mTimeCalibObject = nullptr; - std::array* mCalibSlew = nullptr; - - ClassDefNV(CollisionTimeRecoTask, 3); + const o2::fit::DeadChannelMap* mDeadChannelMap = nullptr; + typename o2::ft0::SlewingCoef::SlewingPlots_t mCalibSlew{}; }; } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h index 408eed72e3af1..c0007ac3a4188 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h @@ -26,11 +26,12 @@ namespace ft0 // These are configurable params for FT0 selection as interaction tag struct InteractionTag : public o2::conf::ConfigurableParamHelper { - int minAmplitudeAC = 0; ///< use only FT0 triggers with high enough amplitude - + int minAmplitudeAC = 2; ///< use only FT0 triggers with high enough amplitude + int minAmplitudeA = 1; + int minAmplitudeC = 1; bool isSelected(const RecPoints& rp) const { - return rp.getTrigger().getVertex() && (rp.getTrigger().getAmplA() + rp.getTrigger().getAmplC()) > minAmplitudeAC; + return rp.getTrigger().getVertex() && rp.getTrigger().getAmplA() >= minAmplitudeA && rp.getTrigger().getAmplC() >= minAmplitudeC && (rp.getTrigger().getAmplA() + rp.getTrigger().getAmplC()) >= minAmplitudeAC; } O2ParamDef(InteractionTag, "ft0tag"); diff --git a/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx b/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx index 6e840c52772cb..349c2edf796a6 100644 --- a/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx +++ b/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx @@ -53,7 +53,7 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); CompressedDigits cd; // just to get member types -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(cd.trigger, CTF::BLC_trigger); MAKECODER(cd.bcInc, CTF::BLC_bcInc); @@ -71,22 +71,18 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa size_t CTFCoder::estimateCompressedSize(const CompressedDigits& cd) { size_t sz = 0; - // clang-format off // RS FIXME this is very crude estimate, instead, an empirical values should be used -#define VTP(vec) typename std::remove_reference::type::value_type -#define ESTSIZE(vec, slot) mCoders[int(slot)] ? \ - rans::calculateMaxBufferSize(vec.size(), reinterpret_cast*>(mCoders[int(slot)].get())->getAlphabetRangeBits(), sizeof(VTP(vec)) ) : vec.size()*sizeof(VTP(vec)) - sz += ESTSIZE(cd.trigger, CTF::BLC_trigger); - sz += ESTSIZE(cd.bcInc, CTF::BLC_bcInc); - sz += ESTSIZE(cd.orbitInc, CTF::BLC_orbitInc); - sz += ESTSIZE(cd.nChan, CTF::BLC_nChan); - sz += ESTSIZE(cd.eventStatus, CTF::BLC_status); - sz += ESTSIZE(cd.idChan, CTF::BLC_idChan); - sz += ESTSIZE(cd.qtcChain, CTF::BLC_qtcChain); - sz += ESTSIZE(cd.cfdTime, CTF::BLC_cfdTime); - sz += ESTSIZE(cd.qtcAmpl, CTF::BLC_qtcAmpl); + sz += estimateBufferSize(static_cast(CTF::BLC_trigger), cd.trigger); + sz += estimateBufferSize(static_cast(CTF::BLC_bcInc), cd.bcInc); + sz += estimateBufferSize(static_cast(CTF::BLC_orbitInc), cd.orbitInc); + sz += estimateBufferSize(static_cast(CTF::BLC_nChan), cd.nChan); + sz += estimateBufferSize(static_cast(CTF::BLC_status), cd.eventStatus); + sz += estimateBufferSize(static_cast(CTF::BLC_idChan), cd.idChan); + sz += estimateBufferSize(static_cast(CTF::BLC_qtcChain), cd.qtcChain); + sz += estimateBufferSize(static_cast(CTF::BLC_cfdTime), cd.cfdTime); + sz += estimateBufferSize(static_cast(CTF::BLC_qtcAmpl), cd.qtcAmpl); // clang-format on - LOG(info) << "Estimated output size is " << sz << " bytes"; + LOG(debug) << "Estimated output size is " << sz << " bytes"; return sz; -} +} \ No newline at end of file diff --git a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx index c981fee23ce93..3e3ffe52671e9 100644 --- a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx +++ b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx @@ -36,8 +36,6 @@ void CollisionTimeRecoTask::processTF(const gsl::span& dig std::vector& vecRecPoints, std::vector& vecChData) { - // vecRecPoints.reserve(digits.size()); - // vecChData.reserve(channels.size()); for (const auto& digit : digits) { if (!ChannelFilterParam::Instance().checkTCMbits(digit.getTriggers().getTriggersignals())) { continue; @@ -58,30 +56,49 @@ RP CollisionTimeRecoTask::processDigit(const o2::ft0::Digit& digit, float sideCtime = 0; constexpr int nMCPsA = 4 * Geometry::NCellsA; - const auto parInv = FT0DigParam::Instance().mMV_2_NchannelsInverse; int nch{0}; + bool isActiveA = false; + bool isActiveC = false; + bool isFlangeEvent = false; + for (const auto& channelData : inChData) { + if (channelData.ChId >= NCHANNELS) { + // Reference channels shouldn't participate in reco at all! + continue; + } + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(channelData.ChId)) { + LOG(debug) << "Channel " << channelData.ChId << " is dead - discarding data"; + continue; + } const float timeInPS = getTimeInPS(channelData); if (ChannelFilterParam::Instance().checkAll(channelData)) { outChData.emplace_back(channelData.ChId, timeInPS, (float)channelData.QTCAmpl, channelData.ChainQTC); nch++; } - // only signals with amplitude participate in collision time - if (TimeFilterParam::Instance().checkAll(channelData)) { - if (channelData.ChId < nMCPsA) { + const bool isOkForTimeCalc = TimeFilterParam::Instance().checkAll(channelData); + // only signals which satisfy conditions may participate in time calculation + if (channelData.ChId < nMCPsA) { + // A-side + if (isOkForTimeCalc) { sideAtime += timeInPS; ndigitsA++; - } else if (channelData.ChId < NCHANNELS) { + } + isActiveA = true; + } else { + // C-side + if (isOkForTimeCalc) { sideCtime += timeInPS; ndigitsC++; } + isActiveC = true; + isFlangeEvent |= channelData.CFDTime < -350 && channelData.CFDTime > -450; } } std::array mCollisionTime = {RP::sDummyCollissionTime, RP::sDummyCollissionTime, RP::sDummyCollissionTime, RP::sDummyCollissionTime}; - // !!!! tobe done::should be fix with ITS vertex - mCollisionTime[TimeA] = (ndigitsA > 0) ? sideAtime / ndigitsA : RP::sDummyCollissionTime; // 2 * o2::InteractionRecord::DummyTime; - mCollisionTime[TimeC] = (ndigitsC > 0) ? sideCtime / ndigitsC : RP::sDummyCollissionTime; // 2 * o2::InteractionRecord::DummyTime; + + mCollisionTime[TimeA] = (ndigitsA > 0) ? std::round(sideAtime / ndigitsA) : RP::sDummyCollissionTime; // 2 * o2::InteractionRecord::DummyTime; + mCollisionTime[TimeC] = (ndigitsC > 0) ? std::round(sideCtime / ndigitsC) : RP::sDummyCollissionTime; // 2 * o2::InteractionRecord::DummyTime; if (ndigitsA > 0 && ndigitsC > 0) { mCollisionTime[Vertex] = (mCollisionTime[TimeA] - mCollisionTime[TimeC]) / 2.; @@ -89,7 +106,8 @@ RP CollisionTimeRecoTask::processDigit(const o2::ft0::Digit& digit, } else { mCollisionTime[TimeMean] = std::min(mCollisionTime[TimeA], mCollisionTime[TimeC]); } - return RecPoints{mCollisionTime, firstEntry, nch, digit.mIntRecord, digit.mTriggers}; + const uint8_t extraTrgWord = RecPoints::makeExtraTrgWord(isActiveA, isActiveC, isFlangeEvent); + return RecPoints(firstEntry, nch, digit.mIntRecord, mCollisionTime, digit.mTriggers, extraTrgWord); } //______________________________________________________ void CollisionTimeRecoTask::FinishTask() @@ -100,9 +118,9 @@ void CollisionTimeRecoTask::FinishTask() float CollisionTimeRecoTask::getTimeInPS(const o2::ft0::ChannelData& channelData) { + // Getting time offset float offsetChannel{0}; - float slewoffset{0}; - if (mTimeCalibObject && channelData.ChId < NCHANNELS) { + if (mTimeCalibObject) { // Temporary, will be changed to status bit checking // Check statistics const auto& stat = mTimeCalibObject->mTime[channelData.ChId].mStat; @@ -122,12 +140,12 @@ float CollisionTimeRecoTask::getTimeInPS(const o2::ft0::ChannelData& channelData offsetChannel = meanHist; } } - /* - if (mCalibSlew && channelData.ChId < NCHANNELS) { - TGraph& gr = mCalibSlew->at(channelData.ChId); - slewoffset = gr.Eval(channelData.QTCAmpl); - } - */ - const float globalOffset = (offsetChannel + slewoffset) * Geometry::ChannelWidth; - return float(channelData.CFDTime) * Geometry::ChannelWidth - globalOffset; + // Getting slewing offset + float slewoffset{0}; + const auto& gr = mCalibSlew[static_cast(channelData.getFlag(o2::ft0::ChannelData::EEventDataBit::kNumberADC))][channelData.ChId]; + slewoffset = gr.Eval(channelData.QTCAmpl); + + // Final calculation + const float globalOffset = offsetChannel + slewoffset; + return (static_cast(channelData.CFDTime) - globalOffset) * Geometry::ChannelWidth; } diff --git a/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h b/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h index 81153dfff9132..e35f871e91c7c 100644 --- a/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h +++ b/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h @@ -15,7 +15,6 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::ft0::CollisionTimeRecoTask + ; #pragma link C++ class o2::ft0::InteractionTag + ; #endif diff --git a/Detectors/FIT/FT0/simulation/CMakeLists.txt b/Detectors/FIT/FT0/simulation/CMakeLists.txt index f94d085d5b48e..1559649755c4b 100644 --- a/Detectors/FIT/FT0/simulation/CMakeLists.txt +++ b/Detectors/FIT/FT0/simulation/CMakeLists.txt @@ -31,12 +31,8 @@ o2_target_root_dictionary(FT0Simulation o2_add_executable(digi2raw COMPONENT_NAME ft0 SOURCES src/digit2raw.cxx - PUBLIC_LINK_LIBRARIES O2::FT0Simulation - O2::DetectorsRaw - O2::DetectorsCommonDataFormats - O2::CommonUtils - Boost::program_options - O2::FT0Raw) + PUBLIC_LINK_LIBRARIES O2::FT0Raw + Boost::program_options) o2_data_file(COPY data DESTINATION Detectors/FT0/simulation) diff --git a/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h b/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h index bd5693707673f..3a035a247c6ef 100644 --- a/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h +++ b/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h @@ -13,6 +13,7 @@ #define ALICEO2_FT0_DIGITIZER_H #include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include "DataFormatsFT0/Digit.h" #include "DataFormatsFT0/ChannelData.h" #include "DataFormatsFT0/MCLabel.h" @@ -73,6 +74,7 @@ class Digitizer void SetChannelOffset(o2::ft0::FT0ChannelTimeCalibrationObject const* caliboffsets) { mCalibOffset = caliboffsets; }; + void setDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) { mDeadChannelMap = deadChannelMap; }; double getTimeOffsetWrtBC() const { return mIntRecord.getTimeOffsetWrtBC(); } struct CFDOutput { @@ -165,6 +167,7 @@ class Digitizer o2::dataformats::MCTruthContainer& labels); o2::ft0::FT0ChannelTimeCalibrationObject const* mCalibOffset = nullptr; + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; ClassDefNV(Digitizer, 3); }; diff --git a/Detectors/FIT/FT0/simulation/src/Detector.cxx b/Detectors/FIT/FT0/simulation/src/Detector.cxx index 015b709355f98..fb756adba7df5 100644 --- a/Detectors/FIT/FT0/simulation/src/Detector.cxx +++ b/Detectors/FIT/FT0/simulation/src/Detector.cxx @@ -882,18 +882,10 @@ TGeoVolume* Detector::constructFrameCGeometry() Double_t yPMT[NCellsC]; Double_t zPMT[NCellsC]; - Double_t aPMT[NCellsC]; - Double_t bPMT[NCellsC]; - Double_t gPMT[NCellsC]; - Double_t xQrad[NCellsC]; Double_t yQrad[NCellsC]; Double_t zQrad[NCellsC]; - Double_t aQrad[NCellsC]; - Double_t bQrad[NCellsC]; - Double_t gQrad[NCellsC]; - Double_t rotC[NCellsC]; Double_t comC[NCellsC]; @@ -903,31 +895,10 @@ TGeoVolume* Detector::constructFrameCGeometry() yPMT[i] = scalePMT * yc2[i]; zPMT[i] = scalePMT * zc2[i]; - aPMT[i] = TMath::ATan(yPMT[i] / xPMT[i]) - TMath::Pi() / 2 + 2 * TMath::Pi(); - if (xPMT[i] < 0) { - bPMT[i] = TMath::ACos(zPMT[i] / crad); - } else { - bPMT[i] = -1 * TMath::ACos(zPMT[i] / crad); - } - - aPMT[i] *= 180 / TMath::Pi(); - bPMT[i] *= 180 / TMath::Pi(); - gPMT[i] = -1 * aPMT[i]; - // Quartz radiator transformations xQrad[i] = scaleQrad * xc2[i]; yQrad[i] = scaleQrad * yc2[i]; zQrad[i] = scaleQrad * zc2[i]; - - aQrad[i] = TMath::ATan(yQrad[i] / xQrad[i]) - TMath::Pi() / 2 + 2 * TMath::Pi(); - if (xQrad[i] < 0) { - bQrad[i] = TMath::ACos(zQrad[i] / crad); - } else { - bQrad[i] = -1 * TMath::ACos(zQrad[i] / crad); - } - aQrad[i] *= 180 / TMath::Pi(); - bQrad[i] *= 180 / TMath::Pi(); - gQrad[i] = -1 * aQrad[i]; } TString nameRot; @@ -1297,7 +1268,7 @@ void Detector::DefineOpticalProperties() inputDir += "/share/Detectors/FT0/files/"; TString optPropPath = inputDir + "quartzOptProperties.txt"; - optPropPath = gSystem->ExpandPathName(optPropPath.Data()); // Expand $(ALICE_ROOT) into real system path + gSystem->ExpandPathName(optPropPath); // Expand $(ALICE_ROOT) into real system path Int_t result = ReadOptProperties(optPropPath.Data()); if (result < 0) { @@ -1455,15 +1426,15 @@ void Detector::DefineSim2LUTindex() } inputDir += "/share/Detectors/FT0/files/"; - std::string indPath = inputDir + "Sim2DataChannels.txt"; - indPath = gSystem->ExpandPathName(indPath.data()); // Expand $(ALICE_ROOT) into real system path + TString indPath = inputDir + "Sim2DataChannels.txt"; + gSystem->ExpandPathName(indPath); // Expand $(ALICE_ROOT) into real system path std::ifstream infile; - infile.open(indPath.data()); - LOG(info) << " file open " << indPath.data(); + infile.open(indPath.Data()); + LOG(info) << " file open " << indPath.Data(); // Check if file is opened correctly if (infile.fail() == true) { - LOG(error) << "Error opening ascii file (it is probably a folder!): " << indPath.c_str(); + LOG(error) << "Error opening ascii file (it is probably a folder!): " << indPath; } int fromfile; for (int iind = 0; iind < Geometry::Nchannels; iind++) { diff --git a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx old mode 100644 new mode 100755 index 583f62019f8a0..6f270216a0267 --- a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx @@ -13,7 +13,16 @@ #include "FT0Simulation/DigitizationConstants.h" #include "FT0Base/FT0DigParam.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include +#include "CommonConstants/PhysicsConstants.h" +#include "CommonDataFormat/InteractionRecord.h" + +#include "DataFormatsFT0/LookUpTable.h" +#include "FT0Base/Constants.h" +#include +#include +#include +#include +#include #include "TMath.h" #include "TRandom.h" @@ -34,24 +43,84 @@ namespace o2::ft0 template Float signalForm_i(Float x) { - using namespace std; - Float const a = -0.45458; - Float const b = -0.83344945; - return x > Float(0) ? -(exp(b * x) - exp(a * x)) / Float(7.8446501) : Float(0); + float p0, p1, p2, p3, p4, p5, p6, p7; + p0 = 1.30853; + p1 = 0.516807; + p2 = 3.36714; + p3 = -1.01206; + p4 = 1.42832; + p5 = 1.1589; + p6 = 1.22019; + p7 = 0.426818; + + Double_t val = 0; + + if (x > p3) { + Double_t x1 = x - p3; + Double_t arg1 = (log(x1) - p0) / p1; + val += p2 * (1.0 / (x1 * p1 * sqrt(2 * TMath::Pi()))) * exp(-0.5 * arg1 * arg1); + } + + if (x > p7) { + Double_t x2 = x - p7; + Double_t arg2 = (log(x2) - p4) / p5; + val += p6 * (1.0 / (x2 * p5 * sqrt(2 * TMath::Pi()))) * exp(-0.5 * arg2 * arg2); + } + + return val; }; // integrated signal shape function inline float signalForm_integral(float x) { - using namespace std; - double const a = -0.45458; - double const b = -0.83344945; - if (x < 0) { - x = 0; + float p0, p1, p2, p3, p4, p5, p6, p7; + p0 = 1.30853; + p1 = 0.516807; + p2 = 3.36714; + p3 = -1.01206; + p4 = 1.42832; + p5 = 1.1589; + p6 = 1.22019; + p7 = 0.426818; + Double_t val = 0; + + if (x > p3) { + Double_t x1 = x - p3; + Double_t z1 = (log(x1) - p0) / (sqrt(2) * p1); + val += p2 * 0.5 * (1 + TMath::Erf(z1)); // norm1 * CDF1 } - return -(exp(b * x) / b - exp(a * x) / a) / 7.8446501; + + if (x > p7) { + Double_t x2 = x - p7; + Double_t z2 = (log(x2) - p4) / (sqrt(2) * p5); + val += p6 * 0.5 * (1 + TMath::Erf(z2)); // norm2 * CDF2 + } + + return val; +}; +/* +// signal shape function +template +Float signalForm_i(Float x) +{ +using namespace std; +Float const a = -0.45458; +Float const b = -0.83344945; +return x > Float(0) ? -(exp(b * x) - exp(a * x)) / Float(7.8446501) : Float(0); }; +// integrated signal shape function +inline float signalForm_integral(float x) +{ +using namespace std; +double const a = -0.45458; +double const b = -0.83344945; +if (x < 0) { + x = 0; +} +return -(exp(b * x) / b - exp(a * x) / a) / 7.8446501; +}; +*/ // SIMD version of the integrated signal shape function inline Vc::float_v signalForm_integralVc(Vc::float_v x) { @@ -204,14 +273,27 @@ void Digitizer::process(const std::vector* hits, if (hit.GetEnergyLoss() > 0) { continue; } - const auto& params = FT0DigParam::Instance(); + Int_t hit_ch = hit.GetDetectorID(); + + // If the dead channel map is used, and the channel with ID 'hit_ch' is dead, don't process this hit. + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(hit_ch)) { + continue; + } + + const auto& params = FT0DigParam::Instance(); + Bool_t is_A_side = (hit_ch < 4 * mGeometry.NCellsA); - Float_t time_compensate = is_A_side ? params.mA_side_cable_cmps : params.mC_side_cable_cmps; - Double_t hit_time = hit.GetTime() - time_compensate; + + // Subtract time-of-flight from hit time + const Float_t timeOfFlight = hit.GetPos().R() / o2::constants::physics::LightSpeedCm2NS; + const Float_t timeOffset = is_A_side ? params.hitTimeOffsetA : params.hitTimeOffsetC; + Double_t hit_time = hit.GetTime() - timeOfFlight + timeOffset + mIntRecord.getTimeOffsetWrtBC(); + if (hit_time > 150) { continue; // not collect very slow particles } + auto relBC = o2::InteractionRecord{hit_time}; if (mCache.size() <= relBC.bc) { mCache.resize(relBC.bc + 1); @@ -235,19 +317,111 @@ void Digitizer::storeBC(BCCache& bc, if (bc.hits.empty()) { return; } + // Initialize mapping channelID -> PM hash and PM side (A/C) using FT0 LUT + static bool pmLutInitialized = false; + static std::array mChID2PMhash{}; + static std::map mMapPMhash2isAside; // hashed PM -> is A side + + if (!pmLutInitialized) { + std::map mapFEE2hash; // module name -> hashed PM id + uint8_t tcmHash = 0; + + const auto& lut = o2::ft0::SingleLUT::Instance().getVecMetadataFEE(); + auto lutSorted = lut; + std::sort(lutSorted.begin(), lutSorted.end(), + [](const auto& first, const auto& second) { return first.mModuleName < second.mModuleName; }); + + uint8_t binPos = 0; + for (const auto& lutEntry : lutSorted) { + const auto& moduleName = lutEntry.mModuleName; + const auto& moduleType = lutEntry.mModuleType; + const auto& strChID = lutEntry.mChannelID; + + auto [it, inserted] = mapFEE2hash.insert({moduleName, binPos}); + if (inserted) { + if (moduleName.find("PMA") != std::string::npos) { + mMapPMhash2isAside.insert({binPos, true}); + } else if (moduleName.find("PMC") != std::string::npos) { + mMapPMhash2isAside.insert({binPos, false}); + } + ++binPos; + } + + if (std::regex_match(strChID, std::regex("^[0-9]{1,3}$"))) { + int chID = std::stoi(strChID); + if (chID < o2::ft0::Constants::sNCHANNELS_PM) { + mChID2PMhash[chID] = mapFEE2hash[moduleName]; + } else { + LOG(fatal) << "Incorrect LUT entry: chID " << strChID << " | " << moduleName; + } + } else if (moduleType != "TCM") { + LOG(fatal) << "Non-TCM module w/o numerical chID: chID " << strChID << " | " << moduleName; + } else { // TCM + tcmHash = mapFEE2hash[moduleName]; + } + } + + pmLutInitialized = true; + } + int n_hit_A = 0, n_hit_C = 0, mean_time_A = 0, mean_time_C = 0; int summ_ampl_A = 0, summ_ampl_C = 0; + int sum_A_ampl = 0, sum_C_ampl = 0; + int nPMTs = mGeometry.NCellsA * 4 + mGeometry.NCellsC * 4; + std::vector sum_ampl_ipmt(nPMTs, 0); + // Per-PM summed charge (like in digits2trgFT0) + std::map mapPMhash2sumAmpl; + for (const auto& entry : mMapPMhash2isAside) { + mapPMhash2sumAmpl.insert({entry.first, 0}); + } + int vertex_time; const auto& params = FT0DigParam::Instance(); + + static bool pmGroupsInitialized = false; + static std::vector> pmtChannelGroups; + if (!pmGroupsInitialized) { + std::unordered_map> tmpGroups; + for (int ch = 0; ch < o2::ft0::Constants::sNCHANNELS_PM; ++ch) { + tmpGroups[mChID2PMhash[static_cast(ch)]].push_back(ch); + } + + for (auto& [pmHash, chVec] : tmpGroups) { + std::sort(chVec.begin(), chVec.end()); + if (chVec.size() % 4 != 0) { + LOG(fatal) << "PM hash " << int(pmHash) << " has " << chVec.size() + << " channels in LUT, expected multiplicity of 4"; + } + for (size_t i = 0; i < chVec.size(); i += 4) { + std::array arr = {chVec[i + 0], chVec[i + 1], chVec[i + 2], chVec[i + 3]}; + pmtChannelGroups.push_back(arr); + } + } + pmGroupsInitialized = true; + } + int first = digitsCh.size(), nStored = 0; auto& particles = bc.hits; std::sort(std::begin(particles), std::end(particles)); auto channel_end = particles.begin(); std::vector channel_times; + std::vector baseAmp(params.mMCPs, 0.f); + std::vector finalAmp(params.mMCPs, 0.f); + std::vector chTime(params.mMCPs, -5000); + std::vector chChain(params.mMCPs, 0); + std::vector chValid(params.mMCPs, false); + + static const std::array, 4> localNeighbours = {{{{1, 2, 3}}, + {{0, 3, 2}}, + {{0, 3, 1}}, + {{1, 2, 0}}}}; + + // std::set disabledChannels = {40, 41, 42, 43, 88, 89, 90, 91, 56, 57, 58, 59, 60, 61, 62, 63, 72, 73, 74, 75, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 164, 165, 166, 167, 184, 185, 186, 187, 160, 161, 162, 163, 188, 189, 190, 191, 156, 157, 158, 159, 192, 193, 194, 195, 152, 153, 154, 155, 196, 197, 198, 199, 148, 149, 150, 151, 144, 145, 146, 147, 204, 205, 206, 207, 200, 201, 202, 203}; // przykładowe kanały for (Int_t ipmt = 0; ipmt < params.mMCPs; ++ipmt) { auto channel_begin = channel_end; channel_end = std::find_if(channel_begin, particles.end(), [ipmt](BCCache::particle const& p) { return p.hit_ch != ipmt; }); + if (channel_end - channel_begin < params.mAmp_trsh) { continue; } @@ -268,29 +442,105 @@ void Digitizer::storeBC(BCCache& bc, if (mCalibOffset) { miscalib = mCalibOffset->mTimeOffsets[ipmt]; } - int smeared_time = 1000. * (*cfd.particle - params.mCfdShift) * params.mChannelWidthInverse + miscalib + int(1000. * mIntRecord.getTimeOffsetWrtBC() * params.mChannelWidthInverse); + int smeared_time = 1000. * (*cfd.particle - params.mCfdShift) * params.mChannelWidthInverse + miscalib; bool is_time_in_signal_gate = (smeared_time > -params.mTime_trg_gate && smeared_time < params.mTime_trg_gate); float charge = measure_amplitude(channel_times) * params.mCharge2amp; - float amp = is_time_in_signal_gate ? params.mMV_2_Nchannels * charge : 0; - if (amp > 4095) { - amp = 4095; + float amp = is_time_in_signal_gate ? params.mMV_2_Nchannels * charge : 0.f; + if (amp > 4095.f) { + amp = 4095.f; } - - LOG(debug) << mEventID << " bc " << firstBCinDeque.bc << " orbit " << firstBCinDeque.orbit << ", ipmt " << ipmt << ", smeared_time " << smeared_time << " nStored " << nStored << " offset " << miscalib; + // if (!disabledChannels.count(ipmt)) { + // continue; + // } + + LOG(debug) << mEventID << " bc " << firstBCinDeque.bc << " orbit " << firstBCinDeque.orbit + << ", ipmt " << ipmt << ", smeared_time " << smeared_time + << " nStored " << nStored << " offset " << miscalib + << " base amp " << amp; if (is_time_in_signal_gate) { chain |= (1 << o2::ft0::ChannelData::EEventDataBit::kIsCFDinADCgate); chain |= (1 << o2::ft0::ChannelData::EEventDataBit::kIsEventInTVDC); } + + baseAmp[ipmt] = amp; + finalAmp[ipmt] = amp; + chTime[ipmt] = smeared_time; + chChain[ipmt] = chain; + chValid[ipmt] = true; + } + + for (const auto& channels : pmtChannelGroups) { + for (int localIdx = 0; localIdx < 4; ++localIdx) { + const int src = channels[localIdx]; + if (!chValid[src] || baseAmp[src] <= 0.f) { + continue; + } + + const int nb1 = channels[localNeighbours[localIdx][0]]; + const int nb2 = channels[localNeighbours[localIdx][1]]; + const int diag = channels[localNeighbours[localIdx][2]]; + + const float directXtalk = baseAmp[src] * params.Cross_Talk_Frac; + const float diagXtalk = baseAmp[src] * (params.Cross_Talk_Frac / 3.f); + + finalAmp[nb1] += directXtalk; + finalAmp[nb2] += directXtalk; + finalAmp[diag] += diagXtalk; + + if (!chValid[nb1] && directXtalk >= params.mAmpThresholdForCrossTalkDigit) { + chValid[nb1] = true; + chTime[nb1] = chTime[src]; + chChain[nb1] = chChain[src]; + } + + if (!chValid[nb2] && directXtalk >= params.mAmpThresholdForCrossTalkDigit) { + chValid[nb2] = true; + chTime[nb2] = chTime[src]; + chChain[nb2] = chChain[src]; + } + + if (!chValid[diag] && diagXtalk >= params.mAmpThresholdForCrossTalkDigit) { + chValid[diag] = true; + chTime[diag] = chTime[src]; + chChain[diag] = chChain[src]; + } + } + } + + for (Int_t ipmt = 0; ipmt < params.mMCPs; ++ipmt) { + if (!chValid[ipmt]) { + continue; + } + + float amp = finalAmp[ipmt]; + if (amp > 4095.f) { + amp = 4095.f; + } + const bool hasPrimarySignal = (baseAmp[ipmt] > 0.f); + const bool isCrossTalkOnly = (!hasPrimarySignal && amp > 0.f); + + if (isCrossTalkOnly && amp < params.mAmpThresholdForCrossTalkDigit) { + continue; + } + + const int smeared_time = chTime[ipmt]; + const int chain = chChain[ipmt]; + const bool is_time_in_signal_gate = (smeared_time > -params.mTime_trg_gate && smeared_time < params.mTime_trg_gate); + + if (is_time_in_signal_gate && ipmt < o2::ft0::Constants::sNCHANNELS_PM) { + mapPMhash2sumAmpl[mChID2PMhash[static_cast(ipmt)]] += static_cast(amp); + } + digitsCh.emplace_back(ipmt, smeared_time, int(amp), chain); nStored++; - // fill triggers - Bool_t is_A_side = (ipmt < 4 * mGeometry.NCellsA); if (!is_time_in_signal_gate) { continue; } + sum_ampl_ipmt[ipmt] += amp; + if (is_A_side) { n_hit_A++; summ_ampl_A += amp; @@ -301,17 +551,47 @@ void Digitizer::storeBC(BCCache& bc, mean_time_C += smeared_time; } } + + for (size_t i = 0; i < sum_ampl_ipmt.size(); i++) { + sum_ampl_ipmt[i] = sum_ampl_ipmt[i] >> 3; + if (i < 4 * mGeometry.NCellsA) { + sum_A_ampl += sum_ampl_ipmt[i]; + } else { + sum_C_ampl += sum_ampl_ipmt[i]; + } + } + + // Sum over PMs (using per-PM map) for debug/monitoring + int sum_PM_ampl_debug = 0; + int sum_PM_ampl_A_debug = 0; + int sum_PM_ampl_C_debug = 0; + for (const auto& entry : mapPMhash2sumAmpl) { + int pmAmpl = (entry.second >> 3); + sum_PM_ampl_debug += pmAmpl; + auto itSide = mMapPMhash2isAside.find(entry.first); + if (itSide != mMapPMhash2isAside.end()) { + if (itSide->second) { + sum_PM_ampl_A_debug += pmAmpl; + } else { + sum_PM_ampl_C_debug += pmAmpl; + } + } + } + LOG(debug) << "Sum PM amplitude (LUT-based): total=" << sum_PM_ampl_debug + << " A-side=" << sum_PM_ampl_A_debug + << " C-side=" << sum_PM_ampl_C_debug; + Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0; is_A = n_hit_A > 0; is_C = n_hit_C > 0; - is_Central = summ_ampl_A + summ_ampl_C >= params.mtrg_central_trh; - is_SemiCentral = summ_ampl_A + summ_ampl_C >= params.mtrg_semicentral_trh; + is_Central = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 * params.mtrg_central_trh; + is_SemiCentral = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 * params.mtrg_semicentral_trh && !is_Central; uint32_t amplA = is_A ? summ_ampl_A * 0.125 : -5000; // sum amplitude A side / 8 (hardware) uint32_t amplC = is_C ? summ_ampl_C * 0.125 : -5000; // sum amplitude C side / 8 (hardware) int timeA = is_A ? mean_time_A / n_hit_A : -5000; // average time A side int timeC = is_C ? mean_time_C / n_hit_C : -5000; // average time C side vertex_time = (timeC - timeA) * 0.5; - isVertex = is_A && is_C && (vertex_time > -params.mTime_trg_gate && vertex_time < params.mTime_trg_gate); + isVertex = is_A && is_C && (vertex_time > -params.mTime_trg_vertex_gate && vertex_time < params.mTime_trg_vertex_gate); LOG(debug) << " A " << is_A << " timeA " << timeA << " mean_time_A " << mean_time_A << " n_hit_A " << n_hit_A << " C " << is_C << " timeC " << timeC << " mean_time_C " << mean_time_C << " n_hit_C " << n_hit_C << " vertex_time " << vertex_time; Triggers triggers; bool isLaser = false; diff --git a/Detectors/FIT/FT0/simulation/src/digit2raw.cxx b/Detectors/FIT/FT0/simulation/src/digit2raw.cxx index 1c30a7627435f..b700f06e9985f 100644 --- a/Detectors/FIT/FT0/simulation/src/digit2raw.cxx +++ b/Detectors/FIT/FT0/simulation/src/digit2raw.cxx @@ -13,51 +13,12 @@ /// \author ruben.shahoyan@cern.ch afurs@cern.ch #include -#include #include -#include "CommonUtils/StringUtils.h" -#include "CommonUtils/ConfigurableParam.h" -#include "DetectorsRaw/HBFUtils.h" #include "FT0Raw/RawWriterFT0.h" -#include "DataFormatsParameters/GRPObject.h" /// MC->raw conversion for FT0 namespace bpo = boost::program_options; -struct Configuration { - Configuration(const bpo::variables_map& vm) - { - mInputFile = vm["input-file"].as(); - mOutputDir = vm["output-dir"].as(); - mVerbosity = vm["verbosity"].as(); - mFileFor = vm["file-for"].as(); - mRdhVersion = vm["rdh-version"].as(); - mEnablePadding = vm["enable-padding"].as(); - mNoEmptyHBF = vm["no-empty-hbf"].as(); - mFlpName = vm["flp-name"].as(); - mCcdbPath = vm["ccdb-path"].as(); - mChannelMapPath = vm["lut-path"].as(); - if (mRdhVersion < 7 && !mEnablePadding) { - mEnablePadding = true; - LOG(info) << "padding is always ON for RDH version " << mRdhVersion; - } - mDataFormat = mEnablePadding ? 0 : 2; - } - bool mNoEmptyHBF; - bool mEnablePadding; - int mVerbosity; - uint32_t mRdhVersion; - uint32_t mDataFormat; - std::string mInputFile; - std::string mOutputDir; - std::string mFileFor; - std::string mFlpName; - std::string mCcdbPath; - std::string mChannelMapPath; -}; - -template -void digit2raw(const Configuration& cfg); int main(int argc, char** argv) { @@ -69,21 +30,10 @@ int main(int argc, char** argv) bpo::positional_options_description opt_pos; try { - auto add_option = opt_general.add_options(); - add_option("help,h", "Print this help message"); - add_option("verbosity,v", bpo::value()->default_value(0), "verbosity level"); - add_option("input-file,i", bpo::value()->default_value("ft0digits.root"), "input FT0 digits file"); - add_option("flp-name", bpo::value()->default_value("alio2-cr1-flp200"), "single file per: all,flp,cru,link"); - add_option("file-for,f", bpo::value()->default_value("all"), "single file per: all,flp,cruendpoint,link"); - add_option("output-dir,o", bpo::value()->default_value("./"), "output directory for raw data"); - uint32_t defRDH = o2::raw::RDHUtils::getVersion(); - add_option("rdh-version,r", bpo::value()->default_value(defRDH), "RDH version to use"); - add_option("no-empty-hbf,e", bpo::value()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); - add_option("hbfutils-config,u", bpo::value()->default_value(std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)), "config file for HBFUtils (or none)"); - add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); - add_option("ccdb-path", bpo::value()->default_value(""), "CCDB url which contains LookupTable"); - add_option("lut-path", bpo::value()->default_value(""), "LookupTable path, e.g. FT0/LookupTable"); - add_option("enable-padding", bpo::value()->default_value(false)->implicit_value(true), "enable GBT word padding to 128 bits even for RDH V7"); + opt_general.add_options()("help,h", "Print this help message"); + // config with FT0 defaults + o2::fit::DigitToRawConfig::configureExecOptions(opt_general, "ft0digits.root", "alio2-cr1-flp200"); + // common part opt_all.add(opt_general).add(opt_hidden); bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); @@ -102,62 +52,12 @@ int main(int argc, char** argv) std::cerr << e.what() << ", application will now exit" << std::endl; exit(2); } - - std::string confDig = vm["hbfutils-config"].as(); - if (!confDig.empty() && confDig != "none") { - o2::conf::ConfigurableParam::updateFromFile(confDig, "HBFUtils"); - } - o2::conf::ConfigurableParam::updateFromString(vm["configKeyValues"].as()); - - const Configuration cfg(vm); + const o2::fit::DigitToRawConfig cfg(vm); if (!cfg.mEnablePadding) { - digit2raw(cfg); + o2::fit::DigitToRawDevice::digit2raw(cfg); } else { - digit2raw(cfg); + o2::fit::DigitToRawDevice::digit2raw(cfg); } o2::raw::HBFUtils::Instance().print(); - return 0; } - -template -void digit2raw(const Configuration& cfg) -{ - TStopwatch swTot; - swTot.Start(); - RawWriterType m2r; - m2r.setFileFor(cfg.mFileFor); - m2r.setFlpName(cfg.mFlpName); - m2r.setVerbosity(cfg.mVerbosity); - if (cfg.mCcdbPath != "") { - m2r.setCCDBurl(cfg.mCcdbPath); - } - if (cfg.mChannelMapPath != "") { - m2r.setLUTpath(cfg.mChannelMapPath); - } - auto& wr = m2r.getWriter(); - std::string inputGRP = o2::base::NameConf::getGRPFileName(); - const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); - wr.setContinuousReadout(grp->isDetContinuousReadOut(o2::detectors::DetID::FT0)); // must be set explicitly - const int superPageSizeInB = 1024 * 1024; - wr.setSuperPageSize(superPageSizeInB); - wr.useRDHVersion(cfg.mRdhVersion); - wr.setDontFillEmptyHBF(cfg.mNoEmptyHBF); - wr.useRDHDataFormat(cfg.mDataFormat); - if (!cfg.mEnablePadding) { // CRU page alignment padding is used only if no GBT word padding is used - wr.setAlignmentSize(16); // change to constexpr static field from class? - wr.setAlignmentPaddingFiller(0xff); - } - o2::raw::assertOutputDirectory(cfg.mOutputDir); - - std::string outDirName(cfg.mOutputDir); - if (outDirName.back() != '/') { - outDirName += '/'; - } - - m2r.convertDigitsToRaw(outDirName, cfg.mInputFile); - wr.writeConfFile(wr.getOrigin().str, "RAWDATA", o2::utils::Str::concat_string(outDirName, wr.getOrigin().str, "raw.cfg")); - // - swTot.Stop(); - swTot.Print(); -} diff --git a/Detectors/FIT/FT0/workflow/CMakeLists.txt b/Detectors/FIT/FT0/workflow/CMakeLists.txt index 2dbbbae41e261..123a29293e2fb 100644 --- a/Detectors/FIT/FT0/workflow/CMakeLists.txt +++ b/Detectors/FIT/FT0/workflow/CMakeLists.txt @@ -98,6 +98,11 @@ o2_add_executable(recpoints-reader-workflow COMPONENT_NAME ft0 PUBLIC_LINK_LIBRARIES O2::FT0Workflow) +o2_add_executable(recpoints-writer-workflow + SOURCES src/recpoints-writer-workflow.cxx + COMPONENT_NAME ft0 + PUBLIC_LINK_LIBRARIES O2::FT0Workflow) + o2_add_executable(integrate-cluster-workflow SOURCES src/cluster-integrator.cxx COMPONENT_NAME ft0 diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h index 4f8e8b5e9be63..d6009accfa45b 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace ft0 class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h index 8fd597af8629d..a1b3714fdbb26 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace ft0 class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataDecoderDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataDecoderDPLSpec.h index e039f18c47bc9..b78d0435d3636 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataDecoderDPLSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataDecoderDPLSpec.h @@ -110,7 +110,7 @@ framework::DataProcessorSpec getFT0DataDecoderDPLSpec(bool askSTFDist) std::vector outputSpec; outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); - std::vector inputSpec{{"STF", ConcreteDataTypeMatcher{"FT0", "RAWDATA"}, Lifetime::Optional}}; + std::vector inputSpec{{"STF", ConcreteDataTypeMatcher{"FT0", "RAWDATA"}, Lifetime::Timeframe}}; if (askSTFDist) { inputSpec.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); } diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h deleted file mode 100644 index 7b7e98d50368e..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataProcessDPLSpec.h - -#ifndef O2_FT0DATAPROCESSDPLSPEC_H -#define O2_FT0DATAPROCESSDPLSPEC_H - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -#include "FT0Raw/DigitBlockFT0.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" - -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -class FT0DataProcessDPLSpec : public Task -{ - public: - FT0DataProcessDPLSpec(bool dumpEventBlocks) : mDumpEventBlocks(dumpEventBlocks) {} - ~FT0DataProcessDPLSpec() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mDumpEventBlocks; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFT0; -}; - -framework::DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor); - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0DATAPROCESSDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h deleted file mode 100644 index f7db7f3a0e7cb..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataReaderDPLSpec.h - -#ifndef O2_FT0DATAREADERDPLSPEC_H -#define O2_FT0DATAREADERDPLSPEC_H -#include "DataFormatsFT0/LookUpTable.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "DPLUtils/DPLRawParser.h" -#include "Framework/InputRecordWalker.h" -#include -#include -#include -#include "CommonUtils/VerbosityConfig.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ -template -class FT0DataReaderDPLSpec : public Task -{ - public: - FT0DataReaderDPLSpec(const RawReader& rawReader) : mRawReader(rawReader) {} - FT0DataReaderDPLSpec() = default; - ~FT0DataReaderDPLSpec() override = default; - typedef RawReader RawReader_t; - void init(InitContext& ic) final { o2::ft0::SingleLUT::Instance().printFullMap(); } - void run(ProcessingContext& pc) final - { - // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" - // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow - { - static size_t contDeadBeef = 0; // number of times 0xDEADBEEF was seen continuously - std::vector dummy{InputSpec{"dummy", ConcreteDataMatcher{o2::header::gDataOriginFT0, o2::header::gDataDescriptionRawData, 0xDEADBEEF}}}; - for (const auto& ref : InputRecordWalker(pc.inputs(), dummy)) { - const auto dh = o2::framework::DataRefUtils::getHeader(ref); - auto payloadSize = DataRefUtils::getPayloadSize(ref); - if (payloadSize == 0) { - auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; - if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", - dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, - contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); - } - mRawReader.makeSnapshot(pc); // send empty output - return; - } - } - contDeadBeef = 0; // if good data, reset the counter - } - std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{o2::header::gDataOriginFT0, o2::header::gDataDescriptionRawData}, Lifetime::Timeframe}}; - DPLRawParser parser(pc.inputs(), filter); - std::size_t count = 0; - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - //Proccessing each page - count++; - auto rdhPtr = reinterpret_cast(it.raw()); - gsl::span payload(it.data(), it.size()); - mRawReader.process(payload, o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); - } - LOG(info) << "Pages: " << count; - mRawReader.accumulateDigits(); - mRawReader.makeSnapshot(pc); - mRawReader.clear(); - } - RawReader_t mRawReader; -}; - -template -framework::DataProcessorSpec getFT0DataReaderDPLSpec(const RawReader& rawReader, bool askSTFDist) -{ - LOG(info) << "DataProcessorSpec initDataProcSpec() for RawReaderFT0"; - std::vector outputSpec; - RawReader::prepareOutputSpec(outputSpec); - std::vector inputSpec{{"STF", ConcreteDataTypeMatcher{o2::header::gDataOriginFT0, "RAWDATA"}, Lifetime::Optional}}; - if (askSTFDist) { - inputSpec.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); - } - return DataProcessorSpec{ - "ft0-datareader-dpl", - inputSpec, - outputSpec, - adaptFromTask>(rawReader), - Options{}}; -} - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0DATAREADERDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h deleted file mode 100644 index a4988b2c18fc7..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_FIT_FT0WORKFLOW_H -#define O2_FIT_FT0WORKFLOW_H - -/// @file FT0Workflow.h - -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace ft0 -{ -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist); -} // namespace ft0 -} // namespace o2 -#endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h deleted file mode 100644 index f50b3d224b32c..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -//file RawReaderFT0.h class for RAW data reading -// -// Artur.Furs -// afurs@cern.ch -// -//Main purpuse is to decode FT0 data blocks and push them to DigitBlockFT0 for proccess -//TODO: prepare wrappers for containers with digits and combine classes below into one template class? -#ifndef ALICEO2_FIT_RAWREADERFT0_H_ -#define ALICEO2_FIT_RAWREADERFT0_H_ -#include -#include -#include -#include "FT0Raw/RawReaderFT0Base.h" - -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" - -#include "Framework/ProcessingContext.h" -#include "Framework/DataAllocator.h" -#include "Framework/OutputSpec.h" -#include - -namespace o2 -{ -namespace ft0 -{ -//Normal TCM mode -template -class RawReaderFT0 : public RawReaderFT0BaseNorm -{ - public: - RawReaderFT0(bool dumpData) : mDumpData(dumpData) {} - RawReaderFT0(const RawReaderFT0&) = default; - - RawReaderFT0() = default; - ~RawReaderFT0() = default; - static constexpr bool sUseTrgInput = useTrgInput; - void clear() - { - mVecDigits.clear(); - if constexpr (sUseTrgInput) { - mVecTriggerInput.clear(); - } - mVecChannelData.clear(); - } - void accumulateDigits() - { - if constexpr (sUseTrgInput) { - getDigits(mVecDigits, mVecChannelData, mVecTriggerInput); - } else { - getDigits(mVecDigits, mVecChannelData); - } - LOG(info) << "Number of Digits: " << mVecDigits.size(); - LOG(info) << "Number of ChannelData: " << mVecChannelData.size(); - if constexpr (sUseTrgInput) { - LOG(info) << "Number of TriggerInput: " << mVecTriggerInput.size(); - } - if (mDumpData) { - DigitBlockFT0::print(mVecDigits, mVecChannelData); - } - } - static void prepareOutputSpec(std::vector& outputSpec) - { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe); - if constexpr (sUseTrgInput) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe); - } - } - void makeSnapshot(o2::framework::ProcessingContext& pc) - { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe}, mVecChannelData); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecTriggerInput; - std::vector mVecChannelData; -}; - -//Extended TCM mode (additional raw data struct) -template -class RawReaderFT0ext : public RawReaderFT0BaseExt -{ - public: - RawReaderFT0ext(bool dumpData) : mDumpData(dumpData) {} - RawReaderFT0ext(const RawReaderFT0ext&) = default; - static constexpr bool sUseTrgInput = useTrgInput; - RawReaderFT0ext() = default; - ~RawReaderFT0ext() = default; - void clear() - { - mVecDigits.clear(); - mVecChannelData.clear(); - mVecTrgExt.clear(); - if constexpr (sUseTrgInput) { - mVecTriggerInput.clear(); - } - } - void accumulateDigits() - { - if constexpr (sUseTrgInput) { - getDigits(mVecDigits, mVecChannelData, mVecTrgExt, mVecTriggerInput); - } else { - getDigits(mVecDigits, mVecChannelData, mVecTrgExt); - } - LOG(info) << "Number of Digits: " << mVecDigits.size(); - LOG(info) << "Number of ChannelData: " << mVecChannelData.size(); - LOG(info) << "Number of TriggerExt: " << mVecTrgExt.size(); - if (mDumpData) { - DigitBlockFT0ext::print(mVecDigits, mVecChannelData, mVecTrgExt); - } - } - static void prepareOutputSpec(std::vector& outputSpec) - { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0, o2::framework::Lifetime::Timeframe); - if constexpr (sUseTrgInput) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe); - } - } - void makeSnapshot(o2::framework::ProcessingContext& pc) - { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe}, mVecChannelData); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0, o2::framework::Lifetime::Timeframe}, mVecTrgExt); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecChannelData; - std::vector mVecTrgExt; - std::vector mVecTriggerInput; -}; - -} // namespace ft0 -} // namespace o2 - -#endif \ No newline at end of file diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h index 0df02634472ee..6de23a1c66bfd 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace ft0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap = true); } // namespace ft0 } // namespace o2 #endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h index ee41569682a47..307b2109fe35f 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h @@ -34,7 +34,7 @@ class ReconstructionDPL : public Task static constexpr int NCHANNELS = o2::ft0::Geometry::Nchannels; public: - ReconstructionDPL(bool useMC, const std::string ccdbpath) : mUseMC(useMC), mCCDBpath(ccdbpath) {} + ReconstructionDPL(bool useMC, const std::string& ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool useDeadChannelMap) : mUseMC(useMC), mCCDBpath(ccdbpath), mUseTimeOffsetCalib(useTimeOffsetCalib), mUseSlewingCalib(useSlewingCalib), mUseDeadChannelMap(useDeadChannelMap) {} ~ReconstructionDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -44,6 +44,10 @@ class ReconstructionDPL : public Task private: bool mUseMC = false; bool mUpdateCCDB = true; + bool mUseTimeOffsetCalib = true; + bool mUseSlewingCalib = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; const std::string mCCDBpath = o2::base::NameConf::getCCDBServer(); std::vector mRecPoints; std::vector mRecChData; @@ -53,7 +57,7 @@ class ReconstructionDPL : public Task }; /// create a processor spec -framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch"); +framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch", bool useTimeOffsetCalib = true, bool useSlewingCalib = true, bool useDeadChannelMap = true); } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx index e4f11d3f5bd1e..09586d778ac15 100644 --- a/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx @@ -64,13 +64,13 @@ void DigitReader::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); LOG(debug) << "FT0DigitReader pushed " << channels.size() << " channels in " << digits.size() << " digits"; - pc.outputs().snapshot(Output{"FT0", "DIGITSBC", 0, Lifetime::Timeframe}, digits); - pc.outputs().snapshot(Output{"FT0", "DIGITSCH", 0, Lifetime::Timeframe}, channels); + pc.outputs().snapshot(Output{"FT0", "DIGITSBC", 0}, digits); + pc.outputs().snapshot(Output{"FT0", "DIGITSCH", 0}, channels); if (mUseMC) { - pc.outputs().snapshot(Output{"FT0", "DIGITSMCTR", 0, Lifetime::Timeframe}, labels); + pc.outputs().snapshot(Output{"FT0", "DIGITSMCTR", 0}, labels); } if (mUseTrgInput) { - pc.outputs().snapshot(Output{"FT0", "TRIGGERINPUT", 0, Lifetime::Timeframe}, trgInput); + pc.outputs().snapshot(Output{"FT0", "TRIGGERINPUT", 0}, trgInput); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx index 887d1f8c2d6dc..97ea337705fee 100644 --- a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx @@ -24,12 +24,13 @@ namespace o2 { namespace ft0 { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setSupportBCShifts(true); + mCTFCoder.setDictBinding("ctfdict_FT0"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -50,8 +51,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc); - auto buff = pc.inputs().get>("ctf"); + mCTFCoder.updateTimeDependentParams(pc, true); + auto buff = pc.inputs().get>("ctf_FT0"); auto& digits = pc.outputs().make>(OutputRef{"digits"}); auto& channels = pc.outputs().make>(OutputRef{"channels"}); @@ -72,7 +73,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "FT0", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -80,17 +81,19 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) OutputSpec{{"ctfrep"}, "FT0", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "FT0", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionary")); + inputs.emplace_back("ctf_FT0", "FT0", "CTFDATA", sspec, Lifetime::Timeframe); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_FT0", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "ft0-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx index 8f9e865ae8f99..7be6618a61103 100644 --- a/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace ft0 { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -48,14 +47,14 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - mCTFCoder.updateTimeDependentParams(pc); + mCTFCoder.updateTimeDependentParams(pc, true); auto digits = pc.inputs().get>("digits"); auto channels = pc.inputs().get>("channels"); if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{"FT0", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"FT0", "CTFDATA", 0}); auto iosize = mCTFCoder.encode(buffer, digits, channels); if (mSelIR) { mCTFCoder.getIRFramesSelector().clear(); @@ -70,12 +69,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "FT0", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("channels", "FT0", "DIGITSCH", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionary")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -83,12 +85,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) "ft0-entropy-encoder", inputs, Outputs{{"FT0", "CTFDATA", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataDecoderDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataDecoderDPLSpec.cxx index 945e7ad9d84d1..6d81740dd2173 100644 --- a/Detectors/FIT/FT0/workflow/src/FT0DataDecoderDPLSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/FT0DataDecoderDPLSpec.cxx @@ -28,12 +28,11 @@ namespace ft0 void FT0DataDecoderDPLSpec::run(ProcessingContext& pc) { auto t1 = std::chrono::high_resolution_clock::now(); - auto dummyOutput = [&pc, this]() { this->mVecDigits.resize(0); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe}, mVecDigits); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0}, mVecDigits); this->mVecChannelData.resize(0); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe}, mVecChannelData); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); }; // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" @@ -529,8 +528,8 @@ void FT0DataDecoderDPLSpec::run(ProcessingContext& pc) // Due to empty Digit container this dummy object will never participate in any further tasks. mVecChannelData.emplace_back(); } - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe}, mVecChannelData); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0}, mVecDigits); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); auto t2 = std::chrono::high_resolution_clock::now(); auto delay = std::chrono::duration_cast(t2 - t1); LOG(debug) << "Decoder delay: " << delay.count(); diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx deleted file mode 100644 index d7a7a689d402f..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataProcessDPLSpec.cxx - -#include "FT0Workflow/FT0DataProcessDPLSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ -using namespace std; -void FT0DataProcessDPLSpec::init(InitContext& ic) -{ -} - -void FT0DataProcessDPLSpec::run(ProcessingContext& pc) -{ - LOG(info) << "FT0DataProcessDPLSpec running..."; - auto vecDigits = pc.inputs().get>("digits"); - auto vecChannelData = pc.inputs().get>("digch"); - if (mDumpEventBlocks) { - DigitBlockFT0::print(vecDigits, vecChannelData); - } -} - -DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor) -{ - std::vector inputSpec; - inputSpec.emplace_back("digits", o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); - inputSpec.emplace_back("digch", o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); - LOG(info) << "DataProcessorSpec getFT0DataProcessDPLSpec"; - return DataProcessorSpec{ - "ft0-dataprocess-dpl-flp", - inputSpec, - Outputs{}, - AlgorithmSpec{adaptFromTask(dumpProcessor)}, - Options{}}; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx deleted file mode 100644 index caa642794b561..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataReaderDPLSpec.cxx - -#include "FT0Workflow/FT0DataReaderDPLSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx b/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx deleted file mode 100644 index 156feb7dd3e2f..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0Workflow.cxx - -#include "FT0Workflow/FT0Workflow.h" -#include "FT0Workflow/FT0DataProcessDPLSpec.h" -#include "FT0Workflow/FT0DataReaderDPLSpec.h" -#include "FT0Workflow/FT0DigitWriterSpec.h" -#include "FT0Workflow/RawReaderFT0.h" -namespace o2 -{ -namespace ft0 -{ - -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist) -{ - LOG(info) << "framework::WorkflowSpec getFT0Workflow"; - framework::WorkflowSpec specs; - if (isExtendedMode) { - specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0ext{dumpReader}, askSTFDist)); - } else { - specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0{dumpReader}, askSTFDist)); - } - if (useProcess) { - specs.emplace_back(o2::ft0::getFT0DataProcessDPLSpec(dumpProcessor)); - } - if (!disableRootOut) { - specs.emplace_back(o2::ft0::getFT0DigitWriterSpec(false, false)); - } - return specs; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx b/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx deleted file mode 100644 index b2ef17e540112..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "FT0Workflow/RawReaderFT0.h" -using namespace o2::ft0; diff --git a/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx b/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx index 69f21abc369c6..ba5ae4aa1356c 100644 --- a/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx @@ -49,8 +49,8 @@ void RecPointReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(debug) << "FT0 RecPointReader pushes " << mRecPoints->size() << " recpoints with " << mChannelData->size() << " channels at entry " << ent; - pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, *mRecPoints); - pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0, Lifetime::Timeframe}, *mChannelData); + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0}, *mRecPoints); + pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, *mChannelData); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/FIT/FT0/workflow/src/RecoQCworkflow.cxx b/Detectors/FIT/FT0/workflow/src/RecoQCworkflow.cxx index d5cb7cdf980e9..6993c46197dac 100644 --- a/Detectors/FIT/FT0/workflow/src/RecoQCworkflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/RecoQCworkflow.cxx @@ -132,7 +132,7 @@ DataProcessorSpec getRecoQCworkflow(GID::mask_t src) { auto dataRequest = std::make_shared(); LOG(info) << "@@ request primary vertex"; - dataRequest->requestPrimaryVertertices(false); + dataRequest->requestPrimaryVertices(false); dataRequest->requestFT0RecPoints(false); LOG(info) << "@@@ requested T0"; std::vector outputs; // empty diff --git a/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx index 8c5ab34166760..2231011febd7f 100644 --- a/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx @@ -22,13 +22,13 @@ namespace o2 namespace ft0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::ft0::getDigitReaderSpec(useMC)); } - specs.emplace_back(o2::ft0::getReconstructionSpec(useMC, ccdbpath)); + specs.emplace_back(o2::ft0::getReconstructionSpec(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::ft0::getRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx index 95ee4f3ff4b20..bc5217c8d7471 100644 --- a/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx @@ -44,6 +44,7 @@ void ReconstructionDPL::init(InitContext& ic) LOG(info) << "FT0 param mMinRMS: " << CalibParam::Instance().mMinRMS; LOG(info) << "FT0 param mMaxSigma: " << CalibParam::Instance().mMaxSigma; LOG(info) << "FT0 param mMaxDiffMean: " << CalibParam::Instance().mMaxDiffMean; + LOG(info) << "FT0 dead channel map will be applied " << mUseDeadChannelMap; } void ReconstructionDPL::run(ProcessingContext& pc) @@ -59,25 +60,29 @@ void ReconstructionDPL::run(ProcessingContext& pc) if (mUseMC) { LOG(info) << "Ignoring MC info"; } - if (mUpdateCCDB) { - auto timeCalibObject = pc.inputs().get("ft0_timespectra"); - mReco.SetTimeCalibObject(timeCalibObject.get()); + if (mUseTimeOffsetCalib) { + auto timeOffsetCalibObject = pc.inputs().get("ft0_timespectra"); + mReco.SetTimeCalibObject(timeOffsetCalibObject.get()); } - /* - auto calibslew = mCCDBManager.get>("FT0/SlewingCorr"); - LOG(debug) << " calibslew " << calibslew; - if (calibslew) { - mReco.SetSlew(calibslew); - LOG(info) << " calibslew set slew " << calibslew; + + if (mUseSlewingCalib) { + auto slewingCalibObject = pc.inputs().get("ft0_slewing_coef"); + mReco.SetSlewingCalibObject(slewingCalibObject.get()); + } + + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + LOG(debug) << "Applying dead channel map"; + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.SetDeadChannelMap(deadChannelMap.get()); } - */ + mRecPoints.reserve(digits.size()); mRecChData.reserve(channels.size()); mReco.processTF(digits, channels, mRecPoints, mRecChData); // do we ignore MC in this task? LOG(debug) << "FT0 reconstruction pushes " << mRecPoints.size() << " RecPoints"; - pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, mRecPoints); - pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0, Lifetime::Timeframe}, mRecChData); + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0}, mRecPoints); + pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, mRecChData); mTimer.Stop(); } @@ -85,7 +90,17 @@ void ReconstructionDPL::run(ProcessingContext& pc) void ReconstructionDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { if (matcher == ConcreteDataMatcher("FT0", "TimeSpectraInfo", 0)) { - mUpdateCCDB = false; + LOG(debug) << "New TimeSpectraInfo is uploaded"; + return; + } + if (matcher == ConcreteDataMatcher("FT0", "SlewingCoef", 0)) { + LOG(debug) << "New SlewingCoef is uploaded"; + mUseSlewingCalib = false; // upload only once, slewing should be stable during the run + return; + } + if (matcher == ConcreteDataMatcher("FT0", "DeadChannelMap", 0)) { + LOG(debug) << "New DeadChannelMap is uploaded"; + mUpdateDeadChannelMap = false; return; } } @@ -96,19 +111,33 @@ void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) +DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool useDeadChannelMap) { std::vector inputSpec; std::vector outputSpec; inputSpec.emplace_back("digits", o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); inputSpec.emplace_back("digch", o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); + if (useMC) { LOG(info) << "Currently Reconstruction does not consume and provide MC truth"; inputSpec.emplace_back("labels", o2::header::gDataOriginFT0, "DIGITSMCTR", 0, Lifetime::Timeframe); } - inputSpec.emplace_back("ft0_timespectra", "FT0", "TimeSpectraInfo", 0, - Lifetime::Condition, - ccdbParamSpec("FT0/Calib/TimeSpectraInfo")); + if (useTimeOffsetCalib) { + inputSpec.emplace_back("ft0_timespectra", "FT0", "TimeSpectraInfo", 0, + Lifetime::Condition, + ccdbParamSpec("FT0/Calib/TimeSpectraInfo", {}, 1)); + } + + if (useSlewingCalib) { + inputSpec.emplace_back("ft0_slewing_coef", "FT0", "SlewingCoef", 0, + Lifetime::Condition, + ccdbParamSpec("FT0/Calib/SlewingCoef")); + } + + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFT0, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/DeadChannelMap")); + } outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECCHDATA", 0, Lifetime::Timeframe); @@ -117,7 +146,7 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) "ft0-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC, ccdbpath)}, + AlgorithmSpec{adaptFromTask(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, useDeadChannelMap)}, Options{}}; } diff --git a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx index 6a98bbdafd53b..144d27abeda9c 100644 --- a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::ft0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::ft0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx index ee84c8b35a5f6..ab39068aedb38 100644 --- a/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx @@ -39,7 +39,10 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, - {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + {"disable-time-offset-calib", o2::framework::VariantType::Bool, false, {"disable timeoffset calibration"}}, + {"disable-slewing-calib", o2::framework::VariantType::Bool, false, {"disable slewing calibration"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"disable-dead-channel-map", VariantType::Bool, false, {"disable dead channel map"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -60,9 +63,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto ccdbpath = o2::base::NameConf::getCCDBServer(); auto disableRootInp = configcontext.options().get("disable-root-input"); auto disableRootOut = configcontext.options().get("disable-root-output"); + const auto useTimeOffsetCalib = !configcontext.options().get("disable-time-offset-calib"); + const auto useSlewingCalib = !configcontext.options().get("disable-slewing-calib"); + const auto useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); LOG(info) << "WorkflowSpec getRecoWorkflow useMC " << useMC << " CCDB " << ccdbpath; - auto wf = o2::ft0::getRecoWorkflow(useMC, ccdbpath, disableRootInp, disableRootOut); + auto wf = o2::ft0::getRecoWorkflow(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/FIT/FT0/workflow/src/recpoints-reader-workflow.cxx b/Detectors/FIT/FT0/workflow/src/recpoints-reader-workflow.cxx index 376a8385941ce..b1d824e10687e 100644 --- a/Detectors/FIT/FT0/workflow/src/recpoints-reader-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/recpoints-reader-workflow.cxx @@ -9,29 +9,35 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file recpoints-reader-workflow.cxx -/// \brief Implementation of FT0 digits reader +/// \file recpoints-reader-workflow.cxx +/// \brief FT0 RecPoints reader workflow /// -/// \author ruben.shahoyan@cern.ch +/// \author ruben.shahoyan@cern.ch, Andreas Molander andreas.molander@cern.ch -#include "Framework/CallbackService.h" -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/Task.h" -#include "FT0Workflow/RecPointReaderSpec.h" #include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" + +#include "FT0Workflow/RecPointReaderSpec.h" + +#include using namespace o2::framework; +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + // we need to add workflow options before including Framework/runDataProcessing void customize(std::vector& workflowOptions) { - // option allowing to set parameters - - std::vector options{ - {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}}; - std::string keyvaluehelp("Semicolon separated key=value strings"); - options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); + std::vector options{ + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -39,9 +45,14 @@ void customize(std::vector& workflowOptions) WorkflowSpec defineDataProcessing(const ConfigContext& ctx) { - WorkflowSpec specs; o2::conf::ConfigurableParam::updateFromString(ctx.options().get("configKeyValues")); - DataProcessorSpec producer = o2::ft0::getRecPointReaderSpec(ctx.options().get("disable-mc")); + bool disableMC = ctx.options().get("disable-mc"); + + WorkflowSpec specs; + DataProcessorSpec producer = o2::ft0::getRecPointReaderSpec(!disableMC); specs.push_back(producer); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(ctx, specs); return specs; } diff --git a/Detectors/FIT/FT0/workflow/src/recpoints-writer-workflow.cxx b/Detectors/FIT/FT0/workflow/src/recpoints-writer-workflow.cxx new file mode 100644 index 0000000000000..c8baef76b5ccd --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/recpoints-writer-workflow.cxx @@ -0,0 +1,47 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file recpoints-writer-workflow.cxx +/// \brief FT0 RecPoints writer workflow +/// +/// \author Andreas Molander andreas.molander@cern.ch + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" + +#include "FT0Workflow/RecPointWriterSpec.h" + +#include + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + o2::conf::ConfigurableParam::updateFromString(ctx.options().get("configKeyValues")); + bool disableMC = ctx.options().get("disable-mc"); + + WorkflowSpec specs; + DataProcessorSpec producer = o2::ft0::getRecPointWriterSpec(!disableMC); + specs.push_back(producer); + return specs; +} diff --git a/Detectors/FIT/FV0/base/README.md b/Detectors/FIT/FV0/base/README.md new file mode 100644 index 0000000000000..1fba33c47b690 --- /dev/null +++ b/Detectors/FIT/FV0/base/README.md @@ -0,0 +1,17 @@ + + +# FV0 Base + +## Geometry + +The `o2::fv0::Geometry` class represents the geometry of the FV0 detector as used in simulation. It also provides utility methods for retrieving the center locations of the detector cells. See the below example for how to use the `Geometry` class to query the FV0 cell locations. Note that these are the ideal locations, and that any misalignment is not considered. + +```cpp +o2::fv0::Geometry* fv0Geometry = o2::fv0::Geometry::instance(o2::fv0::Geometry::eUninitialized); +o2::fv0::Point3Dsimple cellPosition = fv0Geometry->getReadoutCenter(chId); +float x = cellPosition.x; +float y = cellPosition.y; +float z = cellPosition.z; +``` diff --git a/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h b/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h index 3b50be7441ec2..ec87c07c57c45 100644 --- a/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h +++ b/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h @@ -133,6 +133,16 @@ class Geometry return o2::base::GeometryManager::getPNEntry(getDetID(), index); } + static std::string getDetectorRightSymName() + { + return sDetectorRightName + "_0"; + } + + static std::string getDetectorLeftSymName() + { + return sDetectorLeftName + "_1"; + } + /// Get the density of the PMTs. static constexpr float getPmtDensity() { @@ -143,6 +153,8 @@ class Geometry explicit Geometry(EGeoType initType); inline static const std::string sDetectorName = "FV0"; + inline static const std::string sDetectorRightName = sDetectorName + "RIGHT"; + inline static const std::string sDetectorLeftName = sDetectorName + "LEFT"; // General geometry constants static constexpr float sEpsilon = 0.01; ///< Used to make one spatial dimension infinitesimally larger than other diff --git a/Detectors/FIT/FV0/calibration/macros/readChannelTimeOffsetFV0CalibObjectFromCCDB.C b/Detectors/FIT/FV0/calibration/macros/readChannelTimeOffsetFV0CalibObjectFromCCDB.C index 06b86e3c5015d..3f42c0219b101 100644 --- a/Detectors/FIT/FV0/calibration/macros/readChannelTimeOffsetFV0CalibObjectFromCCDB.C +++ b/Detectors/FIT/FV0/calibration/macros/readChannelTimeOffsetFV0CalibObjectFromCCDB.C @@ -22,8 +22,8 @@ int readChannelTimeOffsetFV0CalibObjectFromCCDB(const std::string url = "http:// { o2::ccdb::CcdbApi api; api.init(url); - map metadata; - map headers; + std::map metadata; + std::map headers; auto retrieved = api.retrieveFromTFileAny("FV0/Calib/ChannelTimeOffset", metadata, -1, &headers); std::cout << "--- HEADERS ---" << std::endl; diff --git a/Detectors/FIT/FV0/dcsmonitoring/workflow/FV0DCSConfigProcessorSpec.h b/Detectors/FIT/FV0/dcsmonitoring/workflow/FV0DCSConfigProcessorSpec.h index f546cb6088041..7c5a888312713 100644 --- a/Detectors/FIT/FV0/dcsmonitoring/workflow/FV0DCSConfigProcessorSpec.h +++ b/Detectors/FIT/FV0/dcsmonitoring/workflow/FV0DCSConfigProcessorSpec.h @@ -40,12 +40,14 @@ DataProcessorSpec getFV0DCSConfigProcessorSpec() return DataProcessorSpec{ "fv0-dcs-config-processor", - Inputs{{"inputConfig", o2::header::gDataOriginFV0, "DCS_CONFIG_FILE", Lifetime::Timeframe}, - {"inputConfigFileName", o2::header::gDataOriginFV0, "DCS_CONFIG_NAME", Lifetime::Timeframe}}, + Inputs{{"inputConfig", o2::header::gDataOriginFV0, "DCS_CONFIG_FILE", Lifetime::Sporadic}, + {"inputConfigFileName", o2::header::gDataOriginFV0, "DCS_CONFIG_NAME", Lifetime::Sporadic}}, outputs, AlgorithmSpec{adaptFromTask("FV0", ddDChM)}, Options{{"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}, - {"filename-dchm", VariantType::String, "FV0-deadchannels.txt", {"Dead channel map file name"}}}}; + {"filename-dchm", VariantType::String, "FV0-deadchannels.txt", {"Dead channel map file name"}}, + {"valid-days-dchm", VariantType::UInt32, 180u, {"Dead channel map validity in days"}}, + {"no-validate", VariantType::Bool, false, {"Don't validate the CCDB uploads"}}}}; } } // namespace framework diff --git a/Detectors/FIT/FV0/macros/FV0Misaligner.C b/Detectors/FIT/FV0/macros/FV0Misaligner.C index 500bdaf565965..61be50b48dede 100644 --- a/Detectors/FIT/FV0/macros/FV0Misaligner.C +++ b/Detectors/FIT/FV0/macros/FV0Misaligner.C @@ -1,13 +1,32 @@ +// Copyright 2021-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FV0Misaligner.C +/// \brief ROOT macro for creating an FV0 geometry alignment object. The alignment object will align both +/// detector halves in the same way. Based on ITSMisaligner.C +/// +/// \author Andreas Molander andreas.molander@cern.ch, Alla Maevskaya + #if !defined(__CLING__) || defined(__ROOTCLING__) + +#include "CCDB/CcdbApi.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "DetectorsCommonDataFormats/AlignParam.h" -#include "DetectorsBase/GeometryManager.h" -#include "CCDB/CcdbApi.h" -#include +#include "FV0Base/Geometry.h" + #include #include #include + #endif using AlgPar = std::array; @@ -20,16 +39,14 @@ void FV0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" const std::string& fileName = "FV0Alignment.root") { std::vector params; - o2::base::GeometryManager::loadGeometry("", false); AlgPar pars; bool glo = true; o2::detectors::DetID detFV0("FV0"); - // FV0 detector - for (int ihalf = 1; ihalf < 3; ihalf++) { - std::string symName = Form("FV0half_%i", ihalf); - pars = generateMisalignment(x, y, z, psi, theta, phi); + pars = generateMisalignment(x, y, z, psi, theta, phi); + + for (auto& symName : {o2::fv0::Geometry::getDetectorRightSymName(), o2::fv0::Geometry::getDetectorLeftSymName()}) { params.emplace_back(symName.c_str(), -1, pars[0], pars[1], pars[2], pars[3], pars[4], pars[5], glo); } @@ -37,7 +54,7 @@ void FV0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" std::string path = objectPath.empty() ? o2::base::DetectorNameConf::getAlignmentPath(detFV0) : objectPath; LOGP(info, "Storing alignment object on {}/{}", ccdbHost, path); o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdbHost.c_str()); // or http://localhost:8080 for a local installation // store abitrary user object in strongly typed manner api.storeAsTFileAny(¶ms, path, metadata, tmin, tmax); @@ -50,14 +67,15 @@ void FV0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" algFile.Close(); } } + AlgPar generateMisalignment(double x, double y, double z, double psi, double theta, double phi) { AlgPar pars; - pars[0] = gRandom->Gaus(0, x); - pars[1] = gRandom->Gaus(0, y); - pars[2] = gRandom->Gaus(0, z); - pars[3] = gRandom->Gaus(0, psi); - pars[4] = gRandom->Gaus(0, theta); - pars[5] = gRandom->Gaus(0, phi); + pars[0] = x; + pars[1] = y; + pars[2] = z; + pars[3] = psi; + pars[4] = theta; + pars[5] = phi; return std::move(pars); } diff --git a/Detectors/FIT/FV0/raw/include/FV0Raw/DigitBlockFV0.h b/Detectors/FIT/FV0/raw/include/FV0Raw/DigitBlockFV0.h index 081ebac1c9a40..bfd95d5ff5c29 100644 --- a/Detectors/FIT/FV0/raw/include/FV0Raw/DigitBlockFV0.h +++ b/Detectors/FIT/FV0/raw/include/FV0Raw/DigitBlockFV0.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -//file DigitBlockFV0.h class for proccessing RAW data into Digits +// file DigitBlockFV0.h class for proccessing RAW data into Digits // // Artur.Furs // afurs@cern.ch @@ -25,10 +25,10 @@ namespace o2 { namespace fv0 { -//Normal data taking mode -using DigitBlockFV0 = DigitBlockFIT; -//TCM extended data taking mode -using DigitBlockFV0ext = DigitBlockFIText; +// Normal data taking mode +using DigitBlockFV0 = o2::fit::DigitBlockFIT; +// TCM extended data taking mode +using DigitBlockFV0ext = o2::fit::DigitBlockFIText; } // namespace fv0 } // namespace o2 #endif diff --git a/Detectors/FIT/FV0/raw/src/DataBlockFV0.cxx b/Detectors/FIT/FV0/raw/src/DataBlockFV0.cxx index 4f53af0c80f79..37027f629550e 100644 --- a/Detectors/FIT/FV0/raw/src/DataBlockFV0.cxx +++ b/Detectors/FIT/FV0/raw/src/DataBlockFV0.cxx @@ -10,3 +10,7 @@ // or submit itself to any jurisdiction. #include "FV0Raw/DataBlockFV0.h" + +template class o2::fit::DataBlockPM, o2::fv0::RawHeaderPM, o2::fv0::RawDataPM>; +template class o2::fit::DataBlockTCM, o2::fv0::RawHeaderTCM, o2::fv0::RawDataTCM>; +template class o2::fit::DataBlockTCMext, o2::fv0::RawHeaderTCMext, o2::fv0::RawDataTCM, o2::fv0::RawDataTCMext>; diff --git a/Detectors/FIT/FV0/raw/src/DigitBlockFV0.cxx b/Detectors/FIT/FV0/raw/src/DigitBlockFV0.cxx index 8da75f3bdbcff..367c57686ab95 100644 --- a/Detectors/FIT/FV0/raw/src/DigitBlockFV0.cxx +++ b/Detectors/FIT/FV0/raw/src/DigitBlockFV0.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FV0Raw/DigitBlockFV0.h" + +template class o2::fit::DigitBlockFIT; +template class o2::fit::DigitBlockFIText; \ No newline at end of file diff --git a/Detectors/FIT/FV0/raw/src/RawReaderFV0Base.cxx b/Detectors/FIT/FV0/raw/src/RawReaderFV0Base.cxx index 7bba6ea3937c9..bbf2a780de0a1 100644 --- a/Detectors/FIT/FV0/raw/src/RawReaderFV0Base.cxx +++ b/Detectors/FIT/FV0/raw/src/RawReaderFV0Base.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FV0Raw/RawReaderFV0Base.h" + +template class o2::fit::RawReaderBaseFIT; +template class o2::fit::RawReaderBaseFIT; diff --git a/Detectors/FIT/FV0/raw/src/RawWriterFV0.cxx b/Detectors/FIT/FV0/raw/src/RawWriterFV0.cxx index 64d75091cbe80..d52e1117460a8 100644 --- a/Detectors/FIT/FV0/raw/src/RawWriterFV0.cxx +++ b/Detectors/FIT/FV0/raw/src/RawWriterFV0.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FV0Raw/RawWriterFV0.h" + +template class o2::fit::RawWriterFIT; +template class o2::fit::RawWriterFIT; diff --git a/Detectors/FIT/FV0/reconstruction/README.md b/Detectors/FIT/FV0/reconstruction/README.md new file mode 100644 index 0000000000000..46de9800fda92 --- /dev/null +++ b/Detectors/FIT/FV0/reconstruction/README.md @@ -0,0 +1,36 @@ + + +# FV0 reconstruction + +Please note that this readme shows how the reconstruction is done _at the moment_. The current reconstruction is not necessarily done the way it _should be_. The algorithm is under review and subject to change. + +The FV0 reconstruction workflow is + +- [O2/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx](../workflow/src/fv0-reco-workflow.cxx) + +and the actual reconstruction happens in + +- [src/BaseRecoTask.cxx](src/BaseRecoTask.cxx) ([include/FV0Reconstruction/BaseRecoTask.h](include/BaseRecoTask.h)) + +## Channel data reconstruction + +Currently, there's no channel data filtering applied in the FV0 reconstruction. All digit channel data are propagated to RecPoints. The following conversions are made: + +- Channel ID (`uint8_t`) -> Channel ID (`int`) +- CFD time (TDC units, `int16_t`) -> Time in ns (`double`) + - Furthermore, if available and enabled, a time offset correction is applied. For FV0 it is not available nor enabled at the moment. +- Amplitude (ADC channels, `int16_t`) -> Ampltidue (ADC channels, `double`) +- Channel bits (`uint8_t`) -> Channel bits (`int`) + +## RecPoint reconstruction + +The reconstructed RecPoints contain: + +- BC information (`o2::InteractionRecord`) +- Channel data +- Three different times: + - "TimeFirst": time of first signal (w/ charge > `FV0DigParam::chargeThrForMeanTime`) + - "TimeGlobalMean": average of all signals passing some filtering (including CFD in gate) + - "TimeSelectedMean": average of all signals passing a bit stricter filtering (includling CFD in gate) diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h index 12d89b82a13cc..c5cb5b0da6d05 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h @@ -18,6 +18,7 @@ #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/RecPoints.h" #include "DataFormatsFV0/FV0ChannelTimeCalibrationObject.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include namespace o2 @@ -33,14 +34,15 @@ class BaseRecoTask ~BaseRecoTask() = default; o2::fv0::RecPoints process(o2::fv0::Digit const& bcd, gsl::span inChData, - gsl::span outChData); + std::vector& outChData); void FinishTask(); void SetChannelOffset(o2::fv0::FV0ChannelTimeCalibrationObject const* caliboffsets) { mCalibOffset = caliboffsets; }; + void SetDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) { mDeadChannelMap = deadChannelMap; } int getOffset(int channel); private: o2::fv0::FV0ChannelTimeCalibrationObject const* mCalibOffset = nullptr; - + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; ClassDefNV(BaseRecoTask, 3); }; } // namespace fv0 diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h index f2b9c8e02c7f1..082fbd93a705a 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h @@ -30,10 +30,10 @@ namespace o2 namespace fv0 { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FV0) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FV0, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF @@ -68,15 +68,15 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& dig using MD = o2::ctf::Metadata::OptStore; // what to do which each field: see o2::ctd::Metadata explanation constexpr MD optField[CTF::getNBlocks()] = { - MD::EENCODE, // BLC_bcInc - MD::EENCODE, // BLC_orbitInc - MD::EENCODE, // BLC_nChan - MD::EENCODE, // BLC_idChan - MD::EENCODE, // BLC_cfdTime - MD::EENCODE, // BLC_qtcAmpl + MD::EENCODE_OR_PACK, // BLC_bcInc + MD::EENCODE_OR_PACK, // BLC_orbitInc + MD::EENCODE_OR_PACK, // BLC_nChan + MD::EENCODE_OR_PACK, // BLC_idChan + MD::EENCODE_OR_PACK, // BLC_cfdTime + MD::EENCODE_OR_PACK, // BLC_qtcAmpl // extra slot was added in the end - MD::EENCODE, // BLC_trigger - MD::EENCODE // BLC_qtcChain + MD::EENCODE_OR_PACK, // BLC_trigger + MD::EENCODE_OR_PACK // BLC_qtcChain }; CompressedDigits cd; if (mExtHeader.isValidDictTimeStamp()) { @@ -97,11 +97,10 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& dig ec->setHeader(cd.header); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODEFV0(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODEFV0(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODEFV0(cd.bcInc, CTF::BLC_bcInc, 0); iosize += ENCODEFV0(cd.orbitInc, CTF::BLC_orbitInc, 0); @@ -129,7 +128,7 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VDIG& digitVec, VCHAN& checkDictVersion(hd); ec.print(getPrefix(), mVerbosity); o2::ctf::CTFIOSize iosize; -#define DECODEFV0(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODEFV0(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODEFV0(cd.bcInc, CTF::BLC_bcInc); iosize += DECODEFV0(cd.orbitInc, CTF::BLC_orbitInc); @@ -169,7 +168,9 @@ void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& cha uint32_t firstEntry = 0, clCount = 0, chipCount = 0; o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); - + if (mBCShift && ir.toLong() >= mBCShift) { + ir -= mBCShift; + } for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { // restore ROFRecord if (cd.orbitInc[idig]) { // non-0 increment => new orbit diff --git a/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx b/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx index 8a217232592df..8032220f8996d 100644 --- a/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx +++ b/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx @@ -27,7 +27,7 @@ using RP = o2::fv0::RecPoints; RP BaseRecoTask::process(o2::fv0::Digit const& bcd, gsl::span inChData, - gsl::span outChData) + std::vector& outChData) { LOG(debug) << "Running reconstruction on new event"; @@ -44,22 +44,27 @@ RP BaseRecoTask::process(o2::fv0::Digit const& bcd, int nch = inChData.size(); for (int ich = 0; ich < nch; ich++) { LOG(debug) << " channel " << ich << " / " << nch; + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(inChData[ich].ChId)) { + LOG(debug) << "Channel " << ich << " is dead - discarding data"; + continue; + } int offsetChannel = getOffset(int(inChData[ich].ChId)); - outChData[ich] = o2::fv0::ChannelDataFloat{inChData[ich].ChId, - (inChData[ich].CFDTime - offsetChannel) * DigitizationConstant::TIME_PER_TDCCHANNEL, - (float)inChData[ich].QTCAmpl, - inChData[ich].ChainQTC}; + outChData.emplace_back(o2::fv0::ChannelDataFloat{inChData[ich].ChId, + (inChData[ich].CFDTime - offsetChannel) * DigitizationConstant::TIME_PER_TDCCHANNEL, + (float)inChData[ich].QTCAmpl, + inChData[ich].ChainQTC}); + const auto& currentOutCh = outChData.back(); // Conditions for reconstructing collision time (3 variants: first, average-relaxed and average-tight) - if (outChData[ich].charge > FV0DigParam::Instance().chargeThrForMeanTime) { - sideAtimeFirst = std::min(static_cast(sideAtimeFirst), outChData[ich].time); + if (currentOutCh.charge > FV0DigParam::Instance().chargeThrForMeanTime) { + sideAtimeFirst = std::min(static_cast(sideAtimeFirst), currentOutCh.time); if (inChData[ich].areAllFlagsGood()) { - if (std::abs(outChData[ich].time) < FV0DigParam::Instance().mTimeThresholdForReco) { - sideAtimeAvg += outChData[ich].time; + if (std::abs(currentOutCh.time) < FV0DigParam::Instance().mTimeThresholdForReco) { + sideAtimeAvg += currentOutCh.time; ndigitsA++; } - if (outChData[ich].charge > FV0DigParam::Instance().mAmpThresholdForReco && std::abs(outChData[ich].time) < FV0DigParam::Instance().mTimeThresholdForReco) { - sideAtimeAvgSelected += outChData[ich].time; + if (currentOutCh.charge > FV0DigParam::Instance().mAmpThresholdForReco && std::abs(currentOutCh.time) < FV0DigParam::Instance().mTimeThresholdForReco) { + sideAtimeAvgSelected += currentOutCh.time; ndigitsASelected++; } } diff --git a/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx b/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx index e718dbd3c07f0..f2e4f59e10c97 100644 --- a/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx +++ b/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx @@ -53,7 +53,7 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); CompressedDigits cd; // just to get member types -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(cd.bcInc, CTF::BLC_bcInc); MAKECODER(cd.orbitInc, CTF::BLC_orbitInc); @@ -73,22 +73,17 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa size_t CTFCoder::estimateCompressedSize(const CompressedDigits& cd) { size_t sz = 0; - // clang-format off // RS FIXME this is very crude estimate, instead, an empirical values should be used -#define VTP(vec) typename std::remove_reference::type::value_type -#define ESTSIZE(vec, slot) mCoders[int(slot)] ? \ - rans::calculateMaxBufferSize(vec.size(), reinterpret_cast*>(mCoders[int(slot)].get())->getAlphabetRangeBits(), sizeof(VTP(vec)) ) : vec.size()*sizeof(VTP(vec)) - sz += ESTSIZE(cd.trigger, CTF::BLC_trigger); - sz += ESTSIZE(cd.bcInc, CTF::BLC_bcInc); - sz += ESTSIZE(cd.orbitInc, CTF::BLC_orbitInc); - sz += ESTSIZE(cd.nChan, CTF::BLC_nChan); + sz += estimateBufferSize(static_cast(CTF::BLC_trigger), cd.trigger); + sz += estimateBufferSize(static_cast(CTF::BLC_bcInc), cd.bcInc); + sz += estimateBufferSize(static_cast(CTF::BLC_orbitInc), cd.orbitInc); + sz += estimateBufferSize(static_cast(CTF::BLC_nChan), cd.nChan); - sz += ESTSIZE(cd.idChan, CTF::BLC_idChan); - sz += ESTSIZE(cd.qtcChain, CTF::BLC_qtcChain); - sz += ESTSIZE(cd.cfdTime, CTF::BLC_cfdTime); - sz += ESTSIZE(cd.qtcAmpl, CTF::BLC_qtcAmpl); - // clang-format on + sz += estimateBufferSize(static_cast(CTF::BLC_idChan), cd.idChan); + sz += estimateBufferSize(static_cast(CTF::BLC_qtcChain), cd.qtcChain); + sz += estimateBufferSize(static_cast(CTF::BLC_cfdTime), cd.cfdTime); + sz += estimateBufferSize(static_cast(CTF::BLC_qtcAmpl), cd.qtcAmpl); - LOG(info) << "Estimated output size is " << sz << " bytes"; + LOG(debug) << "Estimated output size is " << sz << " bytes"; return sz; -} +}; \ No newline at end of file diff --git a/Detectors/FIT/FV0/reconstruction/src/FV0ReconstructionLinkDef.h b/Detectors/FIT/FV0/reconstruction/src/FV0ReconstructionLinkDef.h index c85dd2d378ccb..f4551100c8cc5 100644 --- a/Detectors/FIT/FV0/reconstruction/src/FV0ReconstructionLinkDef.h +++ b/Detectors/FIT/FV0/reconstruction/src/FV0ReconstructionLinkDef.h @@ -15,4 +15,6 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::fv0::BaseRecoTask + ; + #endif diff --git a/Detectors/FIT/FV0/simulation/CMakeLists.txt b/Detectors/FIT/FV0/simulation/CMakeLists.txt index d14b72450ff8f..6c696c52523ae 100644 --- a/Detectors/FIT/FV0/simulation/CMakeLists.txt +++ b/Detectors/FIT/FV0/simulation/CMakeLists.txt @@ -29,12 +29,8 @@ o2_target_root_dictionary(FV0Simulation o2_add_executable(digi2raw COMPONENT_NAME fv0 SOURCES src/digit2raw.cxx - PUBLIC_LINK_LIBRARIES O2::FV0Simulation - O2::DetectorsRaw - O2::DetectorsCommonDataFormats - O2::CommonUtils - Boost::program_options - O2::FV0Raw) + PUBLIC_LINK_LIBRARIES O2::FV0Raw + Boost::program_options) o2_data_file(COPY data DESTINATION Detectors/FV0/simulation) diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h index 3d36f255d5094..b97893822c9d8 100644 --- a/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h @@ -14,6 +14,7 @@ #include "CommonDataFormat/InteractionRecord.h" #include "DataFormatsFV0/Digit.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/MCLabel.h" #include "FV0Simulation/Detector.h" @@ -51,6 +52,7 @@ class Digitizer void setEventId(Int_t id) { mEventId = id; } void setSrcId(Int_t id) { mSrcId = id; } void setInteractionRecord(const InteractionTimeRecord& ir) { mIntRecord = ir; } + void setDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) { mDeadChannelMap = deadChannelMap; }; void process(const std::vector& hits, std::vector& digitsBC, std::vector& digitsCh, std::vector& digitsTrig, @@ -90,8 +92,16 @@ class Digitizer private: static constexpr int BCCacheMin = 0, BCCacheMax = 7, NBC2Cache = 1 + BCCacheMax - BCCacheMin; - void createPulse(float mipFraction, int parID, const double hitTime, std::array const& cachedIR, - int nCachedIR, const int detID); + /// Create signal pulse based on MC hit + /// \param mipFraction Fraction of the MIP energy deposited in the cell + /// \param parID Particle ID + /// \param hitTime Time of the hit + /// \param hitR Length to IP from the position of the hit + /// \param cachedIR Cached interaction records + /// \param nCachedIR Number of cached interaction records + /// \param detID Detector cell ID + void createPulse(float mipFraction, int parID, const double hitTime, const float hitR, + std::array const& cachedIR, int nCachedIR, const int detID); long mTimeStamp; // TF (run) timestamp InteractionTimeRecord mIntRecord; // Interaction record (orbit, bc) -> InteractionTimeRecord @@ -124,6 +134,8 @@ class Digitizer BCCache mLastBCCache; // buffer for the last BC std::array mCfdStartIndex; // start indices for the CFD detector + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; + /// Internal helper methods related to conversion of energy-deposition into el. signal Int_t SimulateLightYield(Int_t pmt, Int_t nPhot) const; Float_t SimulateTimeCfd(int& startIndex, const ChannelDigitF& pulseLast, const ChannelDigitF& pulse) const; diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h index 347f2e7bb707a..6462323a279b7 100644 --- a/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file FV0DigParam.h +/// \file FV0DigParam.h /// \brief Configurable digitization parameters #ifndef ALICEO2_FV0_DIG_PARAM @@ -22,9 +22,11 @@ namespace o2::fv0 { // parameters of FV0 digitization / transport simulation struct FV0DigParam : o2::conf::ConfigurableParamHelper { - float photoCathodeEfficiency = 0.23; // quantum efficiency = nOfPhotoE_emitted_by_photocathode / nIncidentPhotons - float lightYield = 0.01; // light collection efficiency to be tuned using collision data [1%] - float adcChannelsPerMip = 16; // Default: 16 for pp and 8 for PbPb + float hitTimeOffset = 0.0; ///< Hit time offset [ns] + + float photoCathodeEfficiency = 0.23; // quantum efficiency = nOfPhotoE_emitted_by_photocathode / nIncidentPhotons + float lightYield = 0.01; // light collection efficiency to be tuned using collision data [1%] + float adcChannelsPerMip = 16; // Default: 16 for pp and 8 for PbPb float getChannelsPerMilivolt() const { return adcChannelsPerMip / 7.5; } // Non-trivial conversion depending on the pulseshape: amplitude to charge float chargeThrForMeanTime = 5; // Charge threshold, only above which the time is taken into account in calculating the mean time of all qualifying channels @@ -55,19 +57,22 @@ struct FV0DigParam : o2::conf::ConfigurableParamHelper { bool isIntegrateFull = false; // Full charge integration widow in 25 ns float cfdCheckWindow = 2.5; // time window for the cfd in ns to trigger the charge integration int avgNumberPhElectronPerMip = 201; // avg number of photo-electrons per MIP - float globalTimeOfFlight = 315.0 / o2::constants::physics::LightSpeedCm2NS; // TODO [check the correct value for distance of FV0 to IP] float mCfdDeadTime = 15.6; // [ns] float mCFD_trsh = 3.; // [mV] float getCFDTrshInAdc() const { return mCFD_trsh * getChannelsPerMilivolt(); } // [ADC channels] /// Parameters for trigger simulation - bool useMaxChInAdc = true; // default = true - int adcChargeCenThr = 3 * 498; // threshold value of ADC charge for Central trigger - int adcChargeSCenThr = 1 * 498; // threshold value of ADC charge for Semi-central trigger - int maxCountInAdc = 4095; // to take care adc ADC overflow - short mTime_trg_gate = 153; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) - uint8_t defaultChainQtc = 0x48; // only 2 flags are set by default in simulation: kIsCFDinADCgate and kIsEventInTVDC - static constexpr float mAmpThresholdForReco = 24; // only channels with amplitude higher will participate in calibration and collision time - static constexpr short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time + bool useMaxChInAdc = true; // default = true + int adcChargeCenThr = 3 * 498; // threshold value of ADC charge for Central trigger + int adcChargeSCenThr = 1 * 498; // threshold value of ADC charge for Semi-central trigger + int maxCountInAdc = 4095; // to take care adc ADC overflow + short mTime_trg_gate = 153; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) + uint8_t defaultChainQtc = 0x48; // only 2 flags are set by default in simulation: kIsCFDinADCgate and kIsEventInTVDC + float mAmpThresholdForReco = 24; // only channels with amplitude higher will participate in calibration and collision time + short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time + int NchannelsLevel = 2; // trigger Nchannels + float InnerChargeLevel = 4; // InnerRingsChargeLevel + float OuterChargeLevel = 4; // OuterRingsChargeLevel + float ChargeLevel = 8; // ChargeLevel O2ParamDef(FV0DigParam, "FV0DigParam"); }; diff --git a/Detectors/FIT/FV0/simulation/src/Detector.cxx b/Detectors/FIT/FV0/simulation/src/Detector.cxx index 8cf1f5530e93d..07eb9053bf3b8 100644 --- a/Detectors/FIT/FV0/simulation/src/Detector.cxx +++ b/Detectors/FIT/FV0/simulation/src/Detector.cxx @@ -280,6 +280,7 @@ void Detector::ConstructGeometry() // mGeometry->enableComponent(Geometry::eAluminiumContainer, false); mGeometry->buildGeometry(); } + void Detector::addAlignableVolumes() const { // @@ -292,19 +293,19 @@ void Detector::addAlignableVolumes() const LOG(info) << "FV0: Add alignable volumes"; if (!gGeoManager) { - LOG(fatal) << "TGeoManager doesn't exist !"; + LOG(fatal) << "TGeoManager doesn't exist!"; return; } - TString volPath, symName; - for (auto& half : {"RIGHT_0", "LEFT_1"}) { - volPath = Form("/cave_1/barrel_1/FV0_1/FV0%s", half); - symName = Form("FV0%s", half); - LOG(info) << "FV0: Add alignable volume: " << symName << ": " << volPath; - if (!gGeoManager->SetAlignableEntry(symName.Data(), volPath.Data())) { - LOG(fatal) << "FV0: Unable to set alignable entry! " << symName << ": " << volPath; + auto addAlignabelVolume = [](const std::string& volPath, const std::string& symName) -> void { + LOG(info) << "FV0: Add alignable volume: " << symName << " <-> " << volPath; + if (!gGeoManager->SetAlignableEntry(symName.c_str(), volPath.c_str())) { + LOG(fatal) << "FV0: Unable to set alignable entry! " << symName << " <-> " << volPath; } - } + }; + + addAlignabelVolume("/cave_1/barrel_1/FV0_1/FV0RIGHT_0", Geometry::getDetectorRightSymName()); + addAlignabelVolume("/cave_1/barrel_1/FV0_1/FV0LEFT_1", Geometry::getDetectorLeftSymName()); } o2::fv0::Hit* Detector::addHit(Int_t trackId, Int_t cellId, diff --git a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx index 184e6dbbd09f8..3237f9bab7879 100644 --- a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx @@ -38,8 +38,8 @@ void Digitizer::clear() void Digitizer::init() { LOG(info) << "init"; - mNBins = FV0DigParam::Instance().waveformNbins; //Will be computed using detector set-up from CDB - mBinSize = FV0DigParam::Instance().waveformBinWidth; //Will be set-up from CDB + mNBins = FV0DigParam::Instance().waveformNbins; // Will be computed using detector set-up from CDB + mBinSize = FV0DigParam::Instance().waveformBinWidth; // Will be set-up from CDB mNTimeBinsPerBC = std::lround(o2::constants::lhc::LHCBunchSpacingNS / mBinSize); // 1920 bins/BC for (Int_t detID = 0; detID < Constants::nFv0Channels; detID++) { @@ -98,8 +98,13 @@ void Digitizer::process(const std::vector& hits, for (auto ids : hitIdx) { const auto& hit = hits[ids]; Int_t detId = hit.GetDetectorID(); - Double_t hitEdep = hit.GetHitValue() * 1e3; //convert to MeV - Float_t const hitTime = hit.GetTime() * 1e9; + + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(detId)) { + continue; + } + + Double_t hitEdep = hit.GetHitValue() * 1e3; // convert to MeV + Float_t const hitTime = hit.GetTime() * 1e9; // convert to ns // TODO: check how big is inaccuracy if more than 1 'below-threshold' particles hit the same detector cell if (hitEdep < FV0DigParam::Instance().singleMipThreshold || hitTime > FV0DigParam::Instance().singleHitTimeThreshold) { continue; @@ -138,15 +143,17 @@ void Digitizer::process(const std::vector& hits, if (tNS < 0 && cachedIR[nCachedIR] > irHit) { continue; // don't go to negative BC/orbit (it will wrap) } - setBCCache(cachedIR[nCachedIR++]); // ensure existence of cached container - } //BCCache loop - createPulse(mipFraction, hit.GetTrackID(), hitTime, cachedIR, nCachedIR, detId); + // ensure existence of cached container + setBCCache(cachedIR[nCachedIR++]); + } // BCCache loop + + createPulse(mipFraction, hit.GetTrackID(), hitTime, hit.GetPos().R(), cachedIR, nCachedIR, detId); - } //while loop - } //hitloop + } // while loop + } // hitloop } -void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, +void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, const float hitR, std::array const& cachedIR, int nCachedIR, const int detId) { @@ -160,8 +167,9 @@ void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, } } - ///Time of flight subtracted from Hit time //TODO have different TOF according to thr ring number - Int_t const NBinShift = std::lround((hitTime - FV0DigParam::Instance().globalTimeOfFlight) / FV0DigParam::Instance().waveformBinWidth); + // Subtract time-of-flight from hit time + const float timeOfFlight = hitR / o2::constants::physics::LightSpeedCm2NS; + Int_t const NBinShift = std::lround((hitTime - timeOfFlight + FV0DigParam::Instance().hitTimeOffset) / FV0DigParam::Instance().waveformBinWidth); if (NBinShift >= 0 && NBinShift < FV0DigParam::Instance().waveformNbins) { mPmtResponseTemp.resize(FV0DigParam::Instance().waveformNbins, 0.); @@ -192,7 +200,7 @@ void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, } added[ir] = true; } - ///Add MC labels to BCs for those contributed to the PMT signal + /// Add MC labels to BCs for those contributed to the PMT signal for (int ir = 0; ir < nCachedIR; ir++) { if (added[ir]) { auto bcCache = getBCCache(cachedIR[ir]); @@ -230,6 +238,8 @@ void Digitizer::storeBC(const BCCache& bc, int8_t nTotFiredCells = 0; int8_t nTrgFiredCells = 0; // number of fired cells, that follow additional trigger conditions (time gate) int totalChargeAllRing = 0; + int totalChargeInnerRing = 0; + int totalChargeOuterRing = 0; int32_t avgTime = 0; double nSignalInner = 0; double nSignalOuter = 0; @@ -277,8 +287,10 @@ void Digitizer::storeBC(const BCCache& bc, avgTime += iCfdZero; if (iPmt < 24) { nSignalInner++; + totalChargeInnerRing += iTotalCharge; } else { nSignalOuter++; + totalChargeOuterRing += iTotalCharge; } } } @@ -292,13 +304,15 @@ void Digitizer::storeBC(const BCCache& bc, } else { avgTime = o2::fit::Triggers::DEFAULT_TIME; } - ///Triggers for FV0 - bool isA, isAIn, isAOut, isCen, isSCen; + /// Triggers for FV0 + bool isA, isNchannels, isAIn, isAOut, isTotalCharge; isA = nTrgFiredCells > 0; - isAIn = nSignalInner > 0; // ring 1,2 and 3 - isAOut = nSignalOuter > 0; // ring 4 and 5 - isCen = totalChargeAllRing > FV0DigParam::Instance().adcChargeCenThr; - isSCen = totalChargeAllRing > FV0DigParam::Instance().adcChargeSCenThr; + isNchannels = nTrgFiredCells > FV0DigParam::Instance().NchannelsLevel; + // isAIn = nSignalInner > FV0DigParam::Instance().NchannelsLevel; // ring 1,2 and 3 + isAIn = 0.125 * totalChargeInnerRing > 2 * FV0DigParam::Instance().InnerChargeLevel; // ring 1,2 and 3 + // isAOut = nSignalOuter > FV0DigParam::Instance().NchannelsLevel; // ring 4 and 5 + isAOut = 0.125 * totalChargeOuterRing > 2 * FV0DigParam::Instance().OuterChargeLevel; // ring 4 and 5 + isTotalCharge = 0.125 * totalChargeAllRing > 2 * FV0DigParam::Instance().ChargeLevel; Triggers triggers; const int unusedCharge = o2::fit::Triggers::DEFAULT_AMP; @@ -306,10 +320,10 @@ void Digitizer::storeBC(const BCCache& bc, const int unusedZero = o2::fit::Triggers::DEFAULT_ZERO; const bool unusedBitsInSim = false; // bits related to laser and data validity const bool bitDataIsValid = true; - triggers.setTriggers(isA, isAIn, isAOut, isCen, isSCen, nTrgFiredCells, (int8_t)unusedZero, + triggers.setTriggers(isA, isAIn, isAOut, isTotalCharge, isNchannels, nTrgFiredCells, (int8_t)unusedZero, (int32_t)(0.125 * totalChargeAllRing), (int32_t)unusedCharge, (int16_t)avgTime, (int16_t)unusedTime, unusedBitsInSim, unusedBitsInSim, bitDataIsValid); digitsBC.emplace_back(first, nTotFiredCells, bc, triggers, mEventId - 1); - digitsTrig.emplace_back(bc, isA, isAIn, isAOut, isCen, isSCen); + digitsTrig.emplace_back(bc, isA, isAIn, isAOut, isTotalCharge, isNchannels); for (auto const& lbl : bc.labels) { labels.addElement(nBC, lbl); } @@ -334,8 +348,8 @@ Int_t Digitizer::SimulateLightYield(Int_t pmt, Int_t nPhot) const //--------------------------------------------------------------------------- Float_t Digitizer::IntegrateCharge(const ChannelDigitF& pulse) const { - int const chargeIntMin = FV0DigParam::Instance().isIntegrateFull ? 0 : (FV0DigParam::Instance().avgCfdTimeForMip - 6.0) / mBinSize; //Charge integration offset (cfd mean time - 6 ns) - int const chargeIntMax = FV0DigParam::Instance().isIntegrateFull ? mNTimeBinsPerBC : (FV0DigParam::Instance().avgCfdTimeForMip + 14.0) / mBinSize; //Charge integration offset (cfd mean time + 14 ns) + int const chargeIntMin = FV0DigParam::Instance().isIntegrateFull ? 0 : (FV0DigParam::Instance().avgCfdTimeForMip - 6.0) / mBinSize; // Charge integration offset (cfd mean time - 6 ns) + int const chargeIntMax = FV0DigParam::Instance().isIntegrateFull ? mNTimeBinsPerBC : (FV0DigParam::Instance().avgCfdTimeForMip + 14.0) / mBinSize; // Charge integration offset (cfd mean time + 14 ns) if (chargeIntMin < 0 || chargeIntMin > mNTimeBinsPerBC || chargeIntMax > mNTimeBinsPerBC) { LOG(fatal) << "invalid indicess: chargeInMin=" << chargeIntMin << " chargeIntMax=" << chargeIntMax; } @@ -392,7 +406,7 @@ float Digitizer::getDistFromCellCenter(UInt_t cellId, double hitx, double hity) double a = -(y0 - pCell->y) / (x0 - pCell->x); double b = 1; double c = -(y0 - a * x0); - //Return the distance from hit to this line + // Return the distance from hit to this line return (a * hitx + b * hity + c) / TMath::Sqrt(a * a + b * b); } diff --git a/Detectors/FIT/FV0/simulation/src/digit2raw.cxx b/Detectors/FIT/FV0/simulation/src/digit2raw.cxx index b8ac2a9286b38..c7441407093ab 100644 --- a/Detectors/FIT/FV0/simulation/src/digit2raw.cxx +++ b/Detectors/FIT/FV0/simulation/src/digit2raw.cxx @@ -13,51 +13,12 @@ /// \author ruben.shahoyan@cern.ch afurs@cern.ch #include -#include #include -#include "CommonUtils/StringUtils.h" -#include "CommonUtils/ConfigurableParam.h" -#include "DetectorsRaw/HBFUtils.h" #include "FV0Raw/RawWriterFV0.h" -#include "DataFormatsParameters/GRPObject.h" -/// MC->raw conversion for FDD +/// MC->raw conversion for FV0 namespace bpo = boost::program_options; -struct Configuration { - Configuration(const bpo::variables_map& vm) - { - mInputFile = vm["input-file"].as(); - mOutputDir = vm["output-dir"].as(); - mVerbosity = vm["verbosity"].as(); - mFileFor = vm["file-for"].as(); - mRdhVersion = vm["rdh-version"].as(); - mEnablePadding = vm["enable-padding"].as(); - mNoEmptyHBF = vm["no-empty-hbf"].as(); - mFlpName = vm["flp-name"].as(); - mCcdbPath = vm["ccdb-path"].as(); - mChannelMapPath = vm["lut-path"].as(); - if (mRdhVersion < 7 && !mEnablePadding) { - mEnablePadding = true; - LOG(info) << "padding is always ON for RDH version " << mRdhVersion; - } - mDataFormat = mEnablePadding ? 0 : 2; - } - bool mNoEmptyHBF; - bool mEnablePadding; - int mVerbosity; - uint32_t mRdhVersion; - uint32_t mDataFormat; - std::string mInputFile; - std::string mOutputDir; - std::string mFileFor; - std::string mFlpName; - std::string mCcdbPath; - std::string mChannelMapPath; -}; - -template -void digit2raw(const Configuration& cfg); int main(int argc, char** argv) { @@ -69,21 +30,10 @@ int main(int argc, char** argv) bpo::positional_options_description opt_pos; try { - auto add_option = opt_general.add_options(); - add_option("help,h", "Print this help message"); - add_option("verbosity,v", bpo::value()->default_value(0), "verbosity level"); - add_option("input-file,i", bpo::value()->default_value("fv0digits.root"), "input FV0 digits file"); - add_option("flp-name", bpo::value()->default_value("alio2-cr1-flp180"), "single file per: all,flp,cru,link"); - add_option("file-for,f", bpo::value()->default_value("all"), "single file per: all,flp,cruendpoint,link"); - add_option("output-dir,o", bpo::value()->default_value("./"), "output directory for raw data"); - uint32_t defRDH = o2::raw::RDHUtils::getVersion(); - add_option("rdh-version,r", bpo::value()->default_value(defRDH), "RDH version to use"); - add_option("no-empty-hbf,e", bpo::value()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); - add_option("hbfutils-config,u", bpo::value()->default_value(std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)), "config file for HBFUtils (or none)"); - add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); - add_option("ccdb-path", bpo::value()->default_value(""), "CCDB url which contains LookupTable"); - add_option("lut-path", bpo::value()->default_value(""), "LookupTable path, e.g. FV0/LookupTable"); - add_option("enable-padding", bpo::value()->default_value(false)->implicit_value(true), "enable GBT word padding to 128 bits even for RDH V7"); + opt_general.add_options()("help,h", "Print this help message"); + // config with FV0 defaults + o2::fit::DigitToRawConfig::configureExecOptions(opt_general, "fv0digits.root", "alio2-cr1-flp180"); + // common part opt_all.add(opt_general).add(opt_hidden); bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); @@ -102,62 +52,12 @@ int main(int argc, char** argv) std::cerr << e.what() << ", application will now exit" << std::endl; exit(2); } - - std::string confDig = vm["hbfutils-config"].as(); - if (!confDig.empty() && confDig != "none") { - o2::conf::ConfigurableParam::updateFromFile(confDig, "HBFUtils"); - } - o2::conf::ConfigurableParam::updateFromString(vm["configKeyValues"].as()); - - const Configuration cfg(vm); + const o2::fit::DigitToRawConfig cfg(vm); if (!cfg.mEnablePadding) { - digit2raw(cfg); + o2::fit::DigitToRawDevice::digit2raw(cfg); } else { - digit2raw(cfg); + o2::fit::DigitToRawDevice::digit2raw(cfg); } o2::raw::HBFUtils::Instance().print(); - return 0; } - -template -void digit2raw(const Configuration& cfg) -{ - TStopwatch swTot; - swTot.Start(); - RawWriterType m2r; - m2r.setFileFor(cfg.mFileFor); - m2r.setFlpName(cfg.mFlpName); - m2r.setVerbosity(cfg.mVerbosity); - if (cfg.mCcdbPath != "") { - m2r.setCCDBurl(cfg.mCcdbPath); - } - if (cfg.mChannelMapPath != "") { - m2r.setLUTpath(cfg.mChannelMapPath); - } - auto& wr = m2r.getWriter(); - std::string inputGRP = o2::base::NameConf::getGRPFileName(); - const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); - wr.setContinuousReadout(grp->isDetContinuousReadOut(o2::detectors::DetID::FV0)); // must be set explicitly - const int superPageSizeInB = 1024 * 1024; - wr.setSuperPageSize(superPageSizeInB); - wr.useRDHVersion(cfg.mRdhVersion); - wr.setDontFillEmptyHBF(cfg.mNoEmptyHBF); - wr.useRDHDataFormat(cfg.mDataFormat); - if (!cfg.mEnablePadding) { // CRU page alignment padding is used only if no GBT word padding is used - wr.setAlignmentSize(16); // change to constexpr static field from class? - wr.setAlignmentPaddingFiller(0xff); - } - o2::raw::assertOutputDirectory(cfg.mOutputDir); - - std::string outDirName(cfg.mOutputDir); - if (outDirName.back() != '/') { - outDirName += '/'; - } - - m2r.convertDigitsToRaw(outDirName, cfg.mInputFile); - wr.writeConfFile(wr.getOrigin().str, "RAWDATA", o2::utils::Str::concat_string(outDirName, wr.getOrigin().str, "raw.cfg")); - // - swTot.Stop(); - swTot.Print(); -} diff --git a/Detectors/FIT/FV0/workflow/CMakeLists.txt b/Detectors/FIT/FV0/workflow/CMakeLists.txt index eec745d5fdf1e..a304adc61b5fd 100644 --- a/Detectors/FIT/FV0/workflow/CMakeLists.txt +++ b/Detectors/FIT/FV0/workflow/CMakeLists.txt @@ -53,6 +53,16 @@ o2_add_executable(flp-dpl-workflow PUBLIC_LINK_LIBRARIES O2::FV0Workflow O2::FITWorkflow O2::FV0Raw TARGETVARNAME fv0flpexe) +o2_add_executable(recpoints-reader-workflow + SOURCES src/recpoints-reader-workflow.cxx + COMPONENT_NAME fv0 + PUBLIC_LINK_LIBRARIES O2::FV0Workflow) + +o2_add_executable(recpoints-writer-workflow + SOURCES src/recpoints-writer-workflow.cxx + COMPONENT_NAME fv0 + PUBLIC_LINK_LIBRARIES O2::FV0Workflow) + o2_add_executable(integrate-cluster-workflow SOURCES src/cluster-integrator.cxx COMPONENT_NAME fv0 diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h index 67b74f45e42bf..76f1aae5e728d 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace fv0 class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h index db4f154a302c7..0df9403a88a12 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace fv0 class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h index 015870d9178e2..f035b2406e5ba 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace fv0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap); } // namespace fv0 } // namespace o2 #endif diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h index d71e154280e3d..934ce4d2c4a66 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h @@ -34,7 +34,7 @@ class ReconstructionDPL : public Task static constexpr int NCHANNELS = o2::fv0::Constants::nFv0Channels; public: - ReconstructionDPL(bool useMC, const std::string ccdbpath) : mUseMC(useMC), mCCDBpath(ccdbpath) {} + ReconstructionDPL(bool useMC, bool useDeadChannelMap, const std::string ccdbpath) : mUseMC(useMC), mUseDeadChannelMap(useDeadChannelMap), mCCDBpath(ccdbpath) {} ~ReconstructionDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -44,6 +44,8 @@ class ReconstructionDPL : public Task private: bool mUseMC = false; bool mUpdateCCDB = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; const std::string mCCDBpath = o2::base::NameConf::getCCDBServer(); std::vector mRecPoints; std::vector mRecChData; @@ -53,7 +55,7 @@ class ReconstructionDPL : public Task }; /// create a processor spec -framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch"); +framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, bool useDeadChannelMap = true, const std::string ccdbpath = "http://alice-ccdb.cern.ch"); } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx index 08a6c4cbb2e24..a49bda2cec18b 100644 --- a/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx @@ -65,13 +65,13 @@ void DigitReader::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); LOG(debug) << "FV0DigitReader pushed " << channels.size() << " channels in " << digits.size() << " digits"; - pc.outputs().snapshot(Output{"FV0", "DIGITSBC", 0, Lifetime::Timeframe}, digits); - pc.outputs().snapshot(Output{"FV0", "DIGITSCH", 0, Lifetime::Timeframe}, channels); + pc.outputs().snapshot(Output{"FV0", "DIGITSBC", 0}, digits); + pc.outputs().snapshot(Output{"FV0", "DIGITSCH", 0}, channels); if (mUseMC) { - pc.outputs().snapshot(Output{"FV0", "DIGITSMCTR", 0, Lifetime::Timeframe}, labels); + pc.outputs().snapshot(Output{"FV0", "DIGITSMCTR", 0}, labels); } if (mUseTrgInput) { - pc.outputs().snapshot(Output{"FV0", "TRIGGERINPUT", 0, Lifetime::Timeframe}, trgInput); + pc.outputs().snapshot(Output{"FV0", "TRIGGERINPUT", 0}, trgInput); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx index 479ff466d8477..6cf8043cf683f 100644 --- a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx @@ -24,12 +24,13 @@ namespace o2 { namespace fv0 { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setSupportBCShifts(true); + mCTFCoder.setDictBinding("ctfdict_FV0"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -50,8 +51,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc); - auto buff = pc.inputs().get>("ctf"); + mCTFCoder.updateTimeDependentParams(pc, true); + auto buff = pc.inputs().get>("ctf_FV0"); auto& digits = pc.outputs().make>(OutputRef{"digits"}); auto& channels = pc.outputs().make>(OutputRef{"channels"}); @@ -72,7 +73,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "FV0", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -80,17 +81,19 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) OutputSpec{{"ctfrep"}, "FV0", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "FV0", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionary")); + inputs.emplace_back("ctf_FV0", "FV0", "CTFDATA", sspec, Lifetime::Timeframe); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_FV0", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "fv0-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx index a4a34313107d1..2448af09fac4e 100644 --- a/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace fv0 { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -48,14 +47,14 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - mCTFCoder.updateTimeDependentParams(pc); + mCTFCoder.updateTimeDependentParams(pc, true); auto digits = pc.inputs().get>("digits"); auto channels = pc.inputs().get>("channels"); if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{"FV0", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"FV0", "CTFDATA", 0}); auto iosize = mCTFCoder.encode(buffer, digits, channels); pc.outputs().snapshot({"ctfrep", 0}, iosize); if (mSelIR) { @@ -71,12 +70,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "FV0", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("channels", "FV0", "DIGITSCH", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionary")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -85,12 +87,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"FV0", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "FV0", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/FV0DigitWriterSpec.cxx b/Detectors/FIT/FV0/workflow/src/FV0DigitWriterSpec.cxx index eda1f5061e6f2..57ecfe368625c 100644 --- a/Detectors/FIT/FV0/workflow/src/FV0DigitWriterSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/FV0DigitWriterSpec.cxx @@ -51,19 +51,19 @@ o2::framework::DataProcessorSpec getFV0DigitWriterSpec(bool mctruth, bool trigIn "fv0digits.root", "o2sim", MakeRootTreeWriterSpec::CustomClose(finishWriting), - BranchDefinition>{InputSpec{"digitBCinput", "FV0", "DIGITSBC"}, "FV0DigitBC", "fv0-digits-branch-name", 1, + BranchDefinition>{InputSpec{"digitBCinput", "FV0", "DIGITSBC"}, "FV0DigitBC", 1, logger}, - BranchDefinition>{InputSpec{"digitChinput", "FV0", "DIGITSCH"}, "FV0DigitCh", "fv0-chhdata-branch-name"}, - BranchDefinition>{InputSpec{"digitTrinput", "FV0", "TRIGGERINPUT"}, "TRIGGERINPUT", "fv0-triggerinput-branch-name"}, + BranchDefinition>{InputSpec{"digitChinput", "FV0", "DIGITSCH"}, "FV0DigitCh"}, + BranchDefinition>{InputSpec{"digitTrinput", "FV0", "TRIGGERINPUT"}, "TRIGGERINPUT"}, std::move(labelsdef))(); } else { return MakeRootTreeWriterSpec("FV0DigitWriterRaw", "o2_fv0digits.root", "o2sim", MakeRootTreeWriterSpec::CustomClose(finishWriting), - BranchDefinition>{InputSpec{"digitBCinput", "FV0", "DIGITSBC"}, "FV0DigitBC", "fv0-digits-branch-name", 1, + BranchDefinition>{InputSpec{"digitBCinput", "FV0", "DIGITSBC"}, "FV0DigitBC", 1, logger}, - BranchDefinition>{InputSpec{"digitChinput", "FV0", "DIGITSCH"}, "FV0DigitCh", "fv0-chhdata-branch-name"}, + BranchDefinition>{InputSpec{"digitChinput", "FV0", "DIGITSCH"}, "FV0DigitCh"}, std::move(labelsdef))(); } } diff --git a/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx b/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx index 163beacf0c140..5997cac500ee6 100644 --- a/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx @@ -49,8 +49,8 @@ void RecPointReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(debug) << "FV0 RecPointReader pushes " << mRecPoints->size() << " recpoints with " << mChannelData->size() << " channels at entry " << ent; - pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, *mRecPoints); - pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0, Lifetime::Timeframe}, *mChannelData); + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0}, *mRecPoints); + pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, *mChannelData); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx index 6bfc5479303d1..a0ef71b75765a 100644 --- a/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx @@ -22,14 +22,13 @@ namespace o2 namespace fv0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::fv0::getDigitReaderSpec(useMC)); } - - specs.emplace_back(o2::fv0::getReconstructionSpec(useMC)); + specs.emplace_back(o2::fv0::getReconstructionSpec(useMC, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::fv0::getRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx index d48b96fd4e626..cdf297b334588 100644 --- a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx @@ -21,6 +21,7 @@ #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/MCLabel.h" #include "DataFormatsFV0/FV0ChannelTimeCalibrationObject.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include "Framework/CCDBParamSpec.h" using namespace o2::framework; @@ -41,6 +42,7 @@ void ReconstructionDPL::run(ProcessingContext& pc) { mTimer.Start(false); mRecPoints.clear(); + mRecChData.clear(); auto digits = pc.inputs().get>("digits"); auto digch = pc.inputs().get>("digch"); // RS: if we need to process MC truth, uncomment lines below @@ -53,23 +55,24 @@ void ReconstructionDPL::run(ProcessingContext& pc) auto caliboffsets = pc.inputs().get("fv0offsets"); mReco.SetChannelOffset(caliboffsets.get()); } + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.SetDeadChannelMap(deadChannelMap.get()); + } int nDig = digits.size(); LOG(debug) << " nDig " << nDig << " | ndigch " << digch.size(); mRecPoints.reserve(nDig); - mRecChData.resize(digch.size()); for (int id = 0; id < nDig; id++) { const auto& digit = digits[id]; LOG(debug) << " ndig " << id << " bc " << digit.getBC() << " orbit " << digit.getOrbit(); auto channels = digit.getBunchChannelData(digch); - gsl::span out_ch(mRecChData); - out_ch = out_ch.subspan(digit.ref.getFirstEntry(), digit.ref.getEntries()); - mRecPoints.emplace_back(mReco.process(digit, channels, out_ch)); + mRecPoints.emplace_back(mReco.process(digit, channels, mRecChData)); } LOG(debug) << "FV0 reconstruction pushes " << mRecPoints.size() << " RecPoints"; - pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, mRecPoints); - pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0, Lifetime::Timeframe}, mRecChData); + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0}, mRecPoints); + pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, mRecChData); mTimer.Stop(); } @@ -80,6 +83,9 @@ void ReconstructionDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) mUpdateCCDB = false; return; } + if (matcher == ConcreteDataMatcher(o2::header::gDataOriginFV0, "DeadChannelMap", 0)) { + mUpdateDeadChannelMap = false; + } } void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) @@ -88,7 +94,7 @@ void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) +DataProcessorSpec getReconstructionSpec(bool useMC, bool useDeadChannelMap, const std::string ccdbpath) { std::vector inputSpec; std::vector outputSpec; @@ -98,6 +104,10 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) LOG(info) << "Currently Reconstruction does not consume and provide MC truth"; inputSpec.emplace_back("labels", o2::header::gDataOriginFV0, "DIGITSMCTR", 0, Lifetime::Timeframe); } + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFV0, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/DeadChannelMap")); + } inputSpec.emplace_back("fv0offsets", "FV0", "TimeOffset", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/ChannelTimeOffset")); @@ -109,7 +119,7 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) "fv0-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC, ccdbpath)}, + AlgorithmSpec{adaptFromTask(useMC, useDeadChannelMap, ccdbpath)}, Options{}}; } diff --git a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx index 90f37996b55b7..932e0a37ee376 100644 --- a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::fv0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::fv0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx b/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx index 16d1383c7e8c4..309560e2d6b36 100644 --- a/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx @@ -39,6 +39,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, + {"disable-dead-channel-map", o2::framework::VariantType::Bool, false, {"disable dead channel map"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -59,9 +60,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto useMC = !configcontext.options().get("disable-mc"); auto disableRootInp = configcontext.options().get("disable-root-input"); auto disableRootOut = configcontext.options().get("disable-root-output"); + bool useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); LOG(info) << "WorkflowSpec getRecoWorkflow useMC " << useMC; - auto wf = o2::fv0::getRecoWorkflow(useMC, disableRootInp, disableRootOut); + auto wf = o2::fv0::getRecoWorkflow(useMC, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/FIT/FV0/workflow/src/recpoints-reader-workflow.cxx b/Detectors/FIT/FV0/workflow/src/recpoints-reader-workflow.cxx new file mode 100644 index 0000000000000..ecbe89b8bbed3 --- /dev/null +++ b/Detectors/FIT/FV0/workflow/src/recpoints-reader-workflow.cxx @@ -0,0 +1,58 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file recpoints-reader-workflow.cxx +/// \brief FV0 RecPoints reader workflow +/// +/// \author Andreas Molander andreas.molander@cern.ch + +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" + +#include "FV0Workflow/RecPointReaderSpec.h" + +#include + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + o2::conf::ConfigurableParam::updateFromString(ctx.options().get("configKeyValues")); + bool disableMC = ctx.options().get("disable-mc"); + + WorkflowSpec specs; + DataProcessorSpec producer = o2::fv0::getRecPointReaderSpec(!disableMC); + specs.push_back(producer); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(ctx, specs); + return specs; +} diff --git a/Detectors/FIT/FV0/workflow/src/recpoints-writer-workflow.cxx b/Detectors/FIT/FV0/workflow/src/recpoints-writer-workflow.cxx new file mode 100644 index 0000000000000..0fd3bd3bef2e8 --- /dev/null +++ b/Detectors/FIT/FV0/workflow/src/recpoints-writer-workflow.cxx @@ -0,0 +1,47 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file recpoints-writer-workflow.cxx +/// \brief FV0 RecPoints writer workflow +/// +/// \author Andreas Molander andreas.molander@cern.ch + +#include "FV0Workflow/RecPointWriterSpec.h" + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" + +#include + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + o2::conf::ConfigurableParam::updateFromString(ctx.options().get("configKeyValues")); + bool disableMC = ctx.options().get("disable-mc"); + + WorkflowSpec specs; + DataProcessorSpec producer = o2::fv0::getRecPointWriterSpec(!disableMC); + specs.push_back(producer); + return specs; +} diff --git a/Detectors/FIT/README.md b/Detectors/FIT/README.md index b079dce2cc972..8da3f32d7f4e2 100644 --- a/Detectors/FIT/README.md +++ b/Detectors/FIT/README.md @@ -9,6 +9,9 @@ This is a top page for the FIT detector documentation. diff --git a/Detectors/FIT/common/calibration/include/FITCalibration/FITCalibrator.h b/Detectors/FIT/common/calibration/include/FITCalibration/FITCalibrator.h index f059d1f3af088..dc70d6a521a77 100644 --- a/Detectors/FIT/common/calibration/include/FITCalibration/FITCalibrator.h +++ b/Detectors/FIT/common/calibration/include/FITCalibration/FITCalibrator.h @@ -63,13 +63,12 @@ class FITCalibrator final : public o2::calibration::TimeSlotCalibration md; auto* container = slot.getContainer(); - static const double TFlength = 1E-3 * o2::raw::HBFUtils::Instance().getNOrbitsPerTF() * o2::constants::lhc::LHCOrbitMUS; // in ms - auto starting = slot.getStartTimeMS(); - auto stopping = slot.getEndTimeMS(); - LOGP(info, "!!!! {}({})<=TF<={}({}), starting: {} stopping {}", slot.getTFStart(), slot.getStartTimeMS(), slot.getTFEnd(), slot.getEndTimeMS(), starting, stopping); - auto calibrationObject = container->generateCalibrationObject(slot.getStartTimeMS(), slot.getEndTimeMS(), mExtraInfo); + const auto startValidity = slot.getStartTimeMS() - o2::ccdb::CcdbObjectInfo::SECOND * 10; + const auto endValidity = slot.getEndTimeMS() + o2::ccdb::CcdbObjectInfo::MONTH; + LOGP(info, "!!!! {}<=TF<={}, startValidity: {} endValidity: {}", slot.getTFStart(), slot.getTFEnd(), startValidity, endValidity); + auto calibrationObject = container->generateCalibrationObject(startValidity, endValidity, mExtraInfo); std::vector preparedCalibObjects; - preparedCalibObjects.emplace_back(doSerializationAndPrepareObjectInfo(calibrationObject, starting, stopping)); + preparedCalibObjects.emplace_back(doSerializationAndPrepareObjectInfo(calibrationObject, startValidity, endValidity)); mStoredCalibrationObjects.insert(mStoredCalibrationObjects.end(), std::make_move_iterator(preparedCalibObjects.begin()), std::make_move_iterator(preparedCalibObjects.end())); diff --git a/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigProcessorSpec.h b/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigProcessorSpec.h index b67507f9b4801..18c0b593b0a02 100644 --- a/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigProcessorSpec.h +++ b/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigProcessorSpec.h @@ -47,12 +47,18 @@ class FITDCSConfigProcessor : public o2::framework::Task void init(o2::framework::InitContext& ic) final { initDCSConfigReader(); - mDCSConfigReader->setFileNameDChM(ic.options().get("filename-dchm")); + mDCSConfigReader->setFileNameDChM(ic.options().get("filename-dchm")); + mDCSConfigReader->setValidDaysDChM(ic.options().get("valid-days-dchm")); mDCSConfigReader->setCcdbPathDChM(mDetectorName + "/Calib/DeadChannelMap"); mVerbose = ic.options().get("use-verbose-mode"); mDCSConfigReader->setVerboseMode(mVerbose); + mValidateUpload = !ic.options().get("no-validate"); + mDCSConfigReader->setValidateUploadMode(mValidateUpload); + LOG(info) << "Verbose mode: " << mVerbose; + LOG(info) << "Validate upload: " << mValidateUpload; LOG(info) << "Expected dead channel map file name: " << mDCSConfigReader->getFileNameDChM(); + LOG(info) << "Dead channel maps will be valid for " << mDCSConfigReader->getValidDaysDChM() << " days"; } void run(o2::framework::ProcessingContext& pc) final @@ -123,6 +129,7 @@ class FITDCSConfigProcessor : public o2::framework::Task std::string mDetectorName; ///< Detector name o2::header::DataDescription mDataDescriptionDChM; ///< DataDescription for the dead channel map bool mVerbose = false; ///< Verbose mode + bool mValidateUpload = true; ///< Validate upload }; } // namespace fit diff --git a/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigReader.h b/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigReader.h index 428d98eaf2fc7..c43607e091493 100644 --- a/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigReader.h +++ b/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigReader.h @@ -52,18 +52,26 @@ class FITDCSConfigReader const std::string& getFileNameDChM() const; void setFileNameDChM(const std::string& fileName); + const uint getValidDaysDChM() const; + void setValidDaysDChM(const uint validDays); + const bool getVerboseMode() const; void setVerboseMode(const bool verboseMode); + const bool getValidateUploadMode() const; + void setValidateUploadMode(const bool validateUpload); + protected: o2::fit::DeadChannelMap mDChM; ///< The dead channel map CCDB object bool mVerbose = false; ///< Verbose mode private: std::string mFileNameDChM; ///< The expected file name of the dead channel map + uint mValidDaysDChM = 180u; ///< The dead channel map validity in days std::string mCcdbPathDChM; ///< The dead channel map CCDB path long mStartValidityDChM = o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP; ///< Start validity of the dead channel map CCDB object o2::ccdb::CcdbObjectInfo mCcdbObjectInfoDChM; ///< CCDB object info for the dead channel map + bool mValidateUpload = true; ///< Validate upload mode ClassDefNV(FITDCSConfigReader, 1); }; diff --git a/Detectors/FIT/common/dcsmonitoring/src/FITDCSConfigReader.cxx b/Detectors/FIT/common/dcsmonitoring/src/FITDCSConfigReader.cxx index 43deeadbaf6c2..3540d3a2f33c0 100644 --- a/Detectors/FIT/common/dcsmonitoring/src/FITDCSConfigReader.cxx +++ b/Detectors/FIT/common/dcsmonitoring/src/FITDCSConfigReader.cxx @@ -47,6 +47,7 @@ void FITDCSConfigReader::updateDChMCcdbObjectInfo() { std::map metadata; o2::calibration::Utils::prepareCCDBobjectInfo(mDChM, mCcdbObjectInfoDChM, mCcdbPathDChM, metadata, getStartValidityDChM(), getEndValidityDChM()); + mCcdbObjectInfoDChM.setValidateUpload(getValidateUploadMode()); } const o2::fit::DeadChannelMap& FITDCSConfigReader::getDChM() const { return mDChM; } @@ -54,7 +55,7 @@ void FITDCSConfigReader::resetDChM() { mDChM.clear(); } const std::string& FITDCSConfigReader::getCcdbPathDChm() const { return mCcdbPathDChM; } void FITDCSConfigReader::setCcdbPathDChM(const std::string& ccdbPath) { mCcdbPathDChM = ccdbPath; } const long FITDCSConfigReader::getStartValidityDChM() const { return mStartValidityDChM; } -const long FITDCSConfigReader::getEndValidityDChM() const { return mStartValidityDChM + o2::ccdb::CcdbObjectInfo::MONTH; } +const long FITDCSConfigReader::getEndValidityDChM() const { return mStartValidityDChM + getValidDaysDChM() * o2::ccdb::CcdbObjectInfo::DAY; } void FITDCSConfigReader::setStartValidityDChM(const long startValidity) { mStartValidityDChM = startValidity; } const bool FITDCSConfigReader::isStartValidityDChMSet() const { return mStartValidityDChM != o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP; } void FITDCSConfigReader::resetStartValidityDChM() { mStartValidityDChM = o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP; } @@ -64,5 +65,11 @@ o2::ccdb::CcdbObjectInfo& FITDCSConfigReader::getObjectInfoDChM() { return mCcdb const std::string& FITDCSConfigReader::getFileNameDChM() const { return mFileNameDChM; } void FITDCSConfigReader::setFileNameDChM(const std::string& fileName) { mFileNameDChM = fileName; } +const uint FITDCSConfigReader::getValidDaysDChM() const { return mValidDaysDChM; } +void FITDCSConfigReader::setValidDaysDChM(const uint validDays) { mValidDaysDChM = validDays; } + const bool FITDCSConfigReader::getVerboseMode() const { return mVerbose; } -void FITDCSConfigReader::setVerboseMode(const bool verboseMode) { mVerbose = verboseMode; } \ No newline at end of file +void FITDCSConfigReader::setVerboseMode(const bool verboseMode) { mVerbose = verboseMode; } + +const bool FITDCSConfigReader::getValidateUploadMode() const { return mValidateUpload; } +void FITDCSConfigReader::setValidateUploadMode(const bool validateUpload) { mValidateUpload = validateUpload; }; \ No newline at end of file diff --git a/Detectors/FIT/macros/CMakeLists.txt b/Detectors/FIT/macros/CMakeLists.txt index c956392e5a1cd..a6bf1799a5dde 100644 --- a/Detectors/FIT/macros/CMakeLists.txt +++ b/Detectors/FIT/macros/CMakeLists.txt @@ -38,4 +38,16 @@ o2_add_test_root_macro(readFITDeadChannelMap.C o2_add_test_root_macro(readFITDCSdata.C PUBLIC_LINK_LIBRARIES O2::DetectorsDCS O2::CCDB - LABELS fit) \ No newline at end of file + LABELS fit) + +o2_add_test_root_macro(compareRecPoints.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsFT0 + O2::DataFormatsFIT + LABELS fit) + +o2_add_test_root_macro(readAlignParam.C + PUBLIC_LINK_LIBRARIES O2::CCDB + LABELS fit) + +o2_data_file(COPY readFITDCSdata.C DESTINATION Detectors/FIT/macros/) +o2_data_file(COPY readFITDeadChannelMap.C DESTINATION Detectors/FIT/macros/) \ No newline at end of file diff --git a/Detectors/FIT/macros/compareRecPoints.C b/Detectors/FIT/macros/compareRecPoints.C new file mode 100644 index 0000000000000..0ce077bc616ba --- /dev/null +++ b/Detectors/FIT/macros/compareRecPoints.C @@ -0,0 +1,110 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file compareRecPoints.C +/// \brief ROOT macro to compare two trees with RecPoints +/// +/// \author Artur Furs artur.furs@cern.ch, Andreas Molander andreas.molander@cern.ch + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "DataFormatsFT0/RecPoints.h" +#include "DataFormatsFV0/RecPoints.h" +#include "DataFormatsFDD/RecPoint.h" + +#include "TFile.h" +#include "TTree.h" + +#include +#include +#include +#include +#endif + +void compareRecPoints(std::string filename1, std::string filename2) +{ + std::unique_ptr file1(TFile::Open(filename1.c_str(), "READ")); + TTree* tree1 = (TTree*)file1->Get("o2sim"); + + std::unique_ptr file2(TFile::Open(filename2.c_str(), "READ")); + TTree* tree2 = (TTree*)file2->Get("o2sim"); + + if (tree1->GetEntries() != tree2->GetEntries()) { + std::cout << "Non equal number of entries in trees!" << std::endl; + return; + } + + typedef typename o2::ft0::RecPoints RecPoint; + typedef typename o2::ft0::ChannelDataFloat ChannelDataFloat; + + std::vector vecRecPoints1; + std::vector* ptrVecRecPoints1 = &vecRecPoints1; + + std::vector vecChannelDataFloat1; + std::vector* ptrVecChannelDataFloat1 = &vecChannelDataFloat1; + + tree1->SetBranchAddress("FT0Cluster", &ptrVecRecPoints1); + tree1->SetBranchAddress("FT0RecChData", &ptrVecChannelDataFloat1); + + std::vector vecRecPoints2; + std::vector* ptrVecRecPoints2 = &vecRecPoints2; + + std::vector vecChannelDataFloat2; + std::vector* ptrVecChannelDataFloat2 = &vecChannelDataFloat2; + + tree2->SetBranchAddress("FT0Cluster", &ptrVecRecPoints2); + tree2->SetBranchAddress("FT0RecChData", &ptrVecChannelDataFloat2); + + for (int iEntry = 0; iEntry < tree1->GetEntries(); iEntry++) { + tree1->GetEntry(iEntry); + tree2->GetEntry(iEntry); + + if (vecRecPoints1 != vecRecPoints2) { + std::cout << "Non equal RecPoints vector!" << std::endl; + + if (vecRecPoints1.size() == vecRecPoints2.size()) { + for (int iEvent = 0; iEvent < vecRecPoints1.size(); iEvent++) { + const auto& recPoint1 = vecRecPoints1[iEvent]; + const auto& recPoint2 = vecRecPoints2[iEvent]; + + if (!(recPoint1 == recPoint2)) { + std::cout << "First RecPoint" << std::endl; + recPoint1.print(); + std::cout << "Second RecPoint" << std::endl; + recPoint2.print(); + } + } + } else { + std::cout << "Non equal number of RecPoints!" << std::endl; + } + } + if (vecChannelDataFloat1 != vecChannelDataFloat2) { + std::cout << "Non equal ChannelDataFloat vector!" << std::endl; + + if (vecChannelDataFloat1.size() == vecChannelDataFloat2.size()) { + for (int iEvent = 0; iEvent < vecChannelDataFloat1.size(); iEvent++) { + const auto& channelDataFloat1 = vecChannelDataFloat1[iEvent]; + const auto& channelDataFloat2 = vecChannelDataFloat2[iEvent]; + + if (!(channelDataFloat1 == channelDataFloat2)) { + std::cout << "First ChannelDataFloat" << std::endl; + channelDataFloat1.print(); + std::cout << "Second ChannelDataFloat" << std::endl; + channelDataFloat2.print(); + } + } + } else { + std::cout << "Non equal number of ChannelDataFloat!" << std::endl; + } + } + } + + return; +} \ No newline at end of file diff --git a/Detectors/FIT/macros/readAlignParam.C b/Detectors/FIT/macros/readAlignParam.C new file mode 100644 index 0000000000000..c438e7a0c86a5 --- /dev/null +++ b/Detectors/FIT/macros/readAlignParam.C @@ -0,0 +1,51 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file readAlignParam.C +/// \brief ROOT macro for reading geometry alignment parameters +/// +/// \author Andreas Molander + +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include "CCDB/BasicCCDBManager.h" +#include "DetectorsCommonDataFormats/AlignParam.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsCommonDataFormats/DetectorNameConf.h" + +#include +#include + +#endif + +int readAlignParam(const std::string& detectorName = "FT0", + long timestamp = -1, + const std::string& ccdbUrl = "https://alice-ccdb.cern.ch") +{ + o2::ccdb::BasicCCDBManager& ccdbManager = o2::ccdb::BasicCCDBManager::instance(); + ccdbManager.setURL(ccdbUrl); + ccdbManager.setTimestamp(timestamp); + + const o2::detectors::DetID detID(detectorName.c_str()); + const std::string alignmentPath = o2::base::DetectorNameConf::getAlignmentPath(detID); + const auto alignments = ccdbManager.get>(alignmentPath); + + if (!alignments) { + std::cerr << "No alignment parameters found at " << alignmentPath << std::endl; + return 1; + } + + for (auto alignment : *alignments) { + alignment.print(); + } + + return 0; +} \ No newline at end of file diff --git a/Detectors/FIT/macros/readFITDCSdata.C b/Detectors/FIT/macros/readFITDCSdata.C index e531e393f8bf6..43f59ca8d0cfa 100644 --- a/Detectors/FIT/macros/readFITDCSdata.C +++ b/Detectors/FIT/macros/readFITDCSdata.C @@ -84,7 +84,7 @@ void readFITDCSdata(std::string detectorName = "FT0", const std::string& rootOutput = "", const bool print = false, const std::string& textOutput = "", - const bool verbose = true) + const bool verbose = false) { // Parse and check detector name boost::to_upper(detectorName); @@ -181,7 +181,7 @@ void readFITDCSdata(std::string detectorName = "FT0", } // The CCDB object should always contain values for all datapoints. This is just to check that. - if ((detectorName == "FT0" && ccdbMap->size() != 477) || (detectorName == "FV0" && ccdbMap->size() != 147) || (detectorName == "FDD" && ccdbMap->size() != 76)) { + if (verbose && ((detectorName == "FT0" && ccdbMap->size() != 501) || (detectorName == "FV0" && ccdbMap->size() != 147) || (detectorName == "FDD" && ccdbMap->size() != 76))) { LOGP(error, "Wrong number of DCS datapoints fetched for {}, got {}. There is a bug, please send output of this script, with input parameters, to andreas.molander@cern.ch.", detectorName, ccdbMap->size()); @@ -230,16 +230,19 @@ void readFITDCSdata(std::string detectorName = "FT0", LOG(info) << "Printing data point values:"; for (auto& it : dataSeries) { LOGP(info, "{}", it.first); - LOGP(info, "{} value(s):", it.second.values.size()); - if (verbose) { - for (auto& value : it.second.values) { - LOGP(info, "TIME = {} ({}), VALUE = {}", value.first, epochToReadable(value.first), value.second); + int nValues = it.second.values.size(); + LOGP(info, "{} value(s):", nValues); + if (nValues) { + if (verbose) { + for (auto& value : it.second.values) { + LOGP(info, "TIME = {} ({}), VALUE = {}", value.first, epochToReadable(value.first), value.second); + } + } else { + LOGP(info, "First value:"); + LOGP(info, "TIME = {} ({}), VALUE = {}", it.second.values.front().first, epochToReadable(it.second.values.front().first), it.second.values.front().second); + LOGP(info, "Last value:"); + LOGP(info, "TIME = {} ({}), VALUE = {}", it.second.values.back().first, epochToReadable(it.second.values.back().first), it.second.values.back().second); } - } else { - LOGP(info, "First value:"); - LOGP(info, "TIME = {} ({}), VALUE = {}", it.second.values.front().first, epochToReadable(it.second.values.front().first), it.second.values.front().second); - LOGP(info, "Last value:"); - LOGP(info, "TIME = {} ({}), VALUE = {}", it.second.values.back().first, epochToReadable(it.second.values.back().first), it.second.values.back().second); } LOG(info); } diff --git a/Detectors/FIT/macros/readFT0hits.C b/Detectors/FIT/macros/readFT0hits.C index 14d25fa4a99a8..fafcaac570311 100644 --- a/Detectors/FIT/macros/readFT0hits.C +++ b/Detectors/FIT/macros/readFT0hits.C @@ -1,13 +1,29 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + #if !defined(__CLING__) || defined(__ROOTCLING__) + +#include "DataFormatsFIT/Triggers.h" #include "DataFormatsFT0/Digit.h" #include "DataFormatsFT0/HitType.h" #include "SimulationDataFormat/MCEventHeader.h" #include +#include #include #include #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" +#endif + void readFT0hits() { @@ -24,6 +40,8 @@ void readFT0hits() TH2F* hPel = new TH2F("hPelDig", "N p.e. ", 220, 0, 220, 500, 0, 10000); TH2F* hXYA = new TH2F("hXYA", "X vs Y A side", 400, -20, 20, 400, -20, 20); TH2F* hXYC = new TH2F("hXYC", "X vs Y C side", 400, -20, 20, 400, -20, 20); + TH1F* hZA = new TH1F("hZA", "Z A side", 200, 330, 340); + TH1F* hZC = new TH1F("hZC", "Z C side", 200, -90, -80); gDirectory = cwd; @@ -59,10 +77,13 @@ void readFT0hits() hTimeHitA->Fill(detID, hit_time[detID] - 11.04); hTimeHitC->Fill(detID, hit_time[detID] - 2.91); countE[detID]++; - if (detID < 96) + if (detID < 96) { hXYA->Fill(hit.GetX(), hit.GetY()); - if (detID > 95) + hZA->Fill(hit.GetZ()); + } else { hXYC->Fill(hit.GetX(), hit.GetY()); + hZC->Fill(hit.GetZ()); + } } for (int ii = 0; ii < 220; ii++) { if (countE[ii] > 100) { @@ -82,6 +103,6 @@ void readFT0hits() hMultHit->Write(); hXYA->Write(); hXYC->Write(); - + hZA->Write(); + hZC->Write(); } // end of macro -#endif diff --git a/Detectors/FIT/macros/readFV0hits.C b/Detectors/FIT/macros/readFV0hits.C index 5b0dfa8428dc7..933138fb1434b 100644 --- a/Detectors/FIT/macros/readFV0hits.C +++ b/Detectors/FIT/macros/readFV0hits.C @@ -1,3 +1,14 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -22,6 +33,8 @@ #include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "DetectorsCommonDataFormats/DetID.h" +#endif + void AdjustStatBox(TH1* h, float x1ndc, float x2ndc, float y1ndc, float y2ndc) { gPad->Update(); @@ -54,6 +67,9 @@ void InitHistoNames(std::vector& vhName, std::vector& vPdg) vhName.push_back("hElossDet"); vhName.push_back("hEtotVsR"); vhName.push_back("hEtotVsEloss"); + vhName.push_back("hXY"); + vhName.push_back("hXYzoom"); + vhName.push_back("hZ"); for (UInt_t ipdg = 0; ipdg < vPdg.size(); ipdg++) { std::stringstream ss; @@ -63,7 +79,7 @@ void InitHistoNames(std::vector& vhName, std::vector& vPdg) } } -void readFV0Hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) +void readFV0hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) { using namespace o2::detectors; std::string simFName(o2::base::DetectorNameConf::getHitsFileName(DetID::FV0, simPrefix)); @@ -85,6 +101,9 @@ void readFV0Hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) TH2F* hElossDet = new TH2F(vHistoNames.at(8).c_str(), "", nEl, 0, el1, nCells, 0, nCells); TH2F* hEtotVsR = new TH2F(vHistoNames.at(9).c_str(), "", 30000, 0, 300, 80, 0, 80); TH2F* hEtotVsEloss = new TH2F(vHistoNames.at(10).c_str(), "", 30000, 0, 300, nEl, 0, el1); + TH2F* hXY = new TH2F(vHistoNames.at(11).c_str(), "", 200, -100, 100, 200, -100, 100); + TH2F* hXYzoom = new TH2F(vHistoNames.at(12).c_str(), "", 200, -20, 20, 200, -20, 20); + TH1F* hZ = new TH1F(vHistoNames.at(13).c_str(), "", 200, 315, 325); // Setup histo properties hElossDet->SetXTitle("Energy loss [MeV]"); @@ -96,6 +115,14 @@ void readFV0Hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) hEtotVsEloss->SetXTitle("Total energy at entrance [MeV]"); hEtotVsEloss->SetYTitle("Energy loss [MeV]"); hEtotVsEloss->SetZTitle("Counts"); + hXY->SetXTitle("X [cm]"); + hXY->SetYTitle("Y [cm]"); + hXY->SetZTitle("Counts"); + hXYzoom->SetXTitle("X [cm]"); + hXYzoom->SetYTitle("Y [cm]"); + hXYzoom->SetZTitle("Counts"); + hZ->SetXTitle("Hit Z-coordinate [cm]"); + hZ->SetYTitle("Counts"); for (UInt_t ih = 0; ih < vhElossVsDistance.size(); ih++) { TH2F* h = vhElossVsDistance.at(ih); std::stringstream ss; @@ -124,6 +151,9 @@ void readFV0Hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) vh.push_back(hEtotVsEloss); vh.insert(vh.end(), vhElossVsDistance.begin(), vhElossVsDistance.end()); vh.insert(vh.end(), vhElossVsEtot.begin(), vhElossVsEtot.end()); + vh.push_back(hXY); + vh.push_back(hXYzoom); + vh.push_back(hZ); for (UInt_t ih = 0; ih < vh.size(); ih++) { vh[ih]->SetDirectory(0); vh[ih]->GetXaxis()->SetTitleSize(fontsize); @@ -177,6 +207,9 @@ void readFV0Hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) vhElossVsDistance.at(vhElossVsDistance.size() - 1)->Fill(hit->GetEnergyLoss() * 1e3, distance); vhElossVsEtot.at(vhElossVsEtot.size() - 1)->Fill(hit->GetEnergyLoss() * 1e3, hit->GetTotalEnergyAtEntrance() * 1e3); } + hXY->Fill(hit->GetX(), hit->GetY()); + hXYzoom->Fill(hit->GetX(), hit->GetY()); + hZ->Fill(hit->GetZ()); } } @@ -323,5 +356,3 @@ int compareFV0Hits(std::string simFName1 = "fv0hit-rawhistos.root", std::string } return 0; } - -#endif diff --git a/Detectors/FIT/raw/CMakeLists.txt b/Detectors/FIT/raw/CMakeLists.txt index 9090f54029017..d8ab9964dbee4 100644 --- a/Detectors/FIT/raw/CMakeLists.txt +++ b/Detectors/FIT/raw/CMakeLists.txt @@ -10,5 +10,19 @@ # or submit itself to any jurisdiction. o2_add_library(FITRaw - SOURCES src/DataBlockBase.cxx src/DataBlockFIT.cxx src/DigitBlockBase.cxx src/DigitBlockFIT.cxx src/RawReaderBase.cxx src/RawReaderBaseFIT.cxx src/RawWriterFIT.cxx - PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers Microsoft.GSL::GSL O2::DetectorsRaw O2::CommonUtils) + SOURCES src/DataBlockBase.cxx + src/DataBlockFIT.cxx + src/DigitBlockBase.cxx + src/DigitBlockFIT.cxx + src/RawReaderBase.cxx + src/RawReaderBaseFIT.cxx + src/RawWriterFIT.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + O2::Headers + Microsoft.GSL::GSL + O2::DetectorsRaw + O2::CommonUtils + O2::DataFormatsFIT + O2::DetectorsCommonDataFormats + O2::DataFormatsParameters + Boost::program_options) diff --git a/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h b/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h index 6320ab4d64a82..1c95f354c6292 100644 --- a/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h +++ b/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h @@ -41,6 +41,7 @@ #include #include #include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsFIT/RawDataMetric.h" #include "Headers/RAWDataHeader.h" #include #include @@ -57,8 +58,6 @@ namespace o2 namespace fit { -using namespace std; - static constexpr size_t SIZE_WORD = 16; static constexpr size_t SIZE_WORD_GBT = 10; // should be changed to gloabal variable static constexpr size_t SIZE_MAX_PAGE = 8192; // should be changed to gloabal variable @@ -145,49 +144,50 @@ struct DataBlockWrapper { constexpr static std::size_t sSizeWord = getWordSize(); void serialize(std::vector& vecBytes, size_t nWords, size_t& destPos) const { - const uint8_t* srcAddress = (uint8_t*)mData; - if (nWords == 0 || nWords > MaxNwords || vecBytes.size() - destPos < nWords * sSizeWord) { + const auto nBytesToWrite = nWords * sSizeWord; + if ((vecBytes.size() - destPos) < nBytesToWrite || nWords < MinNwords || nWords > MaxNwords) { LOG(info) << "Warning! Incorrect serialisation procedure!"; return; + } else if (nWords == 0) { + // nothing to do + return; } + const uint8_t* srcAddress = (uint8_t*)mData; gsl::span serializedBytes(vecBytes); if constexpr (sIsPadded) { // in case of padding one need to use byte map int nSteps = std::get(sReadingLookupTable[nWords]); for (int iStep = 0; iStep < nSteps; iStep++) { memcpy(serializedBytes.data() + std::get(sByteLookupTable[iStep]) + destPos, srcAddress + std::get(sByteLookupTable[iStep]), std::get(sByteLookupTable[iStep])); } - destPos += nWords * sSizeWord; } else { // no need in byte map - const std::size_t nBytes = nWords * sSizeWord; - memcpy(serializedBytes.data() + destPos, srcAddress, nBytes); - destPos += nBytes; + memcpy(serializedBytes.data() + destPos, srcAddress, nBytesToWrite); } + destPos += nBytesToWrite; } void deserialize(const gsl::span inputBytes, size_t nWords, size_t& srcPos) { mNelements = 0; - mNwords = 0; - if (nWords < MinNwords || nWords > MaxNwords || inputBytes.size() - srcPos < nWords * sSizeWord) { + const auto nBytesToRead = nWords * sSizeWord; + if ((inputBytes.size() - srcPos) < nBytesToRead || nWords < MinNwords || nWords > MaxNwords) { // in case of bad fields responsible for deserialization logic, byte position will be pushed to the end of binary sequence + LOG(error) << "Incomplete payload! |N GBT words " << nWords << " |bytes to read " << nBytesToRead << " |src pos " << srcPos << " |payload size " << inputBytes.size() << " |"; srcPos = inputBytes.size(); + RawDataMetric::setStatusBit(mStatusBits, RawDataMetric::EStatusBits::kIncompletePayload); mIsIncorrect = true; return; } uint8_t* destAddress = (uint8_t*)mData; - mNwords = nWords; mNelements = std::get(sReadingLookupTable[nWords]); if constexpr (sIsPadded) { // in case of padding one need to use byte map int nSteps = std::get(sReadingLookupTable[nWords]); for (int iStep = 0; iStep < nSteps; iStep++) { memcpy(destAddress + std::get(sByteLookupTable[iStep]), inputBytes.data() + std::get(sByteLookupTable[iStep]) + srcPos, std::get(sByteLookupTable[iStep])); } - srcPos += mNwords * sSizeWord; } else { // no need in byte map - const std::size_t nBytes = mNwords * sSizeWord; - memcpy(destAddress, inputBytes.data() + srcPos, nBytes); - srcPos += nBytes; + memcpy(destAddress, inputBytes.data() + srcPos, nBytesToRead); } + srcPos += nBytesToRead; } static constexpr int MaxNwords = Data_t::PayloadSize * Data_t::MaxNelements / Data_t::PayloadPerGBTword + (Data_t::PayloadSize * Data_t::MaxNelements % Data_t::PayloadPerGBTword > 0); // calculating max GBT words per block @@ -376,8 +376,8 @@ struct DataBlockWrapper { } } Data_t mData[Data_t::MaxNelements]; - unsigned int mNelements; // number of deserialized elements; - unsigned int mNwords; // number of deserialized GBT words; //can be excluded + unsigned int mNelements; // number of deserialized elements; + typename RawDataMetric::Status_t mStatusBits{}; // Contains status bits bool mIsIncorrect; }; @@ -445,16 +445,20 @@ class DataBlockBase : public boost::mpl::inherit::mIsIncorrect), ...); // checking deserialization status for sub-block - static_cast(this)->sanityCheck(mIsCorrect); + mergeStatusBits(mStatusBitsAll, DataBlockWrapperHeader_t::mStatusBits); // checking deserialization status for header + (mergeStatusBits(mStatusBitsAll, DataBlockWrapper::mStatusBits), ...); // checking deserialization status for sub-block + static_cast(this)->sanityCheck(mIsCorrect, mStatusBitsAll); } - size_t mSize; // deserialized size - bool mIsCorrect; + size_t mSize{0}; // deserialized size + bool mIsCorrect{false}; + typename RawDataMetric::Status_t mStatusBitsAll{}; protected: // check if there are sub blocks with zero number of elements void isNonZeroBlockSizes(bool& flag, unsigned int nElements) { flag &= (bool)nElements; } void checkDeserialization(bool& flag, bool isIncorrect) { flag &= !(isIncorrect); } + void mergeStatusBits(uint8_t& statusBitsResult, uint8_t statusBits) { statusBitsResult |= statusBits; } }; } // namespace fit diff --git a/Detectors/FIT/raw/include/FITRaw/DataBlockFIT.h b/Detectors/FIT/raw/include/FITRaw/DataBlockFIT.h index c00e66cbac17e..676ed276f675f 100644 --- a/Detectors/FIT/raw/include/FITRaw/DataBlockFIT.h +++ b/Detectors/FIT/raw/include/FITRaw/DataBlockFIT.h @@ -20,11 +20,11 @@ #include #include #include +#include #include #include #include -using namespace o2::fit; namespace o2 { namespace fit @@ -52,18 +52,31 @@ class DataBlockPM : public DataBlockBase serialize() const { std::size_t nBytes = HeaderPM::MaxNwords * HeaderPM::sSizeWord; - nBytes += HeaderPM::mData[0].nGBTWords * HeaderPM::sSizeWord; + const std::size_t nGBTWords = HeaderPM::mData[0].nGBTWords; + nBytes += nGBTWords * HeaderPM::sSizeWord; std::vector vecBytes(nBytes); std::size_t destBytes = 0; - HeaderPM::serialize(vecBytes, HeaderPM::MaxNwords, destBytes); - DataPM::serialize(vecBytes, HeaderPM::mData[0].nGBTWords, destBytes); + const auto statusBits = HeaderPM::mStatusBits | DataPM::mStatusBits; + if (RawDataMetric::isBitActive(statusBits, RawDataMetric::EStatusBits::kIncompletePayload)) { + // Emulation for IncompletePayload + auto dataBlockTmp = (*this); + dataBlockTmp.HeaderPM::mData[0].nGBTWords = DataPM::MaxNwords + 1; + dataBlockTmp.HeaderPM::serialize(vecBytes, HeaderPM::MaxNwords, destBytes); + } else { + HeaderPM::serialize(vecBytes, HeaderPM::MaxNwords, destBytes); + } + DataPM::serialize(vecBytes, nGBTWords, destBytes); return vecBytes; } // Custom sanity checking for current deserialized block // put here code for raw data checking - void sanityCheck(bool& flag) + void sanityCheck(bool& flag, typename RawDataMetric::Status_t& metric) { + if (HeaderPM::mData[0].isBadDescriptor()) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kWrongDescriptor); + } if (DataPM::mNelements == 0) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kEmptyDataBlock); flag = false; return; } @@ -95,18 +108,34 @@ class DataBlockTCM : public DataBlockBase serialize() const { std::size_t nBytes = HeaderTCM::MaxNwords * HeaderTCM::sSizeWord; - nBytes += HeaderTCM::mData[0].nGBTWords * HeaderTCM::sSizeWord; + const std::size_t nGBTWords = HeaderTCM::mData[0].nGBTWords; + nBytes += nGBTWords * HeaderTCM::sSizeWord; std::vector vecBytes(nBytes); std::size_t destBytes = 0; - HeaderTCM::serialize(vecBytes, HeaderTCM::MaxNwords, destBytes); - DataTCM::serialize(vecBytes, HeaderTCM::mData[0].nGBTWords, destBytes); + const auto statusBits = HeaderTCM::mStatusBits | DataTCM::mStatusBits; + if (RawDataMetric::isBitActive(statusBits, RawDataMetric::EStatusBits::kIncompletePayload)) { + // Emulation for IncompletePayload + auto dataBlockTmp = (*this); + dataBlockTmp.HeaderTCM::mData[0].nGBTWords = DataTCM::MaxNwords + 1; + dataBlockTmp.HeaderTCM::serialize(vecBytes, HeaderTCM::MaxNwords, destBytes); + } else { + HeaderTCM::serialize(vecBytes, HeaderTCM::MaxNwords, destBytes); + } + DataTCM::serialize(vecBytes, nGBTWords, destBytes); return vecBytes; } // Custom sanity checking for current deserialized block // put here code for raw data checking - void sanityCheck(bool& flag) + void sanityCheck(bool& flag, typename RawDataMetric::Status_t& metric) { - // TODO, Descriptor checking + if (HeaderTCM::mData[0].isBadDescriptor()) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kWrongDescriptor); + } + if (DataTCM::mNelements == 0) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kEmptyDataBlock); + flag = false; + return; + } } }; @@ -135,20 +164,34 @@ class DataBlockTCMext : public DataBlockBase serialize() const { std::size_t nBytes = HeaderTCMext::MaxNwords * HeaderTCMext::sSizeWord; - nBytes += HeaderTCMext::mData[0].nGBTWords * HeaderTCMext::sSizeWord; + const std::size_t nGBTWords = HeaderTCMext::mData[0].nGBTWords; + nBytes += nGBTWords * HeaderTCMext::sSizeWord; std::vector vecBytes(nBytes); std::size_t destBytes = 0; - HeaderTCMext::serialize(vecBytes, HeaderTCMext::MaxNwords, destBytes); + const auto statusBits = HeaderTCMext::mStatusBits | DataTCM::mStatusBits; + if (RawDataMetric::isBitActive(statusBits, RawDataMetric::EStatusBits::kIncompletePayload)) { + // Emulation for IncompletePayload + auto dataBlockTmp = (*this); + dataBlockTmp.HeaderTCMext::mData[0].nGBTWords = DataTCM::MaxNwords + 1; + dataBlockTmp.HeaderTCMext::serialize(vecBytes, HeaderTCMext::MaxNwords, destBytes); + } else { + HeaderTCMext::serialize(vecBytes, HeaderTCMext::MaxNwords, destBytes); + } DataTCM::serialize(vecBytes, DataTCM::MaxNwords, destBytes); - DataTCMext::serialize(vecBytes, HeaderTCMext::mData[0].nGBTWords - DataTCM::MaxNwords, destBytes); + DataTCMext::serialize(vecBytes, nGBTWords - DataTCM::MaxNwords, destBytes); return vecBytes; } // Custom sanity checking for current deserialized block // put here code for raw data checking - void sanityCheck(bool& flag) + void sanityCheck(bool& flag, typename RawDataMetric::Status_t& metric) { - - // TODO, Descriptor checking + if (HeaderTCMext::mData[0].isBadDescriptor()) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kWrongDescriptor); + } + if (DataTCMext::mNelements == 0) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kEmptyDataBlock); + flag = false; + } } }; diff --git a/Detectors/FIT/raw/include/FITRaw/DigitBlockFIT.h b/Detectors/FIT/raw/include/FITRaw/DigitBlockFIT.h index 3f9419b1dfb04..3afd0e6f70976 100644 --- a/Detectors/FIT/raw/include/FITRaw/DigitBlockFIT.h +++ b/Detectors/FIT/raw/include/FITRaw/DigitBlockFIT.h @@ -24,6 +24,7 @@ #include "FITRaw/DigitBlockBase.h" #include +#include #include "TTree.h" @@ -102,8 +103,8 @@ auto ConvertTCMData2Digit(DigitType& digit, const TCMDataType& tcmData) // PM to ChannelData convertation // FT0 and FV0 -template -auto ConvertEventData2ChData(std::vector& vecChData, const PMDataType& pmData, int linkID, int ep) -> std::enable_if_t().QTCAmpl), int16_t>::value> +template +auto ConvertEventData2ChData(std::vector& vecChData, const PMDataType& pmData, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t().QTCAmpl), int16_t>::value> { bool isValid{}; const auto globalChID = LookupTableType::Instance().getChannel(linkID, ep, pmData.channelID, isValid); @@ -111,20 +112,22 @@ auto ConvertEventData2ChData(std::vector& vecChData, const PMDa vecChData.emplace_back(static_cast(globalChID), static_cast(pmData.time), static_cast(pmData.charge), static_cast(pmData.pmBits)); } else { static int warningCount = 0; + metric.addStatusBit(RawDataMetricType::EStatusBits::kWrongChannelMapping); if (warningCount++ < 100) { LOG(warning) << "Incorrect global channel! linkID: " << linkID << " | EndPoint: " << ep << " | LocalChID: " << static_cast(pmData.channelID); } } } // FDD -template -auto ConvertEventData2ChData(std::vector& vecChData, const PMDataType& pmData, int linkID, int ep) -> std::enable_if_t().mChargeADC), int16_t>::value> +template +auto ConvertEventData2ChData(std::vector& vecChData, const PMDataType& pmData, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t().mChargeADC), int16_t>::value> { bool isValid{}; const auto globalChID = LookupTableType::Instance().getChannel(linkID, ep, pmData.channelID, isValid); if (isValid) { vecChData.emplace_back(static_cast(globalChID), static_cast(pmData.time), static_cast(pmData.charge), static_cast(pmData.pmBits)); } else { + metric.addStatusBit(RawDataMetricType::EStatusBits::kWrongChannelMapping); static int warningCount = 0; if (warningCount++ < 100) { LOG(warning) << "Incorrect global channel! linkID: " << linkID << " | EndPoint: " << ep << " | LocalChID: " << static_cast(pmData.channelID); @@ -155,18 +158,18 @@ class DigitBlockFIT : public DigitBlockBase DigitBlockFIT(const DigitBlockFIT& other) = default; ~DigitBlockFIT() = default; // Filling data from PM - template - auto processDigits(const DataBlockType& dataBlock, int linkID, int ep) -> std::enable_if_t::value> + template + auto processDigits(const DataBlockType& dataBlock, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t::value> { const int nElements = dataBlock.DataPM::mNelements; for (int iEventData = 0; iEventData < nElements; iEventData++) { const auto& pmData = dataBlock.DataPM::mData[iEventData]; - DigitBlockFIThelper::ConvertEventData2ChData(DigitBlockBase_t::mSubDigit, pmData, linkID, ep); + DigitBlockFIThelper::ConvertEventData2ChData(DigitBlockBase_t::mSubDigit, pmData, metric, linkID, ep); } } // Filling data from TCM (normal mode) - template - auto processDigits(const DataBlockType& dataBlock, int linkID, int ep) -> std::enable_if_t::value> + template + auto processDigits(const DataBlockType& dataBlock, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t::value> { auto& tcmData = dataBlock.DataTCM::mData[0]; @@ -175,7 +178,7 @@ class DigitBlockFIT : public DigitBlockBase // Decompose digits into DataBlocks // DataBlockPM template - auto decomposeDigits() const -> std::enable_if_t::value, std::map> + auto decomposeDigits(const typename RawDataMetric::Status_t& status) const -> std::enable_if_t::value, std::map> { using Topo_t = typename LookupTable_t::Topo_t; @@ -195,32 +198,62 @@ class DigitBlockFIT : public DigitBlockBase DataBlockType& refDataBlock = pairInserted.first->second; if (pairInserted.second) { // Header preparation + refDataBlock.HeaderPM::mStatusBits |= status; refDataBlock.HeaderPM::mData[0].setIntRec(DigitBlockFIThelper::GetIntRecord(DigitBlockBase_t::mDigit)); - refDataBlock.HeaderPM::mData[0].startDescriptor = 0xf; + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kWrongDescriptor)) { + // Emulation for WrongDescriptor + refDataBlock.HeaderPM::mData[0].startDescriptor = 0x0; + } else { + refDataBlock.HeaderPM::mData[0].startDescriptor = 0xf; + } std::size_t nElements = mapTopoCounter.find(pairInserted.first->first)->second; std::size_t nWords = nElements / 2 + nElements % 2; - refDataBlock.HeaderPM::mData[0].nGBTWords = nWords; + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kEmptyDataBlock)) { + // Emulation for EmptyDataBlock + refDataBlock.HeaderPM::mData[0].nGBTWords = 0; + } else { + refDataBlock.HeaderPM::mData[0].nGBTWords = nWords; + } refDataBlock.HeaderPM::mNelements = 1; } // Data preparation + refDataBlock.DataPM::mStatusBits |= status; auto& refPos = refDataBlock.DataPM::mNelements; auto& refData = refDataBlock.DataPM::mData[refPos]; refPos++; - DigitBlockFIThelper::ConvertChData2EventData(entry.second.get(), refData, LookupTable_t::Instance().getLocalChannelID(entry.first)); + int localChannelID = LookupTable_t::Instance().getLocalChannelID(entry.first); + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kWrongChannelMapping)) { + // Emulation for WrongChannelMapping + localChannelID = 0xf; + } + DigitBlockFIThelper::ConvertChData2EventData(entry.second.get(), refData, localChannelID); } return mapResult; } // DataBlockTCM template - auto decomposeDigits() const -> std::enable_if_t::value, std::pair> + auto decomposeDigits(const typename RawDataMetric::Status_t& status) const -> std::enable_if_t::value, std::pair> { DataBlockType dataBlockTCM{}; // Header preparation + dataBlockTCM.HeaderTCM::mStatusBits |= status; dataBlockTCM.HeaderTCM::mData[0].setIntRec(DigitBlockFIThelper::GetIntRecord(DigitBlockBase_t::mDigit)); - dataBlockTCM.HeaderTCM::mData[0].startDescriptor = 0xf; - dataBlockTCM.HeaderTCM::mData[0].nGBTWords = dataBlockTCM.DataTCM::MaxNwords; + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kWrongDescriptor)) { + // Emulation for WrongDescriptor + dataBlockTCM.HeaderTCM::mData[0].startDescriptor = 0x0; + } else { + dataBlockTCM.HeaderTCM::mData[0].startDescriptor = 0xf; + } + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kEmptyDataBlock)) { + // Emulation for EmptyDataBlock + dataBlockTCM.HeaderTCM::mData[0].nGBTWords = 0; + } else { + dataBlockTCM.HeaderTCM::mData[0].nGBTWords = 1; + } + dataBlockTCM.HeaderTCM::mNelements = 1; auto& refTCMdata = dataBlockTCM.DataTCM::mData[0]; + dataBlockTCM.DataTCM::mStatusBits |= status; dataBlockTCM.DataTCM::mNelements = 1; // Data preparation DigitBlockFIThelper::ConvertDigit2TCMData(DigitBlockBase_t::mDigit, refTCMdata); @@ -240,7 +273,7 @@ class DigitBlockFIT : public DigitBlockBase inputTree->SetBranchAddress(decltype(vecChannelData)::value_type::sDigitBranchName, &ptrVecChannelData); for (int iEntry = 0; iEntry < inputTree->GetEntries(); iEntry++) { inputTree->GetEntry(iEntry); - LOG(info) << "Processing TF " << iEntry; + LOG(detail) << "Processing TF " << iEntry; digitBlockProc.processDigitBlockPerTF(DigitBlockBase_t::template makeDigitBlock(vecDigit, vecChannelData)); } } @@ -289,18 +322,18 @@ class DigitBlockFIText : public DigitBlockBase - auto processDigits(const DataBlockType& dataBlock, int linkID, int ep) -> std::enable_if_t::value> + template + auto processDigits(const DataBlockType& dataBlock, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t::value> { const int nElements = dataBlock.DataPM::mNelements; for (int iEventData = 0; iEventData < nElements; iEventData++) { const auto& pmData = dataBlock.DataPM::mData[iEventData]; - DigitBlockFIThelper::ConvertEventData2ChData(DigitBlockBase_t::mSubDigit, pmData, linkID, ep); + DigitBlockFIThelper::ConvertEventData2ChData(DigitBlockBase_t::mSubDigit, pmData, metric, linkID, ep); } } // Filling data from TCM (extended mode) - template - auto processDigits(const DataBlockType& dataBlock, int linkID, int ep) -> std::enable_if_t::value> + template + auto processDigits(const DataBlockType& dataBlock, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t::value> { auto& tcmData = dataBlock.DataTCM::mData[0]; DigitBlockFIThelper::ConvertTCMData2Digit(DigitBlockBase_t::mDigit, tcmData); @@ -313,7 +346,7 @@ class DigitBlockFIText : public DigitBlockBase - auto decomposeDigits() const -> std::enable_if_t::value, std::map> + auto decomposeDigits(const typename RawDataMetric::Status_t& status) const -> std::enable_if_t::value, std::map> { using Topo_t = typename LookupTable_t::Topo_t; std::map mapResult; @@ -333,29 +366,54 @@ class DigitBlockFIText : public DigitBlockBasefirst)->second; std::size_t nWords = nElements / 2 + nElements % 2; - refDataBlock.HeaderPM::mData[0].nGBTWords = nWords; + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kEmptyDataBlock)) { + // Emulation for EmptyDataBlock + refDataBlock.HeaderPM::mData[0].nGBTWords = 0; + } else { + refDataBlock.HeaderPM::mData[0].nGBTWords = nWords; + } refDataBlock.HeaderPM::mNelements = 1; } // Data preparation auto& refPos = refDataBlock.DataPM::mNelements; auto& refData = refDataBlock.DataPM::mData[refPos]; refPos++; - DigitBlockFIThelper::ConvertChData2EventData(entry.second.get(), refData, LookupTable_t::Instance().getLocalChannelID(entry.first)); + int localChannelID = LookupTable_t::Instance().getLocalChannelID(entry.first); + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kWrongChannelMapping)) { + // Emulation for WrongChannelMapping + localChannelID = 0xf; + } + DigitBlockFIThelper::ConvertChData2EventData(entry.second.get(), refData, localChannelID); } return mapResult; } // DataBlockTCM template - auto decomposeDigits() const -> std::enable_if_t::value, std::pair> + auto decomposeDigits(const typename RawDataMetric::Status_t& status) const -> std::enable_if_t::value, std::pair> { DataBlockType dataBlockTCM{}; // Header preparation dataBlockTCM.HeaderTCMext::mData[0].setIntRec(DigitBlockFIThelper::GetIntRecord(DigitBlockBase_t::mDigit)); - dataBlockTCM.HeaderTCMext::mData[0].startDescriptor = 0xf; - dataBlockTCM.HeaderTCMext::mData[0].nGBTWords = dataBlockTCM.DataTCM::MaxNwords + dataBlockTCM.DataTCMext::MaxNwords; + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kWrongDescriptor)) { + // Emulation for WrongDescriptor + dataBlockTCM.HeaderTCMext::mData[0].startDescriptor = 0x0; + } else { + dataBlockTCM.HeaderTCMext::mData[0].startDescriptor = 0xf; + } + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kEmptyDataBlock)) { + // Emulation for EmptyDataBlock + dataBlockTCM.HeaderTCMext::mData[0].nGBTWords = 0; + } else { + dataBlockTCM.HeaderTCMext::mData[0].nGBTWords = 1; + } dataBlockTCM.HeaderTCMext::mNelements = 1; auto& refTCMdata = dataBlockTCM.DataTCM::mData[0]; // Data preparation diff --git a/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h b/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h index 5edf7ceec4757..4c281c5559ee1 100644 --- a/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h +++ b/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,7 @@ #include "Headers/RAWDataHeader.h" #include #include +#include #include namespace o2 @@ -47,9 +49,13 @@ class RawReaderBase RawReaderBase() = default; ~RawReaderBase() = default; typedef DigitBlockType DigitBlock_t; + typedef RawDataMetric RawDataMetric_t; + using LookupTable_t = typename DigitBlock_t::LookupTable_t; + using EntryCRU_t = typename LookupTable_t::EntryCRU_t; + using HashTableCRU_t = std::unordered_map; + + HashTableCRU_t mHashTableMetrics{}; typedef boost::mpl::vector VecDataBlocks_t; - // typedef boost::mpl::vector VecDataBlocks_t; - // std::tuple...> mTupleVecDataBlocks; std::tuple..., std::vector...> mTupleVecDataBlocks; std::map mMapDigits; @@ -61,27 +67,19 @@ class RawReaderBase } // decoding binary data into data blocks template - size_t decodeBlocks(const gsl::span binaryPayload, std::vector& vecDataBlocks) + size_t decodeBlocks(const gsl::span binaryPayload, RawDataMetric& metric, std::vector& vecDataBlocks) { size_t srcPos = 0; - const auto payloadSize = binaryPayload.size(); - const int padding = payloadSize % DataBlockType::DataBlockWrapperHeader_t::sSizeWord; - const int nCruWords = payloadSize / SIZE_WORD; - const int pageSizeThreshold = SIZE_WORD * (nCruWords - int(padding > 0)); + const auto& payloadSize = binaryPayload.size(); + const int nWords = payloadSize / DataBlockType::DataBlockWrapperHeader_t::sSizeWord; + const int pageSizeThreshold = DataBlockType::DataBlockWrapperHeader_t::sSizeWord * (nWords - int(nWords > 1)); // no need in reading last GBT word, this will be 0xff... or empty header while (srcPos < pageSizeThreshold) { auto& refDataBlock = vecDataBlocks.emplace_back(); refDataBlock.decodeBlock(binaryPayload, srcPos); srcPos += refDataBlock.mSize; - if (refDataBlock.isOnlyHeader()) { + if (metric.checkBadDataBlock(refDataBlock.mStatusBitsAll)) { // exclude data block in case of single header(no data, total size == 16 bytes) vecDataBlocks.pop_back(); - continue; - } - if (!refDataBlock.isCorrect()) { - LOG(warning) << "INCORRECT DATA BLOCK! Byte position: " << srcPos - refDataBlock.mSize << " | Payload size: " << binaryPayload.size() << " | DataBlock size: " << refDataBlock.mSize; - refDataBlock.print(); - vecDataBlocks.pop_back(); - return srcPos; } } return srcPos; @@ -89,32 +87,24 @@ class RawReaderBase // processing data blocks into digits template - void processBinaryData(gsl::span payload, T&&... feeParameters) + void processBinaryData(gsl::span payload, uint16_t feeID, uint8_t linkID, uint8_t epID) { auto& vecDataBlocks = getVecDataBlocks(); - auto srcPos = decodeBlocks(payload, vecDataBlocks); + auto& metric = addMetric(feeID, linkID, epID); + auto srcPos = decodeBlocks(payload, metric, vecDataBlocks); for (const auto& dataBlock : vecDataBlocks) { auto intRec = dataBlock.getInteractionRecord(); auto [digitIter, isNew] = mMapDigits.try_emplace(intRec, intRec); - digitIter->second.template processDigits(dataBlock, std::forward(feeParameters)...); + digitIter->second.template processDigits(dataBlock, metric, static_cast(linkID), static_cast(epID)); + metric.addStatusBit(RawDataMetric::EStatusBits::kDecodedDataBlock); } vecDataBlocks.clear(); - /* //Must be checked for perfomance - std::size_t srcPos = 0; - while (srcPos < payload.size()) { - DataBlockType dataBlock{}; - dataBlock.decodeBlock(payload, srcPos); - srcPos += dataBlock.mSize; - if (!dataBlock.isCorrect()) { - LOG(warning) << "INCORRECT DATA BLOCK! Byte position: " << srcPos << " | Payload size: " << payload.size() << " | DataBlock size: " << dataBlock.mSize; - //dataBlock.print(); - return; - } - auto intRec = dataBlock.getInteractionRecord(); - auto [digitIter, isNew] = mMapDigits.try_emplace(intRec, intRec); - digitIter->second.template processDigits(dataBlock, std::forward(feeParameters)...); - } - */ + } + RawDataMetric& addMetric(uint16_t feeID, uint8_t linkID, uint8_t epID, bool isRegisteredFEE = true) + { + auto metricPair = mHashTableMetrics.try_emplace(EntryCRU_t{static_cast(linkID), static_cast(epID)}, linkID, epID, feeID, isRegisteredFEE); + auto& metric = metricPair.first->second; + return metric; } // pop digits template @@ -127,6 +117,13 @@ class RawReaderBase mMapDigits.clear(); return digitCounter; } + void getMetrics(std::vector& vecMetrics) + { + for (const auto& en : mHashTableMetrics) { + vecMetrics.push_back(en.second); + } + mHashTableMetrics.clear(); + } private: // Check for unique DataBlock classes diff --git a/Detectors/FIT/raw/include/FITRaw/RawReaderBaseFIT.h b/Detectors/FIT/raw/include/FITRaw/RawReaderBaseFIT.h index 09b43cca24607..7a5c98aa1834c 100644 --- a/Detectors/FIT/raw/include/FITRaw/RawReaderBaseFIT.h +++ b/Detectors/FIT/raw/include/FITRaw/RawReaderBaseFIT.h @@ -25,6 +25,8 @@ #include "FITRaw/RawReaderBase.h" #include #include "Headers/RAWDataHeader.h" +#include +#include #include @@ -64,22 +66,27 @@ class RawReaderBaseFIT : public RawReaderBase - void process(bool isPadded, gsl::span payload, T&&... feeParameters) + void process(bool isPadded, gsl::span payload, uint16_t feeID, T&&... feeParameters) { if (LookupTable_t::Instance().isTCM(std::forward(feeParameters)...)) { // TCM data proccessing if (isPadded) { - RawReaderBase_t::template processBinaryData(payload, std::forward(feeParameters)...); + RawReaderBase_t::template processBinaryData(payload, feeID, std::forward(feeParameters)...); } else { - RawReaderBase_t::template processBinaryData(payload, std::forward(feeParameters)...); + RawReaderBase_t::template processBinaryData(payload, feeID, std::forward(feeParameters)...); } - } else { + } else if (LookupTable_t::Instance().isPM(std::forward(feeParameters)...)) { // PM data proccessing if (isPadded) { - RawReaderBase_t::template processBinaryData(payload, std::forward(feeParameters)...); + RawReaderBase_t::template processBinaryData(payload, feeID, std::forward(feeParameters)...); } else { - RawReaderBase_t::template processBinaryData(payload, std::forward(feeParameters)...); + RawReaderBase_t::template processBinaryData(payload, feeID, std::forward(feeParameters)...); } + } else { + auto& metric = RawReaderBase_t::addMetric(feeID, std::forward(feeParameters)..., false); + metric.addStatusBit(RawDataMetric::EStatusBits::kWrongChannelMapping); + LOG(error) << "Unregistered in ChannelMap link!"; + metric.print(); } } }; diff --git a/Detectors/FIT/raw/include/FITRaw/RawWriterFIT.h b/Detectors/FIT/raw/include/FITRaw/RawWriterFIT.h index 97e9b6ee61a3d..cad08318dc7f7 100644 --- a/Detectors/FIT/raw/include/FITRaw/RawWriterFIT.h +++ b/Detectors/FIT/raw/include/FITRaw/RawWriterFIT.h @@ -18,16 +18,24 @@ #define ALICEO2_FIT_RAWREADERBASEFIT_H_ #include #include +#include #include +#include +#include +#include +#include + #include "FITRaw/DataBlockFIT.h" #include "FITRaw/DigitBlockFIT.h" +#include #include #include "Headers/RAWDataHeader.h" #include "DetectorsRaw/HBFUtils.h" #include "DetectorsRaw/RawFileWriter.h" #include "CommonUtils/StringUtils.h" -#include -#include +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/NameConf.h" +#include "DataFormatsParameters/GRPObject.h" namespace o2 { @@ -43,6 +51,7 @@ class RawWriterFIT typedef typename LookupTable_t::Topo_t Topo_t; typedef DataBlockPMtype DataBlockPM_t; typedef DataBlockTCMtype DataBlockTCM_t; + typedef typename RawDataMetric::Status_t MetricStatus_t; using RDHUtils = o2::raw::RDHUtils; RawWriterFIT() = default; ~RawWriterFIT() = default; @@ -50,6 +59,9 @@ class RawWriterFIT void setFileFor(const std::string& fileFor) { mFileFor = fileFor; } void setFlpName(const std::string& flpName) { mFlpName = flpName; } bool getFilePerLink() const { return mOutputPerLink; } + void setStatusEmu(MetricStatus_t statusEmu) { mStatusEmu = statusEmu; } + void setRandomEmu(bool isRandomStatusEmu) { mIsRandomStatusEmu = isRandomStatusEmu; } + void setVerbosity(int verbosityLevel) { mVerbosity = verbosityLevel; } void setCCDBurl(const std::string& ccdbPath) { LookupTable_t::setCCDBurl(ccdbPath); } void setLUTpath(const std::string& lutPath) { LookupTable_t::setLUTpath(lutPath); } @@ -60,11 +72,18 @@ class RawWriterFIT { return 0; // do not split, always start new CRU page } - void convertDigitsToRaw(const std::string& outputDir, const std::string& filenameDigits) + void convertDigitsToRaw(const std::string& outputDir, const std::string& filenameDigits, long timestamp = -1) { LOG(info) << "Converting Digits to Raw data..."; + if (mStatusEmu > 0 || mIsRandomStatusEmu) { + LOG(warning) << "Raw data metric emulation applied: isRandom " << mIsRandomStatusEmu << "; metricStatus: " << static_cast(mStatusEmu); + if (mStatusEmu == 0) { + LOG(warning) << "Metric status is not specified, all bits will be randomly generated"; + mStatusEmu = RawDataMetric::getAllBitsActivated(); + } + } mWriter.setCarryOverCallBack(this); - LookupTable_t::Instance().printFullMap(); + LookupTable_t::Instance(nullptr, timestamp).printFullMap(); // Preparing topo2FEEmetadata map mMapTopo2FEEmetadata.clear(); mMapTopo2FEEmetadata = LookupTable_t::Instance().template makeMapFEEmetadata(); @@ -106,15 +125,19 @@ class RawWriterFIT void processDigitBlockPerTF(const std::vector& vecDigitBlock) // Is used in DigitBlockFIT_t::processDigitBlocks for each TF (TTree entry) { for (const auto& digitBlock : vecDigitBlock) { + MetricStatus_t statusEmu{mStatusEmu}; // Processing PM data - auto mapDataBlockPM = digitBlock.template decomposeDigits(); + if (mIsRandomStatusEmu) { + statusEmu = static_cast(std::rand()) & mStatusEmu; + } + const auto mapDataBlockPM = digitBlock.template decomposeDigits(statusEmu); if (mVerbosity > 0) { digitBlock.print(); } for (const auto& dataBlockPair : mapDataBlockPM) { const auto& topo = dataBlockPair.first; const auto& dataBlock = dataBlockPair.second; - const auto itRdh = mMapTopo2FEEmetadata.find(topo); + const auto& itRdh = mMapTopo2FEEmetadata.find(topo); if (itRdh == mMapTopo2FEEmetadata.end()) { LOG(warning) << "No CRU entry in map! Data block: "; dataBlock.print(); @@ -128,10 +151,13 @@ class RawWriterFIT mWriter.addData(RDHUtils::getFEEID(rdh), RDHUtils::getCRUID(rdh), RDHUtils::getLinkID(rdh), RDHUtils::getEndPointID(rdh), dataBlock.getInteractionRecord(), data); } // Processing TCM data - const auto dataBlockPair = digitBlock.template decomposeDigits(); + if (mIsRandomStatusEmu) { + statusEmu = static_cast(std::rand()) & mStatusEmu; + } + const auto dataBlockPair = digitBlock.template decomposeDigits(statusEmu); const auto& topo = dataBlockPair.first; const auto& dataBlock = dataBlockPair.second; - const auto itRdh = mMapTopo2FEEmetadata.find(topo); + const auto& itRdh = mMapTopo2FEEmetadata.find(topo); if (itRdh == mMapTopo2FEEmetadata.end()) { LOG(warning) << "No CRU entry in map! Data block: "; dataBlock.print(); @@ -145,16 +171,189 @@ class RawWriterFIT mWriter.addData(RDHUtils::getFEEID(rdh), RDHUtils::getCRUID(rdh), RDHUtils::getLinkID(rdh), RDHUtils::getEndPointID(rdh), dataBlock.getInteractionRecord(), data); } } - - o2::raw::RawFileWriter mWriter{LookupTable_t::sDetectorName}; + static constexpr auto getDetID() { return LookupTable_t::sDetID; }; + o2::raw::RawFileWriter mWriter{getDetID().getDataOrigin()}; std::string mFlpName{}; std::string mFileFor{}; - int mDataFormat{0}; // RDH::dataFormat field, 0 - padded, 2 - no padding + MetricStatus_t mStatusEmu{}; // for metrics emulation, contains bits RawDataMetric::EStatusBits + bool mIsRandomStatusEmu{false}; // to use status bits randomly + int mDataFormat{0}; // RDH::dataFormat field, 0 - padded, 2 - no padding std::map mMapTopo2FEEmetadata; // const o2::raw::HBFUtils& mSampler = o2::raw::HBFUtils::Instance(); bool mOutputPerLink = false; int mVerbosity = 0; }; + +/* + * Special configurator for digit2raw executables + */ +namespace bpo = boost::program_options; +struct DigitToRawConfig { + using VariablesMap = typename boost::program_options::variables_map; + using OptionsDescription = typename boost::program_options::options_description; + using MetricStatus_t = typename RawDataMetric::Status_t; + DigitToRawConfig(const VariablesMap& vm) + { + setFromExecOptions(vm); + } + DigitToRawConfig() = default; // careful, data format should be then checked via adjustDataFormat() + void setFromExecOptions(const VariablesMap& vm) + { + mVerbosity = vm["verbosity"].as(); + mInputFile = vm["input-file"].as(); + mOutputDir = vm["output-dir"].as(); + mFileFor = vm["file-for"].as(); + mFlpName = vm["flp-name"].as(); + mConfigKeyValues = vm["configKeyValues"].as(); + + mRdhVersion = vm["rdh-version"].as(); + mNoEmptyHBF = vm["no-empty-hbf"].as(); + mConfDig = vm["hbfutils-config"].as(); + mEnablePadding = vm["enable-padding"].as(); + mCcdbPath = vm["ccdb-path"].as(); + mChannelMapPath = vm["lut-path"].as(); + + mMetricStatusEmu = static_cast(vm["emu-rawdata-metrics"].as()); + mIsRandomMetric = vm["emu-rawdata-metrics-random"].as(); + + adjustDataFormat(); + } + static void configureExecOptions(OptionsDescription& opt_general, const std::string& defaultInputFilename, const std::string& defaultFLP_name) + { + auto add_option = opt_general.add_options(); + // common configs + add_option("verbosity,v", bpo::value()->default_value(0), "verbosity level"); + add_option("input-file,i", bpo::value()->default_value(defaultInputFilename), "input digits file"); + add_option("output-dir,o", bpo::value()->default_value("./"), "output directory for raw data"); + add_option("flp-name", bpo::value()->default_value(defaultFLP_name), "single file per: all,flp,cru,link"); + add_option("file-for,f", bpo::value()->default_value("all"), "single file per: all,flp,cruendpoint,link"); + add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); + // raw data configs + uint32_t defRDH = o2::raw::RDHUtils::getVersion(); + add_option("rdh-version,r", bpo::value()->default_value(defRDH), "RDH version to use"); + add_option("no-empty-hbf,e", bpo::value()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); + add_option("hbfutils-config,u", bpo::value()->default_value(std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)), "config file for HBFUtils (or none)"); + add_option("enable-padding", bpo::value()->default_value(false)->implicit_value(true), "enable GBT word padding to 128 bits even for RDH V7"); + add_option("ccdb-path", bpo::value()->default_value(""), "CCDB url which contains LookupTable"); + add_option("lut-path", bpo::value()->default_value(""), "LookupTable path"); + // raw data metric emulation mode + add_option("emu-rawdata-metrics", bpo::value()->default_value(0), "Raw data metric emulation, specify 1-byte status word with o2::fit::RawDataMetric::EStatusBits"); + add_option("emu-rawdata-metrics-random", bpo::value()->default_value(false)->implicit_value(true), "Raw data metric emulation, randomly specifies 1-byte status word with o2::fit::RawDataMetric::EStatusBits. If --emu-rawdata-metrics is not specified then all bits will be on and randomly will be taken"); + } + void adjustDataFormat() + { // adjust DataFormat, with or w/o padding + if (mRdhVersion < 7 && !mEnablePadding) { + mEnablePadding = true; + LOG(info) << "padding is always ON for RDH version " << mRdhVersion; + } + mDataFormat = mEnablePadding ? 0 : 2; + } + // common configs + int mVerbosity{0}; + std::string mInputFile{"detdigits.root"}; // put det name before "digits" + std::string mOutputDir{"./"}; + std::string mFlpName{"alio2-cr1-flpnode"}; // put node number after "flp" + std::string mFileFor{"all"}; + std::string mConfigKeyValues{""}; + // raw data configs + uint32_t mRdhVersion{o2::raw::RDHUtils::getVersion()}; + bool mNoEmptyHBF{false}; + std::string mConfDig{std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)}; + bool mEnablePadding{false}; // will be adjusted(if false) wrt to RDH version + std::string mCcdbPath{""}; + std::string mChannelMapPath{""}; + // raw data metric emulation + MetricStatus_t mMetricStatusEmu{}; // status bit for raw data metric emulation + bool mIsRandomMetric{false}; // use random bits for metric emulation + // misc field + uint32_t mDataFormat; +}; + +/* + * Wrapper for digit2raw executables + */ +template +struct DigitToRawDevice { + DigitToRawDevice() = default; + DigitToRawDevice(const DigitToRawConfig& config) { setConfig(config); } + RawWriterFIT_Type mRawWriterFIT{}; + DigitToRawConfig mConfig{}; + void setConfig(const DigitToRawConfig& config) + { + mConfig = config; + configure(mConfig); + } + void configure(const DigitToRawConfig& cfg) + { + // global config + if (!cfg.mConfDig.empty() && cfg.mConfDig != "none") { + o2::conf::ConfigurableParam::updateFromFile(cfg.mConfDig, "HBFUtils"); + } + o2::conf::ConfigurableParam::updateFromString(cfg.mConfigKeyValues); + // configuring FIT raw writer + mRawWriterFIT.setFileFor(cfg.mFileFor); + mRawWriterFIT.setFlpName(cfg.mFlpName); + mRawWriterFIT.setVerbosity(cfg.mVerbosity); + if (cfg.mCcdbPath != "") { + mRawWriterFIT.setCCDBurl(cfg.mCcdbPath); + } + if (cfg.mChannelMapPath != "") { + mRawWriterFIT.setLUTpath(cfg.mChannelMapPath); + } + // raw data metric emulation + mRawWriterFIT.setRandomEmu(cfg.mIsRandomMetric); + mRawWriterFIT.setStatusEmu(cfg.mMetricStatusEmu); + // configuring common raw writer + auto& rawWriter = mRawWriterFIT.getWriter(); + std::string inputGRP = o2::base::NameConf::getGRPFileName(); + const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); + rawWriter.setContinuousReadout(grp->isDetContinuousReadOut(RawWriterFIT_Type::getDetID())); // must be set explicitly + const int superPageSizeInB = 1024 * 1024; + rawWriter.setSuperPageSize(superPageSizeInB); + rawWriter.useRDHVersion(cfg.mRdhVersion); + rawWriter.setDontFillEmptyHBF(cfg.mNoEmptyHBF); + rawWriter.useRDHDataFormat(cfg.mDataFormat); + if (!cfg.mEnablePadding) { // CRU page alignment padding is used only if no GBT word padding is used + rawWriter.setAlignmentSize(16); // change to constexpr static field from class? + rawWriter.setAlignmentPaddingFiller(0xff); + } + } + void run() + { + run(mConfig); + } + void run(const DigitToRawConfig& cfg) + { + // dst path preparations + o2::raw::assertOutputDirectory(cfg.mOutputDir); + std::string outDirName(cfg.mOutputDir); + if (outDirName.back() != '/') { + outDirName += '/'; + } + // start timestamp preparations + const auto& hbfu = o2::raw::HBFUtils::Instance(); + long startTime = hbfu.startTime > 0 ? hbfu.startTime : std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + // convertation digit->raw + mRawWriterFIT.convertDigitsToRaw(outDirName, cfg.mInputFile, startTime); + // config with output info + auto& rawWriter = mRawWriterFIT.getWriter(); + rawWriter.writeConfFile(rawWriter.getOrigin().str, "RAWDATA", o2::utils::Str::concat_string(outDirName, rawWriter.getOrigin().str, "raw.cfg")); + } + + static void digit2raw(const DigitToRawConfig& cfg) + { + TStopwatch swTot; + swTot.Start(); + // configurating + DigitToRawDevice digitToRawDevice(cfg); + // running + digitToRawDevice.run(); + // + swTot.Stop(); + swTot.Print(); + } +}; + } // namespace fit } // namespace o2 diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FITDataReaderDPLSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FITDataReaderDPLSpec.h index 42d016c75ea26..868c0f2aa717f 100644 --- a/Detectors/FIT/workflow/include/FITWorkflow/FITDataReaderDPLSpec.h +++ b/Detectors/FIT/workflow/include/FITWorkflow/FITDataReaderDPLSpec.h @@ -51,10 +51,12 @@ class FITDataReaderDPLSpec : public Task ConcreteDataMatcher mMatcherChMapCCDB; // matcher for Channel map(LUT) from CCDB bool mIsSampledRawData; bool mUpdateCCDB{true}; + bool mDumpMetrics{false}; void init(InitContext& ic) final { auto ccdbUrl = ic.options().get("ccdb-path"); auto lutPath = ic.options().get("lut-path"); + mDumpMetrics = ic.options().get("dump-raw-data-metric"); if (!ic.options().get("disable-empty-tf-protection")) { mRawReader.enableEmptyTFprotection(); } @@ -123,10 +125,10 @@ class FITDataReaderDPLSpec : public Task const auto rdhDataFormat = o2::raw::RDHUtils::getDataFormat(rdhPtr); if (rdhDataFormat == 0) { // padded cntDF0++; - mRawReader.process(true, payload, o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); + mRawReader.process(true, payload, o2::raw::RDHUtils::getFEEID(rdhPtr), o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); } else if (rdhDataFormat == 2) { // no padding cntDF2++; - mRawReader.process(false, payload, o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); + mRawReader.process(false, payload, o2::raw::RDHUtils::getFEEID(rdhPtr), o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); } else { cntDF_unknown++; continue; // or break? @@ -140,6 +142,9 @@ class FITDataReaderDPLSpec : public Task mRawReader.accumulateDigits(); mRawReader.emptyTFprotection(); mRawReader.makeSnapshot(pc); + if (mDumpMetrics) { + mRawReader.dumpRawDataMetrics(); + } mRawReader.clear(); if ((cntDF0 > 0 && cntDF2 > 0) || cntDF_unknown > 0) { LOG(error) << "Strange RDH::dataFormat in TF. Number of pages: DF=0 - " << cntDF0 << " , DF=2 - " << cntDF2 << " , DF=unknown - " << cntDF_unknown; @@ -167,9 +172,10 @@ framework::DataProcessorSpec getFITDataReaderDPLSpec(const RawReaderType& rawRea ConcreteDataMatcher matcherChMapCCDB{rawReader.mDataOrigin, RawReaderType::LookupTable_t::sObjectName, 0}; rawReader.configureOutputSpec(outputSpec); if (isSubSampled) { - inputSpec.push_back({"STF", ConcreteDataTypeMatcher{rawReader.mDataOrigin, "SUB_RAWDATA"}, Lifetime::Optional}); // in case if one need to use DataSampler + inputSpec.push_back({"STF", ConcreteDataTypeMatcher{rawReader.mDataOrigin, "SUB_RAWDATA"}, Lifetime::Sporadic}); // in case if one need to use DataSampler + askSTFDist = false; } else { - inputSpec.push_back({"STF", ConcreteDataTypeMatcher{rawReader.mDataOrigin, "RAWDATA"}, Lifetime::Optional}); + inputSpec.push_back({"STF", ConcreteDataTypeMatcher{rawReader.mDataOrigin, "RAWDATA"}, Lifetime::Timeframe}); } if (askSTFDist) { inputSpec.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); @@ -193,6 +199,7 @@ framework::DataProcessorSpec getFITDataReaderDPLSpec(const RawReaderType& rawRea o2::framework::ConfigParamSpec{"reserve-vec-chdata", VariantType::Int, 0, {"Reserve memory for ChannelData vector, to DPL channel"}}, o2::framework::ConfigParamSpec{"reserve-vec-buffer", VariantType::Int, 0, {"Reserve memory for DataBlock vector, buffer for each page"}}, o2::framework::ConfigParamSpec{"reserve-map-dig", VariantType::Int, 0, {"Reserve memory for Digit map, mapping in RawReader"}}, + o2::framework::ConfigParamSpec{"dump-raw-data-metric", VariantType::Bool, false, {"Dump raw data metrics, for debugging"}}, o2::framework::ConfigParamSpec{"disable-empty-tf-protection", VariantType::Bool, false, {"Disable empty TF protection. In case of empty payload within TF, only dummy ChannelData object will be sent."}}}}; } diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterSpec.h index f14256a8bc44d..603c123d337c0 100644 --- a/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterSpec.h +++ b/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterSpec.h @@ -35,6 +35,11 @@ namespace ft0 class RecPoints; } +namespace fdd +{ +class RecPoint; +} + namespace fit { @@ -63,6 +68,17 @@ struct DataDescriptionFITCurrents { static constexpr header::DataOrigin getDataOrigin() { return header::gDataOriginFT0; } }; +template <> +struct DataDescriptionFITCurrents { + using DataTStruct = IFDDC; + std::string static getName() { return "fdd"; } + std::string static getCCDBPath() { return "FDD/Calib/IFDDC"; } + static constexpr header::DataDescription getDataDescriptionFITC() { return header::DataDescription{"IFDDC"}; } + static constexpr header::DataDescription getDataDescriptionFITTFId() { return header::DataDescription{"IFDDTFID"}; } + static constexpr header::DataDescription getDataDescriptionCCDB() { return header::DataDescription{"IFDDCCCDB"}; } + static constexpr header::DataOrigin getDataOrigin() { return header::gDataOriginFDD; } +}; + template class FITIntegrateClusters : public Task { @@ -105,7 +121,7 @@ class FITIntegrateClusters : public Task mBufferCurrents.mIAmplA[sliceInTF] += amplA; } - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v || std::is_same_v) { const float nChanC = static_cast(cluster.getTrigger().getNChanC()); const float amplC = static_cast(cluster.getTrigger().getAmplC()); if ((nChanC > mMinNChan) && (amplC > mMinAmpl)) { @@ -164,9 +180,9 @@ o2::framework::DataProcessorSpec getFITIntegrateClusterSpec(const bool disableWr inputs); std::vector outputs; - outputs.emplace_back(FitType::getDataOrigin(), FitType::getDataDescriptionFITC(), 0, Lifetime::Sporadic); + outputs.emplace_back(FitType::getDataOrigin(), FitType::getDataDescriptionFITC(), 0, Lifetime::Timeframe); if (!disableWriter) { - outputs.emplace_back(FitType::getDataOrigin(), FitType::getDataDescriptionFITTFId(), 0, Lifetime::Sporadic); + outputs.emplace_back(FitType::getDataOrigin(), FitType::getDataDescriptionFITTFId(), 0, Lifetime::Timeframe); } return DataProcessorSpec{ diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterWriterSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterWriterSpec.h index c5d0afe798740..c53d3e9b6425c 100644 --- a/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterWriterSpec.h +++ b/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterWriterSpec.h @@ -38,8 +38,8 @@ DataProcessorSpec getFITIntegrateClusterWriterSpec() return MakeRootTreeWriterSpec(fmt::format("{}-currents-writer", FitType::getName()).data(), treeFile.data(), treeName.data(), - BranchDefinition::DataTStruct>{InputSpec{"ifitc", FitType::getDataOrigin(), FitType::getDataDescriptionFITC(), 0}, "IFITC", 1}, - BranchDefinition{InputSpec{"tfID", FitType::getDataOrigin(), FitType::getDataDescriptionFITTFId(), 0}, "tfID", 1})(); + BranchDefinition::DataTStruct>{InputSpec{"ifitc", FitType::getDataOrigin(), FitType::getDataDescriptionFITC(), 0, Lifetime::Timeframe}, "IFITC", 1}, + BranchDefinition{InputSpec{"tfID", FitType::getDataOrigin(), FitType::getDataDescriptionFITTFId(), 0, Lifetime::Timeframe}, "tfID", 1})(); } } // end namespace fit diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FITMergeIntegrateClusterSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FITMergeIntegrateClusterSpec.h index 4cc2151b55058..c70fd733cb871 100644 --- a/Detectors/FIT/workflow/include/FITWorkflow/FITMergeIntegrateClusterSpec.h +++ b/Detectors/FIT/workflow/include/FITWorkflow/FITMergeIntegrateClusterSpec.h @@ -158,7 +158,7 @@ o2::framework::DataProcessorSpec getFITMergeIntegrateClusterSpec() inputs.emplace_back("ifitc", FitType::getDataOrigin(), FitType::getDataDescriptionFITC(), 0, Lifetime::Sporadic); auto ccdbRequest = std::make_shared(true, // orbitResetTime - false, // GRPECS=true for nHBF per TF + true, // GRPECS=true for nHBF per TF false, // GRPLHCIF false, // GRPMagField false, // askMatLUT diff --git a/Detectors/FIT/workflow/include/FITWorkflow/RawReaderFIT.h b/Detectors/FIT/workflow/include/FITWorkflow/RawReaderFIT.h index 51670f33806e3..cfe41e4bb474f 100644 --- a/Detectors/FIT/workflow/include/FITWorkflow/RawReaderFIT.h +++ b/Detectors/FIT/workflow/include/FITWorkflow/RawReaderFIT.h @@ -44,6 +44,7 @@ class RawReaderFIT : public RawReaderType ~RawReaderFIT() = default; typedef RawReaderType RawReader_t; typedef typename RawReader_t::DigitBlockFIT_t DigitBlockFIT_t; + typedef typename RawReader_t::RawDataMetric_t RawDataMetric_t; typedef typename DigitBlockFIT_t::LookupTable_t LookupTable_t; typedef typename DigitBlockFIT_t::Digit_t Digit_t; typedef typename DigitBlockFIT_t::SubDigit_t SubDigitTmp_t; @@ -61,10 +62,17 @@ class RawReaderFIT : public RawReaderType o2::header::DataOrigin mDataOrigin; std::vector mVecDigit; std::vector mVecTrgInput; + std::vector mVecRawDataMetric; SubDigit_t mVecSubDigit; // tuple of vectors SingleSubDigit_t mVecSingleSubDigit; // tuple of vectors bool mEnableEmptyTFprotection{false}; bool mDumpData; + void dumpRawDataMetrics() const + { + for (const auto& entry : mVecRawDataMetric) { + entry.print(); + } + } void reserveVecDPL(std::size_t nDigits, std::size_t nSubDigits) { mVecDigit.reserve(nDigits); @@ -97,6 +105,7 @@ class RawReaderFIT : public RawReaderType }, mVecSingleSubDigit); } + mVecRawDataMetric.clear(); } template auto callGetDigit(std::index_sequence, std::index_sequence) @@ -132,6 +141,7 @@ class RawReaderFIT : public RawReaderType if (mDumpData) { callPrint(IndexesSubDigit{}, IndexesSingleSubDigit{}); } + RawReader_t::getMetrics(mVecRawDataMetric); } void configureOutputSpec(std::vector& outputSpec) const { @@ -151,30 +161,32 @@ class RawReaderFIT : public RawReaderType if constexpr (sUseTrgInput) { outputSpec.emplace_back(mDataOrigin, DetTrigInput_t::sChannelNameDPL, 0, o2::framework::Lifetime::Timeframe); } + outputSpec.emplace_back(mDataOrigin, "RawDataMetric", 0, o2::framework::Lifetime::Timeframe); } void makeSnapshot(o2::framework::ProcessingContext& pc) const { - pc.outputs().snapshot(o2::framework::Output{mDataOrigin, Digit_t::sChannelNameDPL, 0, o2::framework::Lifetime::Timeframe}, mVecDigit); + pc.outputs().snapshot(o2::framework::Output{mDataOrigin, Digit_t::sChannelNameDPL, 0}, mVecDigit); if constexpr (sSubDigitExists) { std::apply([&](const auto&... subDigit) { - ((pc.outputs().snapshot(o2::framework::Output{mDataOrigin, (std::decay::type::value_type::sChannelNameDPL), 0, o2::framework::Lifetime::Timeframe}, subDigit)), ...); + ((pc.outputs().snapshot(o2::framework::Output{mDataOrigin, (std::decay::type::value_type::sChannelNameDPL), 0}, subDigit)), ...); }, mVecSubDigit); } if constexpr (sSingleSubDigitExists) { std::apply([&](const auto&... singleSubDigit) { - ((pc.outputs().snapshot(o2::framework::Output{mDataOrigin, (std::decay::type::value_type::sChannelNameDPL), 0, o2::framework::Lifetime::Timeframe}, singleSubDigit)), ...); + ((pc.outputs().snapshot(o2::framework::Output{mDataOrigin, (std::decay::type::value_type::sChannelNameDPL), 0}, singleSubDigit)), ...); }, mVecSingleSubDigit); } if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{mDataOrigin, DetTrigInput_t::sChannelNameDPL, 0, o2::framework::Lifetime::Timeframe}, mVecTrgInput); + pc.outputs().snapshot(o2::framework::Output{mDataOrigin, DetTrigInput_t::sChannelNameDPL, 0}, mVecTrgInput); } + pc.outputs().snapshot(o2::framework::Output{mDataOrigin, "RawDataMetric", 0}, mVecRawDataMetric); } template auto& getRefVec(o2::framework::ProcessingContext& pc) { - auto& refVec = pc.outputs().make(o2::framework::Output{mDataOrigin, VecDigitType::value_type::sChannelNameDPL, 0, o2::framework::Lifetime::Timeframe}); + auto& refVec = pc.outputs().make(o2::framework::Output{mDataOrigin, VecDigitType::value_type::sChannelNameDPL, 0}); return refVec; } void enableEmptyTFprotection() @@ -192,4 +204,4 @@ class RawReaderFIT : public RawReaderType } // namespace fit } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/FOCAL/CMakeLists.txt b/Detectors/FOCAL/CMakeLists.txt index 19c08a7e55f91..d19010da9ac76 100644 --- a/Detectors/FOCAL/CMakeLists.txt +++ b/Detectors/FOCAL/CMakeLists.txt @@ -10,5 +10,8 @@ # or submit itself to any jurisdiction. add_subdirectory(base) +add_subdirectory(calib) +add_subdirectory(calibration) +add_subdirectory(simulation) add_subdirectory(reconstruction) add_subdirectory(workflow) \ No newline at end of file diff --git a/Detectors/FOCAL/base/CMakeLists.txt b/Detectors/FOCAL/base/CMakeLists.txt index 6b8bf8e418bd2..9bbf041f6d3c0 100644 --- a/Detectors/FOCAL/base/CMakeLists.txt +++ b/Detectors/FOCAL/base/CMakeLists.txt @@ -10,13 +10,19 @@ # or submit itself to any jurisdiction. o2_add_library(FOCALBase - SOURCES src/EventReader.cxx + SOURCES src/Composition.cxx + src/EventReader.cxx + src/Geometry.cxx + src/Hit.cxx src/TestbeamAnalysis.cxx - PUBLIC_LINK_LIBRARIES O2::DataFormatsFOCAL ROOT::RIO ROOT::Tree ROOT::TreePlayer + PUBLIC_LINK_LIBRARIES O2::DataFormatsFOCAL O2::SimulationDataFormat O2::CommonUtils ROOT::RIO ROOT::Tree ROOT::TreePlayer Microsoft.GSL::GSL) o2_target_root_dictionary( FOCALBase - HEADERS include/FOCALBase/EventReader.h + HEADERS include/FOCALBase/Composition.h + include/FOCALBase/EventReader.h + include/FOCALBase/Geometry.h + include/FOCALBase/Hit.h include/FOCALBase/TestbeamAnalysis.h ) \ No newline at end of file diff --git a/Detectors/FOCAL/base/include/FOCALBase/Composition.h b/Detectors/FOCAL/base/include/FOCALBase/Composition.h new file mode 100644 index 0000000000000..7f8b14386a2b4 --- /dev/null +++ b/Detectors/FOCAL/base/include/FOCALBase/Composition.h @@ -0,0 +1,77 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_FOCAL_COMPOSITION_H_ +#define ALICEO2_FOCAL_COMPOSITION_H_ + +#include + +namespace o2 +{ + +namespace focal +{ + +class Composition +{ + public: + Composition() = default; + Composition(std::string material, int layer, int stack, int id, + float cx, float cy, float cz, float dx, float dy, float dz); + Composition(Composition* comp); + Composition(const Composition& comp) = default; + ~Composition() = default; + + void setCompositionParameters(std::string material, int layer, int stack, int id, + float cx, float cy, float cz, float dx, float dy, float dz) + { + mMaterial = material; + mLayer = layer; + mStack = stack; + mId = id; + mCenterX = cx; + mCenterY = cy; + mCenterZ = cz; + mSizeX = dx; + mSizeY = dy; + mSizeZ = dz; + }; + void setLayerNumber(int layer) { mLayer = layer; } + void setId(int id) { mId = id; } + void setCenterZ(float val) { mCenterZ = val; } + + std::string material() const { return mMaterial; } + int layer() const { return mLayer; } + int stack() const { return mStack; } + int id() const { return mId; } + float centerX() const { return mCenterX; } + float centerY() const { return mCenterY; } + float centerZ() const { return mCenterZ; } + float sizeX() const { return mSizeX; } + float sizeY() const { return mSizeY; } + float sizeZ() const { return mSizeZ; } + float getThickness(void) const { return mSizeZ; } + + private: + std::string mMaterial; + int mLayer = 0; + int mStack = 0; + int mId = 0; + float mCenterX = 0.0; + float mCenterY = 0.0; + float mCenterZ = 0.0; + float mSizeX = 0.0; + float mSizeY = 0.0; + float mSizeZ = 0.0; +}; +} // end namespace focal +} // end namespace o2 +#endif \ No newline at end of file diff --git a/Detectors/FOCAL/base/include/FOCALBase/Geometry.h b/Detectors/FOCAL/base/include/FOCALBase/Geometry.h new file mode 100644 index 0000000000000..3414d84b5298f --- /dev/null +++ b/Detectors/FOCAL/base/include/FOCALBase/Geometry.h @@ -0,0 +1,253 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_FOCAL_GEOMETRY_H_ +#define ALICEO2_FOCAL_GEOMETRY_H_ + +#include +#include +#include +#include +#include + +#include "FOCALBase/Composition.h" + +namespace o2 +{ +namespace focal +{ + +class VirtualSegment +{ + + public: + VirtualSegment() + { + } + + VirtualSegment& operator=(const VirtualSegment& segment) + { + if (this != &segment) { + mMinLayer = segment.mMinLayer; + mMaxLayer = segment.mMaxLayer; + mPadSize = segment.mPadSize; + mRelativeSensitiveThickness = segment.mRelativeSensitiveThickness; + mIsPixel = segment.mIsPixel; + mPixelTreshold = segment.mPixelTreshold; + } + return *this; + } + + ~VirtualSegment() {} + + int mMinLayer = -1; + int mMaxLayer = -1; + float mPadSize = -1.0; + float mRelativeSensitiveThickness = -1.0; + int mIsPixel = 0; // 0: pad or strip; 1: pixel; 2: HCAL + float mPixelTreshold = -1.0; +}; // class VirtualSegment + +class Geometry +{ + public: + enum HCALDesgin { + Sandwich = 0, // Sandwich HCAL design + Spaghetti = 1, // Spaghetti design + Sheets = 2 // Sheets design + }; + + Geometry() = default; + Geometry(Geometry* geo); + Geometry(const Geometry& geo) = default; + Geometry& operator=(const Geometry& geo) = default; + ~Geometry() = default; + + static Geometry* getInstance(); + static Geometry* getInstance(const std::string name); + + void init(const std::string geoFile); + void init(); + void buildComposition(); + void setParameters(const std::string geoFile); + void setParameters(); + + // geometry helper functions + // TODO: return tuples instead of using references + std::tuple getGeoPadCenter(int tower, int layer, int stack, int row, int col) const; + std::tuple getGeoPadCenterLocal(int towerX, int towerY, int row, int col) const; + std::tuple getGeoPixCenterLocal(int row, int col) const; + std::tuple getGeoPixelCenter(int pixel_id, int tower, int layer, int stack, int row, int col) const; + std::tuple getGeoCompositionCenter(int tower, int layer, int stack) const; + std::tuple getPadPositionId2RowColStackLayer(int id) const; + int getPixelNumber(int vol0, int vol1, int vol2, double x, double y) const; + std::tuple getGeoTowerCenter(int tower, int segment = -1) const; + bool disabledTower(int tower); + std::tuple getVirtualInfo(double x, double y, double z) const; + std::tuple getXYZFromColRowSeg(int col, int row, int segment) const; + std::tuple getVirtualNColRow(int segment) const; + std::tuple getVirtualLayerSegment(float z) const; + std::tuple getVirtualSegmentFromLayer(int layer) const; + int getVirtualSegment(float z) const; + + // getters + int getNumberOfPads() const { return mGlobal_PAD_NX * mGlobal_PAD_NY; } + int getNumberOfPADsInX() const { return mGlobal_PAD_NX_Tower; } + int getNumberOfPADsInY() const { return mGlobal_PAD_NY_Tower; } + int getNumberOfPIXsInX() const { return mGlobal_PIX_NX_Tower; } + int getNumberOfPIXsInY() const { return mGlobal_PIX_NY_Tower; } + float getHCALTowerSize() const { return mGlobal_HCAL_Tower_Size; } + int getHCALTowersInX() const { return mGlobal_HCAL_Tower_NX; } + int getHCALTowersInY() const { return mGlobal_HCAL_Tower_NY; } + int getNumberOfSegments() const { return mNumberOfSegments; } // NOTE: These are not the virtual segments, but just total number of layers as read from the geometry file. Need to disambiguate + int getNumberOfPadLayers() const { return mNPadLayers; } + int getNumberOfPixelLayers() const { return mNPixelLayers; } + int getNumberOfHCalLayers() const { return mNHCalLayers; } + int getNumberOfLayers() const { return mNPadLayers + mNPixelLayers + mNHCalLayers; } + int getNumberOfLayerSeg() const { return mLayerSeg; } + + double getFOCALSizeX() const; + double getFOCALSizeY() const; + double getTowerSize() const { return mTowerSizeX; } + double getTowerSizeX() const; + double getTowerSizeY() const; + double getFOCALSizeZ() const; + double getECALSizeZ() const; + double getECALCenterZ() const; + double getHCALSizeZ() const; + double getHCALCenterZ() const; + double getFOCALSegmentZ(int seg) const; + double getFOCALZ0() const { return mGlobal_FOCAL_Z0; } + int getNumberOfTowersInX() const { return mGlobal_Tower_NX; } + int getNumberOfTowersInY() const { return mGlobal_Tower_NY; } + double getTowerGapSize() const { return mGlobal_TOWER_TOLX; } + double getTowerGapSizeX() const { return mGlobal_TOWER_TOLX; } + double getTowerGapSizeY() const { return mGlobal_TOWER_TOLY; } + double getGlobalPixelSize() const { return mGlobal_Pixel_Size; } + double getGlobalPixelWaferSizeX() const { return mGlobal_PIX_SizeX; } + double getGlobalPixelWaferSizeY() const { return mGlobal_PIX_SizeY; } + double getGlobalPixelSkin() const { return mGlobal_PIX_SKIN; } + double getGlobalPixelOffsetX() const { return mGlobal_PIX_OffsetX; } + double getGlobalPadSize() const { return mGlobal_Pad_Size; } + float getMiddleTowerOffset() const { return mGlobal_Middle_Tower_Offset; } + bool getInsertFrontPadLayers() const { return mInsertFrontPadLayers; } + bool getInsertHCalReadoutMaterial() const { return mInsertFrontHCalReadoutMaterial; } + float getHCALPitchSize() const { return mGlobal_HCAL_Pitch_Size; } + float getHCALBeamPipeHoleSize() const { return mGlobal_HCAL_BeamPipeHole_Size; } + + float getDetectorOpeningRight() const { return mGlobal_DetectorOpening_Right; } + float getDetectorOpeningLeft() const { return mGlobal_DetectorOpening_Left; } + + std::vector getFOCALMicroModule(int layer) const; + const Composition* getComposition(int layer, int stack) const; + std::string_view getTowerGapMaterial() const { return mGlobal_Gap_Material; } + + int getVirtualNSegments() const; + + float getVirtualPadSize(int segment) const; + float getVirtualRelativeSensitiveThickness(int segment) const; + float getVirtualPixelTreshold(int segment) const; + float getVirtualSegmentSizeZ(int segment) const; + float getVirtualSegmentZ(int segment) const; + bool getVirtualIsPixel(int segment) const; + bool getVirtualIsHCal(int segment) const; + int getVirtualNLayersInSegment(int segment) const; + int getVirtualMinLayerInSegment(int segment) const; + int getVirtualMaxLayerInSegment(int segment) const; + + void setUpLayerSegmentMap(); + void setUpTowerWaferSize(); + + HCALDesgin getHCALDesign() const { return mHCALDesign; } + + protected: + std::vector mGeometryComposition; + std::vector mFrontMatterCompositionBase; + std::vector mPadCompositionBase; + std::vector mPixelCompositionBase; + std::vector mHCalCompositionBase; + + // PAD setup + float mGlobal_Pad_Size = 0.0; // pad size + int mGlobal_PAD_NX = 0; // number of X pads in wafer + int mGlobal_PAD_NY = 0; // number of Y pads in wafer + int mGlobal_PAD_NX_Tower = 0; // number of X wafers in tower + int mGlobal_PAD_NY_Tower = 0; // number of Y wafers in tower + float mGlobal_PPTOL = 0.0; // tolerance between the wafers + float mGlobal_PAD_SKIN = 0.0; // dead area (guard ring) on the wafer + float mWaferSizeX = 0.0; // Wafer X size + float mWaferSizeY = 0.0; // Wafer Y size + + float mGlobal_DetectorOpening_Right = 0.0; // detector opening in X + float mGlobal_DetectorOpening_Left = 0.0; // detector opening in Y + + // PIX setup + float mGlobal_Pixel_Size = 0.0; // pixel size + float mGlobal_PIX_SizeX = 0.0; // sensor size X + float mGlobal_PIX_SizeY = 0.0; // sensor size Y + float mGlobal_PIX_OffsetX = 0.0; // offset for pixel layers in X + float mGlobal_PIX_OffsetY = 0.0; // offset for pixel layers in Y + float mGlobal_PIX_SKIN = 0.0; + int mGlobal_PIX_NX_Tower = 0; // number of sensors in X + int mGlobal_PIX_NY_Tower = 0; // number of sensors in Y + bool mGlobal_Pixel_Readout = false; // readout on + + // Tower setup + int mNPadLayers = 0; // total number of pad layers + int mNPixelLayers = 0; // number of pixel layers + std::array mPixelLayerLocations; // location of the pixel layers + int mGlobal_Tower_NX = 0; // How many towers in X + int mGlobal_Tower_NY = 0; // How many towers in Y + float mTowerSizeX = 0.0; // X size of tower + float mTowerSizeY = 0.0; // Y size of tower + float mGlobal_TOWER_TOLX = 0.0; // X - tolarance around tower + float mGlobal_TOWER_TOLY = 0.0; // Y - tolarance around tower + float mGlobal_Middle_Tower_Offset = 0.0; // if odd layers, the middle tower is offset due to the beampipe + std::string mGlobal_Gap_Material; // gap filling material NOTE: currently not used + + float mGlobal_HCAL_Tower_Size = 0.0; + int mGlobal_HCAL_Tower_NX = 0; // Number of HCAL towers on X + int mGlobal_HCAL_Tower_NY = 0; // Number of HCAL towers on Y + float mGlobal_HCAL_Pitch_Size = 0.0; // Distance between two fibers + float mGlobal_HCAL_BeamPipeHole_Size = 0.0; // beam pipe hole size in HCAL + HCALDesgin mHCALDesign = Sandwich; // HCAL design type + + float mGlobal_FOCAL_Z0 = 0.0; + + bool mInsertFrontPadLayers = false; // Have 2 pad layers in front of ECAL for charged particle veto + bool mInsertFrontHCalReadoutMaterial = false; // if true, insert an 1cm thick aluminium layer at 2cm behind HCal to simulate the material introduced by the readout + + int mLayerSeg = 0; + int mNHCalLayers = 0; // number of HCalLayers + std::array mSegments; // which layer belongs to which segment + std::array mNumberOfLayersInSegments; // nymber of layers in each segment + int mNumberOfSegments = 0; // number of long. segements + int mNFrontMatterCompositionBase = 0; + std::array mLocalLayerZ; //// layer location in z + std::array mLocalSegmentsZ; /// segment location in z + float mFrontMatterLayerThickness = 0.0; + float mPadLayerThickness = 0.0; + float mPixelLayerThickness = 0.0; + float mHCalLayerThickness = 0.0; + std::array mLayerThickness; // thickenss of the layers + std::list mDisableTowers; + + int mVirtualNSegments = 0; + std::vector mVirtualSegmentComposition; + + private: + static Geometry* sGeom; + static bool sInit; +}; // Geometry + +} // namespace focal +} // namespace o2 +#endif \ No newline at end of file diff --git a/Detectors/FOCAL/base/include/FOCALBase/Hit.h b/Detectors/FOCAL/base/include/FOCALBase/Hit.h new file mode 100644 index 0000000000000..e24c48ac60284 --- /dev/null +++ b/Detectors/FOCAL/base/include/FOCALBase/Hit.h @@ -0,0 +1,170 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_FOCAL_HIT_H +#define ALICEO2_FOCAL_HIT_H + +#include +#include "SimulationDataFormat/BaseHits.h" +#include "CommonUtils/ShmAllocator.h" +#include + +namespace o2::focal +{ + +/// \class Hit +/// \brief Common FOCAL hit class for the detector simulation +/// \ingroup FOCALbase +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \since May 29, 2024 +class Hit : public o2::BasicXYZEHit +{ + public: + /// \enum Subsystem_t + /// \brief Subsystem index of the Hit + enum class Subsystem_t { + EPADS, ///< ECAL pads + EPIXELS, ///< ECAL pixels + HCAL, ///< HCAL + UNKNOWN ///< Undefined + }; + + /// \struct HitID + /// \brief Mapped information of a channel + struct HitID { + int mParentID; ///< parentID of the track creating this hit + uint8_t mRow; ///< Row of the hit in the calorimeter + uint8_t mColumn; ///< Column of the hit in the calorimeter + uint8_t mLayer; ///< Layer the was hit + + bool operator==(const HitID& other) const + { + return mParentID == other.mParentID && mRow == other.mRow && mColumn == other.mColumn && mLayer == other.mLayer; + } + friend std::ostream& operator<<(std::ostream& stream, const Hit::HitID& channel); + }; + + /// \struct HitIDHasher + /// \brief Hash functor for hit ID + struct HitIDHasher { + + /// \brief Functor implementation + /// \param s Hit for which to determine a hash value + /// \return hash value for channel ID + size_t operator()(const HitID& s) const + { + std::size_t seed = 0; + boost::hash_combine(seed, s.mParentID); + boost::hash_combine(seed, s.mRow); + boost::hash_combine(seed, s.mColumn); + boost::hash_combine(seed, s.mLayer); + return seed; + } + }; + + /// \brief Dummy constructor + Hit() = default; + + /// \brief Construction of the FOCAL hit with full information + /// \param primary Index of the incoming primary particle + /// \param trackID Index of the MC particle (also shower particle) responsible for the energy loss + /// \param detID Module index inside the subsystem + /// \param subsystem FOCAL Subdetector (E-Pads, E-Pixels, HCAL) + /// \param pos Geometric hit position (global coordintate system) + /// \param tof Time-of-flight of the particle to the FOCAL + /// \param eLoss Energy loss + Hit(int primary, int trackID, int detID, Subsystem_t subsystem, double initialEnergy, const math_utils::Point3D& pos, + double tof, double eLoss); + + /// \brief Destructor + ~Hit() = default; + + /// \brief Comparison operator for equalness + /// \param other Hit to compare to + /// \return True if subsytem, module and MC particle ID match, false otherwise + bool operator==(const Hit& other) const; + + /// \brief Comparison operator for smaller + /// \param other Hit to compare to + /// \return True if other hit is smaller (first track ID, then, subsystem ID, then module ID), false otherwise + bool operator<(const Hit& other) const; + + /// \brief Operator for incremental sum, adding energy loss of the other hit to this energy loss + /// \param other Hit to add to this one + /// \return This hit containing the sum of the two energy losses + Hit& operator+=(const Hit& other); + + /// \brief Get the type of the subsystem for which the hit was created + /// \return Subsystem type + Subsystem_t getSubsystem() const { return mSubSystem; } + + /// \brief Check if the hit is a FOCAL-E pixel hit + /// \return True if the hit is a FOCAL-E pixel hit, false otherwise + bool isPixelHit() const { return mSubSystem == Subsystem_t::EPIXELS; } + + /// \brief Check if the hit is a FOCAL-E pad hit + /// \return True if the hit is a FOCAL-E pad hit, false otherwise + bool isPadHit() const { return mSubSystem == Subsystem_t::EPADS; } + + /// \brief Check if the hit is a FOCAL-H hit + /// \return True if the hit is a FOCAL-H, false otherwise + bool isHCALHit() const { return mSubSystem == Subsystem_t::HCAL; } + + /// \brief Get index of the incomimg primary particle associated with the hit + /// \return Associated primary particle + int getPrimary() const { return mPrimary; } + + /// \brief Get energy of the incoming primary particle at the entrance of FOCAL + /// \return Initial energy + double getInitialEnergy() const { return mInitialEnergy; } + + /// \brief Set energy of the incoming primary particle at the entrance of FOCAL + /// \param energy Initial energy + void setInitialEnergy(double energy) { mInitialEnergy = energy; } + + /// \brief Set index of the incomimg primary particle associated with the hit + /// \param primary Associated primary particle + void setPrimary(int primary) { mPrimary = primary; } + + /// \brief Print information of this hit on the output stream + /// \param stream Stream to print on + void printStream(std::ostream& stream) const; + + private: + Subsystem_t mSubSystem = Subsystem_t::UNKNOWN; ///< FOCAL subdetector + int mPrimary = -1; ///< Primary particles at the origin of the hit + double mInitialEnergy = 0.; ///< Energy of the parent particle that entered the EMCAL + + ClassDefNV(Hit, 1); +}; + +/// @brief Sum operator, creating a new hit with the sum of the two energy losses +/// @param lhs Left-hand side of the sum +/// @param rhs Right-hand side of the sum +/// @return New hit with the properties of the lhs hit and the summed energy loss of the two hits +Hit operator+(const Hit& lhs, const Hit& rhs); + +/// \brief Output stream operator for FOCAL hits +/// \param stream Stream to write on +/// \param point Hit to be printed +/// \return Stream after printing +std::ostream& operator<<(std::ostream& stream, const Hit& point); +} // namespace o2::focal + +#ifdef USESHM +namespace std +{ +template <> +class allocator : public o2::utils::ShmAllocator +{ +}; +} // namespace std +#endif +#endif \ No newline at end of file diff --git a/Detectors/FOCAL/base/src/Composition.cxx b/Detectors/FOCAL/base/src/Composition.cxx new file mode 100644 index 0000000000000..aefbbee3cd6ca --- /dev/null +++ b/Detectors/FOCAL/base/src/Composition.cxx @@ -0,0 +1,43 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FOCALBase/Geometry.h" + +using namespace o2::focal; + +Composition::Composition(std::string material, int layer, int stack, int id, + float cx, float cy, float cz, float dx, float dy, float dz) : mMaterial(material), + mLayer(layer), + mStack(stack), + mId(id), + mCenterX(cx), + mCenterY(cy), + mCenterZ(cz), + mSizeX(dx), + mSizeY(dy), + mSizeZ(dz) +{ + // Default constructor +} + +Composition::Composition(Composition* comp) : mMaterial(nullptr), + mLayer(0), + mStack(0), + mId(0), + mCenterX(0), + mCenterY(0), + mCenterZ(0), + mSizeX(0), + mSizeY(0), + mSizeZ(0) +{ + *this = comp; +} \ No newline at end of file diff --git a/Detectors/FOCAL/base/src/FOCALBaseLinkDef.h b/Detectors/FOCAL/base/src/FOCALBaseLinkDef.h index cbb99ddb51abb..b39e551e8ed27 100644 --- a/Detectors/FOCAL/base/src/FOCALBaseLinkDef.h +++ b/Detectors/FOCAL/base/src/FOCALBaseLinkDef.h @@ -17,4 +17,7 @@ #pragma link C++ class o2::focal::EventReader + ; #pragma link C++ class o2::focal::TestbeamAnalysis + ; +#pragma link C++ class o2::focal::Geometry + ; +#pragma link C++ class o2::focal::Composition + ; +#pragma link C++ class o2::focal::Hit + ; #endif diff --git a/Detectors/FOCAL/base/src/Geometry.cxx b/Detectors/FOCAL/base/src/Geometry.cxx new file mode 100644 index 0000000000000..2699ab5c7d602 --- /dev/null +++ b/Detectors/FOCAL/base/src/Geometry.cxx @@ -0,0 +1,1491 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// --- Standard library --- +#include +#include + +// --- ROOT system --- + +#include + +#include "FOCALBase/Geometry.h" + +using namespace o2::focal; + +bool Geometry::sInit = false; +Geometry* Geometry::sGeom = nullptr; + +//_________________________________________________________________________ +Geometry::Geometry(Geometry* geo) +{ + std::fill_n(mPixelLayerLocations.begin(), 20, -1); + std::fill_n(mSegments.begin(), 100, -100); + std::fill_n(mNumberOfLayersInSegments.begin(), 100, -1); + std::fill_n(mLocalLayerZ.begin(), 100, 0.0); + std::fill_n(mLocalSegmentsZ.begin(), 100, 0.0); + std::fill_n(mLayerThickness.begin(), 100, 0.0); + + *this = geo; +} + +//_________________________________________________________________________ +Geometry* Geometry::getInstance() +{ + + if (sGeom == nullptr) { + sGeom = new Geometry(); + sGeom->init(); + } else { + if (sInit == false) { + sGeom = new Geometry(); + sGeom->init(); + } + } + + return sGeom; +} + +//_________________________________________________________________________ +Geometry* Geometry::getInstance(std::string filename) +{ + + if (sGeom == nullptr) { + sGeom = new Geometry(); + sGeom->init(filename); + } else { + if (sInit == false) { + sGeom = new Geometry(); + sGeom->init(filename); + } + } + return sGeom; +} + +//_________________________________________________________________________ +void Geometry::init(std::string filename) +{ + if (filename != "default") { + setParameters(filename); + } else { + setParameters(); + } + + buildComposition(); + sInit = true; +} + +//_________________________________________________________________________ +void Geometry::init() +{ + setParameters(); + buildComposition(); + sInit = true; +} + +//_________________________________________________________________________ +void Geometry::buildComposition() +{ + mGeometryComposition.reserve(1000); + + int nlayers = mNPadLayers + mNPixelLayers + mNHCalLayers; + + ////// Si pad micro module for the first Pad layer + for (int i = 0; i < nlayers; i++) { + if (i < mNPadLayers + mNPixelLayers) { + // Check whether it is a pixel layer + int isPixel = 0; + for (int iPix = 0; iPix < mNPixelLayers && !isPixel; iPix++) { + LOG(debug) << "Check pixel layer idx " << iPix << " loc " << mPixelLayerLocations[iPix] << " i= " << i; + if (i == mPixelLayerLocations[iPix]) { + isPixel = 1; + } + } + + if (isPixel) { + // create pixel compositions + LOG(debug) << "Adding pixel layer at layer " << i; + for (auto& icomp : mPixelCompositionBase) { + icomp.setLayerNumber(i); + mGeometryComposition.push_back(icomp); + } + mLayerThickness[i] = mPixelLayerThickness; + } else { + // create pad compositions + for (auto& icomp : mPadCompositionBase) { + icomp.setLayerNumber(i); + mGeometryComposition.push_back(icomp); + } + mLayerThickness[i] = mPadLayerThickness; + } + } else { + // create hcal compositions + for (auto& icomp : mHCalCompositionBase) { + icomp.setLayerNumber(i); + mGeometryComposition.push_back(icomp); + } + mLayerThickness[i] = mHCalLayerThickness; + } + } // end loop over nlayers + + for (int i = 0; i < nlayers; i++) { + mLocalLayerZ[i] = 0; + for (int j = 0; j < i; j++) { + mLocalLayerZ[i] += mLayerThickness[j]; + } + } + for (int i = 0; i < nlayers; i++) { + mLocalSegmentsZ[mSegments[i]] += mLayerThickness[i]; + } + + mLocalLayerZ[nlayers] = -1; + + ////// add the front matter to the tower + for (auto& icomp : mFrontMatterCompositionBase) { + icomp.setLayerNumber(-1); + mGeometryComposition.push_back(icomp); + } + + //// re-iterate to set the longitudinal positions + for (auto& icomp : mGeometryComposition) { + if (icomp.layer() >= 0) { + icomp.setCenterZ(mFrontMatterLayerThickness + mLocalLayerZ[icomp.layer()] + icomp.centerZ() + icomp.sizeZ() / 2); /// this is pad/strip layer + } else { + icomp.setCenterZ(icomp.centerZ() + icomp.sizeZ() / 2); /// this is frontmatter + } + } +} + +//_________________________________________________________________________ +void Geometry::setParameters() +{ + + LOG(warning) << " Default parameters are not used "; + /////// this is default setting for the global parameters + mGlobal_FOCAL_Z0 = 500; +} + +//_________________________________________________________________________ +void Geometry::setParameters(std::string geometryfile) +{ + /////// this is default setting for the global parameters + mGlobal_FOCAL_Z0 = 700.0; + mInsertFrontPadLayers = false; + // PAD setup + mGlobal_Pad_Size = 1.0; // pad size + mGlobal_PAD_NX = 9; // number of X pads in wafer + mGlobal_PAD_NY = 8; // number of Y pads in wafer + mGlobal_PAD_NX_Tower = 5; // number of X wafers in tower + mGlobal_PAD_NY_Tower = 1; // number of Y wafers in tower + mGlobal_PPTOL = 0.0; // tolerance between the wafers + mGlobal_PAD_SKIN = 0.2; // dead area (guard ring) on the wafer + mGlobal_PIX_SKIN = 0.004; + // PIX setup + mGlobal_Pixel_Readout = false; + mGlobal_Pixel_Size = 0.005; // pixel size + mGlobal_PIX_SizeX = 3.0; // sensor size X + mGlobal_PIX_SizeY = 2.74; // sensor size Y + mGlobal_PIX_OffsetX = 1.0; + mGlobal_PIX_OffsetY = 0.09; + mGlobal_PIX_NX_Tower = 15; // number of sensors in X + mGlobal_PIX_NY_Tower = 3; // number of sensors in Y + + mGlobal_Tower_NX = 2; + mGlobal_Tower_NY = 11; + + mNPixelLayers = 4; + mPixelLayerLocations[0] = 2; + mPixelLayerLocations[1] = 4; + mPixelLayerLocations[2] = 6; + mPixelLayerLocations[3] = 8; + + mTowerSizeX = 0; + mTowerSizeY = 0; + mWaferSizeX = 0; + mWaferSizeY = 0; + + std::ifstream fin(geometryfile); + if (fin.fail()) { + LOG(error) << "No geometry file for FoCAL. Use default ones"; + setParameters(); + return; + } else { + LOG(info) << "Using geometry file " << geometryfile; + } + + std::vector padCompDummy(10); + std::vector hCalCompDummy(10); + std::vector pixelCompDummy(10); + std::vector frontMatterCompDummy(10); + int nPad = 0; + int hHCal = 0; + int nPixel = 0; + int nFrontMatter = 0; + + int npadlayers = 0; + int npixlayers = 0; + int pixl[10]; + + std::string input; + + LOG(info) << "Loading FOCAL geometry file "; + while (getline(fin, input)) { + LOG(debug) << "Read string :: " << input.c_str(); + const char* p = std::strpbrk("#", input.c_str()); + if (p != nullptr) { + LOG(debug) << "Skipping comment"; + continue; + } + + std::vector tokens; + std::stringstream str(input); + std::string tmpStr; + while (getline(str, tmpStr, ' ')) { + if (tmpStr.empty()) { + continue; + } + tokens.push_back(tmpStr); + } + + std::string command = tokens[0]; + LOG(debug) << "command: " << command; + + if (command.find("COMPOSITION") != std::string::npos) /// definition of the composition + { + std::string material = tokens[1]; + float cx = std::stof(tokens[2]); + float cy = std::stof(tokens[3]); + float dx = std::stof(tokens[4]); + float dy = std::stof(tokens[5]); + float dz = std::stof(tokens[6]); + float cz = 0; + + LOG(debug) << "Material :: " << material; + LOG(debug) << "cx/cy/dx/dy/dz :: " << cx << " / " << cy << " / " << dx << " / " << dy << " / " << dz; + + int stack; + if (command.find("PAD") != std::string::npos) { + sscanf(command.c_str(), "COMPOSITION_PAD_S%d", &stack); + padCompDummy.emplace_back(material, stack, stack, 0, cx, cy, cz, dx, dy, dz); + nPad++; + } + + if (command.find("HCAL") != std::string::npos) { + sscanf(command.c_str(), "COMPOSITION_HCAL_S%d", &stack); + hCalCompDummy.emplace_back(material, stack, stack, 0, cx, cy, cz, dx, dy, dz); + hHCal++; + } + + if (command.find("PIX") != std::string::npos) { + sscanf(command.c_str(), "COMPOSITION_PIX_S%d", &stack); + pixelCompDummy.emplace_back(material, stack, stack, 0, cx, cy, cz, dx, dy, dz); + mGlobal_PIX_SizeX = dx; + mGlobal_PIX_SizeY = dy; + nPixel++; + } + + if (command.find("FM") != std::string::npos) { + sscanf(command.c_str(), "COMPOSITION_FM_S%d", &stack); + frontMatterCompDummy.emplace_back(material, stack, stack, 0, cx, cy, cz, dx, dy, dz); + nFrontMatter++; + } + } // end if COMPOSITION + + if (command.find("GLOBAL") != std::string::npos) { + + if (command.find("PAD_SIZE") != std::string::npos) { + mGlobal_Pad_Size = std::stof(tokens[1]); + LOG(debug) << "Pad sensor size is set to : " << mGlobal_Pad_Size; + } + + if (command.find("PAD_NX") != std::string::npos) { + mGlobal_PAD_NX = std::stoi(tokens[1]); + LOG(debug) << "No. sensors per pad wafer in X direction : " << mGlobal_PAD_NX; + } + + if (command.find("PAD_NY") != std::string::npos) { + mGlobal_PAD_NY = std::stoi(tokens[1]); + LOG(debug) << "No. sensors per pad wafer in Y direction : " << mGlobal_PAD_NY; + } + + if (command.find("PAD_SUPERMODULE_X") != std::string::npos) { + mGlobal_PAD_NX_Tower = std::stoi(tokens[1]); + LOG(debug) << "Number of pad wafers per module in X direction : " << mGlobal_PAD_NX_Tower; + } + + if (command.find("PAD_SUPERMODULE_Y") != std::string::npos) { + mGlobal_PAD_NY_Tower = std::stoi(tokens[1]); + LOG(debug) << "Number of pad wafers per module in Y direction : " << mGlobal_PAD_NY_Tower; + } + + if (command.find("PIX_NX") != std::string::npos) { + mGlobal_PIX_NX_Tower = std::stoi(tokens[1]); + LOG(debug) << "Number of pixels sensors per module in X direction : " << mGlobal_PIX_NX_Tower; + } + + if (command.find("PIX_NY") != std::string::npos) { + mGlobal_PIX_NY_Tower = std::stoi(tokens[1]); + LOG(debug) << "Number of pixels sensors per module in Y direction : " << mGlobal_PIX_NY_Tower; + } + + if (command.find("PAD_PPTOL") != std::string::npos) { + mGlobal_PPTOL = std::stof(tokens[1]); + LOG(debug) << "Distance between pad sensors : " << mGlobal_PPTOL; + } + + if (command.find("PAD_SKIN") != std::string::npos) { + mGlobal_PAD_SKIN = std::stof(tokens[1]); + LOG(debug) << "Pad wafer skin : " << mGlobal_PAD_SKIN; + } + + if (command.find("FOCAL_Z") != std::string::npos) { + mGlobal_FOCAL_Z0 = std::stof(tokens[1]); + LOG(debug) << "Z-Location of the FoCAL is set to : " << mGlobal_FOCAL_Z0; + } + + if (command.find("DetectorOpen_Right") != std::string::npos) { + mGlobal_DetectorOpening_Right = std::stof(tokens[1]); + LOG(debug) << "Detector opening on the right : " << mGlobal_DetectorOpening_Right; + } + + if (command.find("DetectorOpen_Left") != std::string::npos) { + mGlobal_DetectorOpening_Left = std::stof(tokens[1]); + LOG(debug) << "Detector opening on the left : " << mGlobal_DetectorOpening_Left; + } + + if (command.find("HCAL_TOWER_SIZE") != std::string::npos) { + mGlobal_HCAL_Tower_Size = std::stof(tokens[1]); + LOG(debug) << "The size of the HCAL readout tower will be : " << mGlobal_HCAL_Tower_Size; + } + + if (command.find("HCAL_PITCH_SIZE") != std::string::npos) { + mGlobal_HCAL_Pitch_Size = std::stof(tokens[1]); + LOG(debug) << "The distance between fibers is : " << mGlobal_HCAL_Pitch_Size; + } + + if (command.find("HCAL_TOWER_NX") != std::string::npos) { + mGlobal_HCAL_Tower_NX = std::stoi(tokens[1]); + LOG(debug) << "The number of the HCAL readout towers in X will be : " << mGlobal_HCAL_Tower_NX; + } + + if (command.find("HCAL_TOWER_NY") != std::string::npos) { + mGlobal_HCAL_Tower_NY = std::stoi(tokens[1]); + LOG(debug) << "The number of the HCAL readout towers in Y will be : " << mGlobal_HCAL_Tower_NY; + } + + if (command.find("HCAL_BEAMPIPE") != std::string::npos) { + mGlobal_HCAL_BeamPipeHole_Size = std::stof(tokens[1]); + LOG(debug) << "The HCAL beam pipe openning : " << mGlobal_HCAL_BeamPipeHole_Size; + } + + if (command.find("PIX_OffsetX") != std::string::npos) { + mGlobal_PIX_OffsetX = std::stof(tokens[1]); + LOG(debug) << "Pixel offset from the beam pipe will be: " << mGlobal_PIX_OffsetX; + } + + if (command.find("PIX_OffsetY") != std::string::npos) { + mGlobal_PIX_OffsetY = std::stof(tokens[1]); + LOG(debug) << "Pixel offset from the top of module will be: " << mGlobal_PIX_OffsetY; + } + + if (command.find("PIX_SKIN") != std::string::npos) { + mGlobal_PIX_SKIN = std::stof(tokens[1]); + LOG(debug) << "Pixel sensor skin : " << mGlobal_PIX_SKIN; + } + + if (command.find("TOWER_TOLX") != std::string::npos) { + mGlobal_TOWER_TOLX = std::stof(tokens[1]); + mGlobal_Gap_Material = tokens[2]; + LOG(debug) << "Distance between modules in X direction : " << mGlobal_TOWER_TOLX << ", Material : " << mGlobal_Gap_Material; + } + + if (command.find("TOWER_TOLY") != std::string::npos) { + mGlobal_TOWER_TOLY = std::stof(tokens[1]); + mGlobal_Gap_Material = tokens[2]; + LOG(debug) << "Distance between modules in Y direction : " << mGlobal_TOWER_TOLY << " Material : " << mGlobal_Gap_Material; + } + + if (command.find("MIDDLE_TOWER_OFFSET") != std::string::npos) { + mGlobal_Middle_Tower_Offset = std::stof(tokens[1]); + LOG(debug) << "Middle tower offset will be: " << mGlobal_Middle_Tower_Offset; + } + + if (command.find("Tower_NX") != std::string::npos) { + mGlobal_Tower_NX = std::stof(tokens[1]); + LOG(debug) << "Number of FOCAL modules in x direction is set to : " << mGlobal_Tower_NX; + } + + if (command.find("Tower_NY") != std::string::npos) { + mGlobal_Tower_NY = std::stof(tokens[1]); + LOG(debug) << "Number of FOCAL modules in y direction is set to : " << mGlobal_Tower_NY; + } + } // end if GLOBAL + + if (command.find("COMMAND") != std::string::npos) { + + if (command.find("NUMBER_OF_PAD_LAYERS") != std::string::npos) { + npadlayers = std::stoi(tokens[1]); + LOG(debug) << "Number of pad layers " << npadlayers; + } + + if (command.find("NUMBER_OF_HCAL_LAYERS") != std::string::npos) { + mNHCalLayers = std::stoi(tokens[1]); + LOG(debug) << "Number of HCAL layers " << mNHCalLayers; + } + + if (command.find("NUMBER_OF_SEGMENTS") != std::string::npos) { + mNumberOfSegments = std::stoi(tokens[1]); + LOG(debug) << "Number of Segments " << mNumberOfSegments; + } + + if (command.find("INSERT_PIX") != std::string::npos) { + sscanf(command.c_str(), "COMMAND_INSERT_PIX_AT_L%d", &pixl[npixlayers]); + LOG(debug) << "Number of pixel layer " << npixlayers << " : location " << pixl[npixlayers]; + npixlayers++; + } + + if (command.find("COMMAND_PIXEL_READOUT_ON") != std::string::npos) { + mGlobal_Pixel_Readout = true; + mGlobal_Pixel_Size = std::stof(tokens[1]); + LOG(debug) << "Pixel readout on (for MASPS): pixel size is set to : " << mGlobal_Pixel_Size; + } + + if (command.find("COMMAND_INSERT_FRONT_PAD_LAYERS") != std::string::npos) { + mInsertFrontPadLayers = true; + LOG(debug) << "Insert two pad layers in front of ECAL for charged particle veto!"; + } + + if (command.find("COMMAND_INSERT_HCAL_READOUT") != std::string::npos) { + mInsertFrontHCalReadoutMaterial = true; + LOG(debug) << "Insert Aluminium 1cm thick layer behind HCAL to simulate readout SiPM material !"; + } + } // end if COMMAND + + if (command.find("VIRTUAL") != std::string::npos) { + + int segment, minlayer, maxLayer, isPixel; + float padSize, sensitiveThickness, pixelTreshold; + + if (command.find("N_SEGMENTS") != std::string::npos) { + mVirtualNSegments = std::stoi(tokens[1]); + LOG(debug) << "Number of Virtual Segments is set to : " << mVirtualNSegments; + } + + if (command.find("SEGMENT_LAYOUT") != std::string::npos) { + minlayer = std::stoi(tokens[1]); + maxLayer = std::stoi(tokens[2]); + padSize = std::stof(tokens[3]); + sensitiveThickness = std::stof(tokens[4]); + isPixel = std::stoi(tokens[5]); + pixelTreshold = std::stof(tokens[6]); + + if (mVirtualSegmentComposition.size() == 0) { + if (mVirtualNSegments <= 0) { + LOG(debug) << "Making 20 segments"; + for (int seg = 0; seg < 20; seg++) { + mVirtualSegmentComposition.emplace_back(); + } + mVirtualNSegments = 20; + } else { + LOG(debug) << "Making " << mVirtualNSegments << " segments"; + for (int seg = 0; seg < mVirtualNSegments; seg++) { + mVirtualSegmentComposition.emplace_back(); + } + } + } + + sscanf(command.c_str(), "VIRTUAL_SEGMENT_LAYOUT_N%d", &segment); + if (segment > mVirtualNSegments) { + continue; + } + mVirtualSegmentComposition[segment].mMinLayer = minlayer; + mVirtualSegmentComposition[segment].mMaxLayer = maxLayer; + mVirtualSegmentComposition[segment].mPadSize = padSize; + mVirtualSegmentComposition[segment].mRelativeSensitiveThickness = sensitiveThickness; + mVirtualSegmentComposition[segment].mPixelTreshold = pixelTreshold; + mVirtualSegmentComposition[segment].mIsPixel = isPixel; + + LOG(debug) << "Segment number " << segment << " defined with (minLayer, maxLayer, padSize, isPixel): (" + << minlayer << ", " << maxLayer << ", " << padSize << ", " << isPixel << ")"; + } // end if SEGMENT_LAYOUT + + } // end if VIRTUAL + + } // end while + + setUpTowerWaferSize(); + /////// re-arrange the longitudinal components + mNPixelLayers = npixlayers; + for (int i = 0; i < npixlayers; i++) { + mPixelLayerLocations[i] = pixl[i]; + } + + mNPadLayers = npadlayers; + LOG(debug) << "mNPadLayers, mNPixelLayers, mNHCalLayers, mNumberOfSegments :: " << mNPadLayers << " / " << mNPixelLayers << " / " << mNHCalLayers << " / " << mNumberOfSegments; + + mLayerSeg = (mNPadLayers + mNPixelLayers + mNHCalLayers) / mNumberOfSegments; + + if (mNumberOfSegments >= 100) { + LOG(warning) << "You reached the segments limits! Setting Number of segments to: 100"; + mNumberOfSegments = 99; + LOG(warning) << "New number of segments: " << mNumberOfSegments; + mLayerSeg = (mNPadLayers + mNPixelLayers + mNHCalLayers) / mNumberOfSegments; + } + if ((mNPadLayers + mNPixelLayers + mNHCalLayers) % mNumberOfSegments) { + mNumberOfSegments++; + for (int i = 0; i < mNumberOfSegments; i++) { + mNumberOfLayersInSegments[i] = mLayerSeg; + } + LOG(debug) << "Number of segments: " << mNumberOfSegments; + } else { + for (int i = 0; i < mNumberOfSegments; i++) { + mNumberOfLayersInSegments[i] = mLayerSeg; + } + } + + setUpLayerSegmentMap(); + + float center_z = 0; + + mPadCompositionBase.reserve(200); + mHCalCompositionBase.reserve(200); + mPixelCompositionBase.reserve(200); + mFrontMatterCompositionBase.reserve(200); + + for (auto& tmpComp : padCompDummy) { + LOG(debug) << "Material(pad layer) " << tmpComp.material(); + LOG(debug) << "layer / stack / id :: " << tmpComp.layer() << " / " << tmpComp.stack() << " / " << tmpComp.id(); + LOG(debug) << "center x,y,dz :: " << tmpComp.sizeX() << " / " << tmpComp.sizeY() << " / " << tmpComp.sizeZ(); + if (tmpComp.material().compare("SiPad")) { // materials other than SiPad + mPadCompositionBase.emplace_back(tmpComp.material(), tmpComp.layer(), tmpComp.stack(), tmpComp.id(), + tmpComp.centerX(), tmpComp.centerY(), center_z, + mTowerSizeX, mTowerSizeY, tmpComp.sizeZ()); + if (mTowerSizeX < tmpComp.sizeX()) { + mTowerSizeX = tmpComp.sizeX(); + } + if (mTowerSizeY < tmpComp.sizeY()) { + mTowerSizeY = tmpComp.sizeY(); + } + } else { + for (int itowerX = 0; itowerX < mGlobal_PAD_NX_Tower; itowerX++) { + for (int itowerY = 0; itowerY < mGlobal_PAD_NY_Tower; itowerY++) { + for (int ix = 0; ix < mGlobal_PAD_NX; ix++) { + for (int iy = 0; iy < mGlobal_PAD_NY; iy++) { + auto [x, y] = getGeoPadCenterLocal(itowerX, itowerY, iy, ix); + mPadCompositionBase.emplace_back("SiPad", tmpComp.layer(), tmpComp.stack(), + ix + iy * mGlobal_PAD_NX + itowerX * mGlobal_PAD_NX * mGlobal_PAD_NY + itowerY * mGlobal_PAD_NX_Tower * mGlobal_PAD_NX * mGlobal_PAD_NY, + x, y, center_z, + mGlobal_Pad_Size, mGlobal_Pad_Size, tmpComp.sizeZ()); + if (mTowerSizeX < mGlobal_Pad_Size) { + mTowerSizeX = mGlobal_Pad_Size; + } + if (mTowerSizeY < mGlobal_Pad_Size) { + mTowerSizeY = mGlobal_Pad_Size; + } + } + } + } // end for itowerY + } // end for itowerX + } // end else + center_z += tmpComp.getThickness(); + } // end loop over pad layer compositions + LOG(debug) << "============ Created all pad layer compositions (" << mPadCompositionBase.size() << " volumes)"; + + mPadLayerThickness = center_z; + + mGlobal_PIX_OffsetY = (getTowerSizeY() - mGlobal_PIX_NY_Tower * mGlobal_PIX_SizeY) / 2 - 2.0 * mGlobal_PIX_SKIN; + + center_z = 0; + for (auto& tmpComp : pixelCompDummy) { + LOG(debug) << "Material (pixel layer) " << tmpComp.material(); + LOG(debug) << "layer / stack / id :: " << tmpComp.layer() << " / " << tmpComp.stack() << " / " << tmpComp.id(); + LOG(debug) << "center x,y,dz :: " << tmpComp.sizeX() << " / " << tmpComp.sizeY() << " / " << tmpComp.sizeZ(); + if (tmpComp.material().compare("SiPix")) { + mPixelCompositionBase.emplace_back(tmpComp.material(), mPixelLayerLocations[0], tmpComp.stack(), tmpComp.id(), + tmpComp.centerX(), tmpComp.centerY(), center_z, mTowerSizeX, mTowerSizeY, tmpComp.sizeZ()); + } else { + for (int ix = 0; ix < mGlobal_PIX_NX_Tower; ix++) { + for (int iy = 0; iy < mGlobal_PIX_NY_Tower; iy++) { + auto [pixX, pixY] = getGeoPixCenterLocal(iy, ix); + mPixelCompositionBase.emplace_back("SiPix", tmpComp.layer(), tmpComp.stack(), ix + iy * mGlobal_PIX_NX_Tower, + pixX, pixY, center_z, + mGlobal_PIX_SizeX, mGlobal_PIX_SizeY, tmpComp.sizeZ()); + } + } + } + center_z += tmpComp.getThickness(); + } + LOG(debug) << "============ Created all pixel layer compositions (" << mPixelCompositionBase.size() << " volumes)"; + mPixelLayerThickness = center_z; + + // Add HCal Layers + center_z = 0; + for (auto& tmpComp : hCalCompDummy) { + LOG(debug) << "Material (hcal) " << tmpComp.material(); + LOG(debug) << "layer / stack / id :: " << tmpComp.layer() << " / " << tmpComp.stack() << " / " << tmpComp.id(); + LOG(debug) << "center x,y,dz :: " << tmpComp.sizeX() << " / " << tmpComp.sizeY() << " / " << tmpComp.sizeZ(); + mHCalCompositionBase.emplace_back(tmpComp.material(), tmpComp.layer(), tmpComp.stack(), tmpComp.id(), + tmpComp.centerX(), tmpComp.centerY(), mNHCalLayers == 1 ? 0. : center_z, // if we decided to use the spagetti HCAL it will be only one layer with two compositions + tmpComp.sizeX(), tmpComp.sizeY(), tmpComp.sizeZ()); + if (mNHCalLayers == 1) { + center_z = tmpComp.getThickness(); + } else { + center_z += tmpComp.getThickness(); + } + } + LOG(debug) << "============ Created all hcal compositions (" << mHCalCompositionBase.size() << " volumes)"; + mHCalLayerThickness = center_z; + center_z = 0; + + if (mNHCalLayers == 1 && hHCal > 2) { + mHCALDesign = Geometry::HCALDesgin::Sheets; + } else if (mNHCalLayers == 1 && hHCal == 2) { + mHCALDesign = Geometry::HCALDesgin::Spaghetti; + } else { + mHCALDesign = Geometry::HCALDesgin::Sandwich; + } + + mFrontMatterLayerThickness = center_z; + LOG(debug) << " end of SetParameters "; +} + +//_________________________________________________________________________ +const Composition* Geometry::getComposition(int layer, int stack) const +{ + + for (auto& icomp : mGeometryComposition) { + if (icomp.layer() == layer && icomp.stack() == stack) { + return &icomp; + } + } + return nullptr; +} + +//_________________________________________________________________________ +std::vector Geometry::getFOCALMicroModule(int layer) const +{ + + std::vector layerComposition; + + if (layer == -1) { + for (auto& icomp : mGeometryComposition) { + layerComposition.push_back(&icomp); + } + } else { + for (auto& icomp : mGeometryComposition) { + if (icomp.layer() == layer) { + layerComposition.push_back(&icomp); + } + } + } + return layerComposition; +} + +//_________________________________________________________________________ +/// this gives global position of the center of tower +std::tuple Geometry::getGeoTowerCenter(int tower, int segment) const +{ + int id = tower; + int itowerx = id % getNumberOfTowersInX(); + int itowery = id / getNumberOfTowersInX(); + + float dwx = getTowerSizeX() + getTowerGapSizeX(); + float dwy = getTowerSizeY() + getTowerGapSizeY(); + + double x = itowerx * dwx + 0.5 * dwx - 0.5 * getFOCALSizeX(); + double y = itowery * dwy + 0.5 * dwy - 0.5 * getFOCALSizeY(); + if (itowerx == 0 && itowery == 5) { + x -= mGlobal_Middle_Tower_Offset; + } + if (itowerx == 1 && itowery == 5) { + x += mGlobal_Middle_Tower_Offset; + } + + // From here is HCal stuff + if (getVirtualIsHCal(segment)) { + auto [status, nCols, nRows] = getVirtualNColRow(segment); + int ix = id % nCols; + int iy = id / nRows; + + switch (mHCALDesign) { + case HCALDesgin::Sandwich: { + float padSize = mVirtualSegmentComposition[segment].mPadSize; + double hCALsizeX = nCols * padSize; + double hCALsizeY = nRows * padSize; + + x = ix * padSize + 0.5 * padSize - 0.5 * hCALsizeX; + y = iy * padSize + 0.5 * padSize - 0.5 * hCALsizeY; + break; + } + case HCALDesgin::Spaghetti: { + float towerSize = getHCALTowerSize() / 7; // To be set from outside (number of channels on x & y) + y = iy * towerSize + 0.5 * towerSize - 0.5 * towerSize * nRows; + x = ix * towerSize + 0.5 * towerSize - 0.5 * towerSize * nCols; + break; + } + case HCALDesgin::Sheets: { + Composition comp1 = mHCalCompositionBase[0]; + Composition comp2 = mHCalCompositionBase[2]; + double hCALsizeX = comp1.sizeX() * 2; // Size of two sheet in X + double hCALsizeY = getHCALTowersInY() * (comp1.sizeY() + comp2.sizeY()) * 2; // To be set in a better way + + x = ix * hCALsizeX / getHCALTowersInX() + 0.5 * hCALsizeX / getHCALTowersInX() - 0.5 * hCALsizeX; + y = iy * hCALsizeY / getHCALTowersInY() + 0.5 * hCALsizeY / getHCALTowersInY() - 0.5 * hCALsizeY; + break; + } + default: + break; + } + } + + /* + //// remove beam pipe area + // define beam pipe radius, calculate half of the tower diagonal in XY + // and remove every tower which center is closer than the sum of the two... + double beamPipeRadius = 3.6; // in cm TODO: check if this is OK + double towerHalfDiag = std::sqrt(2)*0.5*getTowerSizeX(); // tower half diagonal + double minRadius = beamPipeRadius+towerHalfDiag; + // + if((x*x+y*y) < (minRadius*minRadius)){ // comparing the tower center position with the minimum distance in second powers. + //mDisableTowers.push_back(Tower+1); + //return false; + } + */ + + return {x, y, getFOCALZ0()}; +} + +//_________________________________________________________________________ +std::tuple Geometry::getGeoCompositionCenter(int tower, int layer, int stack) const +{ + auto [status, segment] = getVirtualSegmentFromLayer(layer); + auto [towX, towY, towZ] = getGeoTowerCenter(tower, segment); + double z = towZ; + + Composition* comp1 = (Composition*)getComposition(layer, stack); + if (comp1 == nullptr) { + z = z + mLocalLayerZ[layer] - getFOCALSizeZ() / 2; + } else { + z = comp1->centerZ() - getFOCALSizeZ() / 2 + getFOCALZ0(); + } + return {towX, towY, z}; +} + +//_________________________________________________________________________ +/// this gives global position of the pad +std::tuple Geometry::getGeoPadCenter(int tower, int layer, int stack, int row, int col) const +{ + auto [x, y, z] = getGeoCompositionCenter(tower, layer, stack); + int itowerx = tower % mGlobal_PAD_NX_Tower; + int itowery = tower / mGlobal_PAD_NX_Tower; + auto [padX, padY] = getGeoPadCenterLocal(itowerx, itowery, row, col); + + return {x + padX, y + padY, z}; +} + +//_________________________________________________________________________ +/// this gives local position of the pad with respect to the wafer +std::tuple Geometry::getGeoPadCenterLocal(int towerX, int towerY, int row, int col) const +{ + /// startting to count from upper-left + /* + (0,0) + ___________________ + | __ __ + | |__| |__| + | __ __ + | |__| |__| + | __ __ + | |__| |__| + | + */ + double x = +towerX * mWaferSizeX + mGlobal_PAD_SKIN + col * (mGlobal_Pad_Size + mGlobal_PPTOL) + 0.5 * mGlobal_Pad_Size; + double y = -towerY * mWaferSizeY - mGlobal_PAD_SKIN - row * (mGlobal_Pad_Size + mGlobal_PPTOL) - 0.5 * mGlobal_Pad_Size; + x = x - 0.5 * getTowerSizeX(); + y = y + 0.5 * mTowerSizeY; + return {x, y}; +} + +/// this gives local position of the pad with respect to the wafer +std::tuple Geometry::getGeoPixCenterLocal(int row, int col) const +{ + /// startting to count from upper-left + /* + (0,0) + ___________________ + | __ __ + | |__| |__| + | __ __ + | |__| |__| + | __ __ + | |__| |__| + | + */ + double x = +col * (mGlobal_PIX_SizeX + 2.0 * mGlobal_PIX_SKIN) + 0.5 * mGlobal_PIX_SizeX; + double y = -row * (mGlobal_PIX_SizeY + 2.0 * mGlobal_PIX_SKIN) - 0.5 * mGlobal_PIX_SizeY; + x = x - 0.5 * mTowerSizeX; + y = y + 0.5 * mTowerSizeY - mGlobal_PIX_OffsetY; + return {x, y}; +} + +//_________________________________________________________________________ +double Geometry::getTowerSizeX() const +{ + return mTowerSizeX; + // return mGlobal_NX_NY_Pads*(mGlobal_Pad_Size+mGlobal_PPTOL)-mGlobal_PPTOL+2*mGlobal_PAD_SKIN; +} + +//_________________________________________________________________________ +double Geometry::getTowerSizeY() const +{ + return mTowerSizeY; + // return mGlobal_NX_NY_Pads*(mGlobal_Pad_Size+mGlobal_PPTOL)-mGlobal_PPTOL+2*mGlobal_PAD_SKIN; +} + +//_________________________________________________________________________ +double Geometry::getFOCALSizeX() const +{ + return mGlobal_Tower_NX * (getTowerSizeX() + mGlobal_TOWER_TOLX); +} + +//_________________________________________________________________________ +double Geometry::getFOCALSizeY() const +{ + return mGlobal_Tower_NY * (getTowerSizeY() + mGlobal_TOWER_TOLY); +} + +//_________________________________________________________________________ +double Geometry::getFOCALSizeZ() const +{ + + double ret = 0; + for (int i = 0; i < mNPadLayers + mNPixelLayers + mNHCalLayers; i++) { + ret += mLayerThickness[i]; + } + ret = ret + mFrontMatterLayerThickness; + return ret; +} + +//_________________________________________________________________________ +double Geometry::getECALSizeZ() const +{ + + double ret = 0; + for (int i = 0; i < mNPadLayers + mNPixelLayers; i++) { + ret += mLayerThickness[i]; + } + ret = ret + mFrontMatterLayerThickness; + return ret; +} + +//_________________________________________________________________________ +double Geometry::getECALCenterZ() const +{ + + // Determines the ECAL z center of mass with respect to the FOCAL + double centerZ = mFrontMatterLayerThickness + mLocalLayerZ[0] + getECALSizeZ() / 2; + return centerZ; +} + +//_________________________________________________________________________ +double Geometry::getHCALSizeZ() const +{ + + double ret = 0; + for (int i = mNPadLayers + mNPixelLayers; i < mNPadLayers + mNPixelLayers + mNHCalLayers; i++) { + ret += mLayerThickness[i]; + } + return ret; +} + +//_________________________________________________________________________ +double Geometry::getHCALCenterZ() const +{ + + double centerZ = mFrontMatterLayerThickness + mLocalLayerZ[mNPadLayers + mNPixelLayers] + getHCALSizeZ() / 2; + return centerZ; +} + +//_________________________________________________________________________ +// this returns the following quantities for the pad position location +// layer depth +// pad row and col in the wafer +// wafer id in the brick, where the pad belongs to +std::tuple Geometry::getPadPositionId2RowColStackLayer(int id) const +{ + + //// id contains loction of pads in the tower, pad stack, pad layer + ///// (fComp->id()) + (fComp->stack() << 12) + (fComp->layer() << 16) +1 ; + ///// + int number = id - 1; + int padid = (number & 0xfff); + int stack = (number >> 12) & 0x000f; + // lay = (number >> 16) & 0x00ff; + int lay = (number >> 16) & 0x000f; + + // seg = fSegments[lay]; + auto [status, seg] = getVirtualSegmentFromLayer(lay); // NOTE: to be checked since this overrides the initialization above + /*col = padid%mGlobal_PAD_NX; + row = padid/mGlobal_PAD_NX;*/ + int waferx = 0; + int wafery = 0; + int col = 0; + int row = 0; + + // This gives the (col,row) of the pixel sensor + if (getVirtualIsPixel(seg)) { + col = padid % mGlobal_PIX_NX_Tower; + row = padid / mGlobal_PIX_NX_Tower; + } else { + col = padid % mGlobal_PAD_NX; + int remainder = (padid - col) / mGlobal_PAD_NX; + row = remainder % mGlobal_PAD_NY; + remainder = (remainder - row) / mGlobal_PAD_NY; + waferx = remainder % mGlobal_PAD_NX_Tower; + wafery = remainder / mGlobal_PAD_NX_Tower; + } + /*cout << "FROM GEOMETRY stack/lay/seg/waferx/wafery/col/row :: " << stack << " / " << lay << " / " << seg << " / " + << waferx << " / " << wafery << " / " << col << " / " << row << endl;*/ + if (getVirtualIsHCal(seg)) { + auto [status, nCols, nRows] = getVirtualNColRow(seg); + col = id % nCols; + row = id / nRows; + } + + return {row, col, stack, lay, seg, waferx, wafery}; +} + +//_________________________________________________________________________ +//// this gives longitudinal position of the segment +double Geometry::getFOCALSegmentZ(int seg) const +{ + + double ret = 0; + if (seg < 0 || seg > mNumberOfSegments) { + ret = getFOCALZ0(); + } else { + for (int i = 0; i < seg; i++) { + ret += mLocalSegmentsZ[i]; + } + } + ret = ret + mLocalSegmentsZ[seg] / 2 + getFOCALZ0() - getFOCALSizeZ() / 2; + return ret; +} + +//_________________________________________________________________________ +/// this function defines: +/// layer is pixel or pad +/// which segment this layer belongs to? +void Geometry::setUpLayerSegmentMap() +{ + ///// define the longitudinal elements + //// mSegments = -1 -> strip layer + //// mSegments = 0 --> pad 0th segement + //// mSegments = 1 --> pad 1th segement + //// mSegments = 2 --> pad 2th segement + + std::vector layerType; + for (int j = 0; j < mNPixelLayers + mNPadLayers + mNHCalLayers; j++) { + layerType.push_back(0); + } + for (int i = 0; i < mNPixelLayers; i++) { + layerType[mPixelLayerLocations[i]] = -1; + } + + int low = 0; + int start = 0; + int high = 0; + for (int i = 0; i < mNumberOfSegments; i++) { + high += mNumberOfLayersInSegments[i]; + for (int j = start; j < mNPixelLayers + mNPadLayers + mNHCalLayers; j++) { + if (layerType[j] == -1) { + mSegments[j] = i; + start++; + } else { + mSegments[j] = i; + low++; + start++; + } + if (low >= high) { + break; + } + } + } +} + +//_________________________________________________________________________ +/// this is the pixel number to be stored in the Hits.root file +/// this is used for the study with fine pixel readout +/// the pad is divided into the pixels with the size of mGlobal_ Pixel_Size +int Geometry::getPixelNumber(int vol0, int vol1, int /*vol2*/, double x, double y) const +{ + int ret = 0; + if (mGlobal_Pixel_Readout == false) { + ret = -1; + return ret; + } + int id = vol0; + // int tower = vol1; + // int brick = vol2; /// meaning 0 in the current design + + auto [row, col, stack, layer, segment, waferX, waferY] = getPadPositionId2RowColStackLayer(id); + auto [pixX, pixY] = getGeoPixCenterLocal(row, col); + + double x_loc = x - pixX; + double y_loc = y - pixY; + double pixel_nbr_x = ((x_loc + 0.5 * getGlobalPixelWaferSizeX()) / (mGlobal_Pixel_Size)); + double pixel_nbr_y = ((y_loc + 0.5 * getGlobalPixelWaferSizeY()) / (mGlobal_Pixel_Size)); + + int pixel_number_x; + pixel_number_x = static_cast(pixel_nbr_x); + // if(pixel_number_x-pixel_nbr_x>0.5){ + // pixel_number_x = pixel_number_x+1; + // } + + int pixel_number_y; + pixel_number_y = static_cast(pixel_nbr_y); + // if(pixel_number_y-pixel_nbr_y>0.5){ + // pixel_number_y = pixel_number_y+1; + // } + ret = (pixel_number_x << 16) | pixel_number_y; + // cout< Geometry::getGeoPixelCenter(int pixel, int tower, int layer, int stack, int row, int col) const +{ + auto [x0, y0, z0] = getGeoPadCenter(tower, layer, stack, row, col); + + int pixel_y = pixel & 0xff; + int pixel_x = (pixel >> 8) & 0xff; + + double x1, y1; + x1 = pixel_x * mGlobal_Pixel_Size + 0.5 * mGlobal_Pixel_Size - 0.5 * mGlobal_Pad_Size; + y1 = pixel_y * mGlobal_Pixel_Size + 0.5 * mGlobal_Pixel_Size - 0.5 * mGlobal_Pad_Size; + + return {x1 + x0, y1 + y0, z0}; +} + +std::tuple Geometry::getVirtualInfo(double x, double y, double z) const +{ + // + // Calculate col, row, layer, (virtual) segment from x,y,z + // returns false if outside volume + // + int col = -1, row = -1; + auto [status, layer, segment] = getVirtualLayerSegment(z); + + if (!status) { + return {false, col, row, layer, segment}; + } + if (segment == -1) { + return {false, col, row, layer, segment}; + } + if (std::abs(x) > (getFOCALSizeX() + 2 * getMiddleTowerOffset()) / 2) { + return {false, col, row, layer, segment}; + } + if (std::abs(y) > getFOCALSizeY() / 2) { + return {false, col, row, layer, segment}; + } + + if (getVirtualIsHCal(segment)) { + float towerSize = getHCALTowerSize(); + double beamPipeRadius = 3.0; // in cm TODO check the number is OK (different hardcoded values are used elsewhere) + double minRadius = beamPipeRadius + towerSize / 2.; + + double hCALsizeX = getHCALTowersInX() * towerSize; + double hCALsizeY = getHCALTowersInY() * towerSize; + + if (x < minRadius && x > -minRadius && y < minRadius && y > -minRadius) { + x = x < 0 ? x - 0.001 : x + 0.001; + y = y < 0 ? y - 0.001 : y + 0.001; + } + + switch (mHCALDesign) { + case HCALDesgin::Sandwich: { + row = (int)((y + hCALsizeY / 2) / (towerSize)); + col = (int)((x + hCALsizeX / 2) / (towerSize)); + break; + } + case HCALDesgin::Spaghetti: { + row = (int)((y + hCALsizeY / 2) / (towerSize / 7)); + col = (int)((x + hCALsizeX / 2) / (towerSize / 7)); + break; + } + case HCALDesgin::Sheets: { + Composition comp1 = mHCalCompositionBase[0]; + Composition comp2 = mHCalCompositionBase[2]; + double hCALsizeX = comp1.sizeX() * 2; // Size of two sheet in X + double hCALsizeY = getHCALTowersInY() * (comp1.sizeY() + comp2.sizeY()) * 2; // To be set in a better way + + if (y < getHCALBeamPipeHoleSize() / 2 && y > -getHCALBeamPipeHoleSize() / 2) { + if (x < 0) { + x += 1.0; // remove the offset around the beam pipe + } else { + x -= 1.0; // remove the offset around the beam pipe + } + } + + row = (int)((y + hCALsizeY / 2) / (hCALsizeY / getHCALTowersInY())); + if (x > 0) { + x = x - 0.15 - 0.06; + } + col = (int)((x - 0.15 + hCALsizeX / 2) / ((hCALsizeX - 0.15 * 2 - 0.06 * 2) / getHCALTowersInX())); + break; + } + default: + break; + } + } else { + row = (int)((y + getFOCALSizeY() / 2) / mVirtualSegmentComposition[segment].mPadSize); + // if it is the towers right and left of the beam pipe, adjust x so the offset is removed + // if(y < 4.2 && y > - 4.2) { // TO BE set from outside or somewhere else -4,4 is the y position of the middle towers + // x = x < 0 ? x + GetMiddleTowerOffset() : x - GetMiddleTowerOffset(); + // } + col = (int)((x + getFOCALSizeX() / 2) / mVirtualSegmentComposition[segment].mPadSize); + } + return {true, col, row, layer, segment}; +} + +//_______________________________________________________________________ +std::tuple Geometry::getXYZFromColRowSeg(int col, int row, int segment) const +{ + + double x = 0.0, y = 0.0, z = 0.0; + if (segment > mVirtualNSegments) { + return {false, x, y, z}; + } + + if (getVirtualIsHCal(segment)) { + float towerSize = getHCALTowerSize(); + double hCALsizeX = getHCALTowersInX() * towerSize; + double hCALsizeY = getHCALTowersInY() * towerSize; + + switch (mHCALDesign) { + case HCALDesgin::Sandwich: { + y = -1 * hCALsizeY / 2 + ((float)row + 0.5) * (towerSize); + x = -1 * hCALsizeX / 2 + ((float)col + 0.5) * (towerSize); + break; + } + case HCALDesgin::Spaghetti: { + y = -1 * hCALsizeY / 2 + ((float)row + 0.5) * (towerSize / 7); + x = -1 * hCALsizeX / 2 + ((float)col + 0.5) * (towerSize / 7); + break; + } + case HCALDesgin::Sheets: { + Composition comp1 = mHCalCompositionBase[0]; + Composition comp2 = mHCalCompositionBase[2]; + double hCALsizeX = comp1.sizeX() * 2; // Size of two sheet in X + double hCALsizeY = getHCALTowersInY() * (comp1.sizeY() + comp2.sizeY()) * 2; // To be set in a better way + + y = -1 * hCALsizeY / 2 + ((float)row + 0.5) * (hCALsizeY / getHCALTowersInY()); + x = -1 * hCALsizeX / 2 + ((float)col + 0.5) * (hCALsizeX / getHCALTowersInX()); + break; + } + default: + break; + } + } else { + y = -1 * getFOCALSizeY() / 2 + ((float)row + 0.5) * mVirtualSegmentComposition[segment].mPadSize; + x = -1 * getFOCALSizeX() / 2 + ((float)col + 0.5) * mVirtualSegmentComposition[segment].mPadSize; + // Middle towers offset + // if(y < 4.2 && y > - 4.2) { // TO BE set from outside or somewhere else -4,4 is the y position of the middle towers + // x = x < 0 ? x - GetMiddleTowerOffset() : x + GetMiddleTowerOffset(); + // } + } + + if (std::abs(x) > (getFOCALSizeX() + 2 * getMiddleTowerOffset()) / 2) { + return {false, x, y, z}; + } + if (std::abs(y) > getFOCALSizeY() / 2) { + return {false, x, y, z}; + } + z = getVirtualSegmentZ(segment); + return {true, x, y, z}; +} + +//_______________________________________________________________________ +std::tuple Geometry::getVirtualNColRow(int segment) const +{ + + // ix + iy*mGlobal_PAD_NX + itowerX*mGlobal_PAD_NX*mGlobal_PAD_NY + itowerY*mGlobal_PAD_NX_Tower*mGlobal_PAD_NX*mGlobal_PAD_NY + int nCol = -1, nRow = -1; + if (mVirtualSegmentComposition.size() == 0) { + return {false, nCol, nRow}; + } + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return {false, nCol, nRow}; + } + nCol = (int)(getFOCALSizeX() / mVirtualSegmentComposition[segment].mPadSize + 0.001); + nRow = (int)(getFOCALSizeY() / mVirtualSegmentComposition[segment].mPadSize + 0.001); + if (getVirtualIsHCal(segment)) { + switch (mHCALDesign) { + case HCALDesgin::Sandwich: { + nCol = getHCALTowersInX(); + nRow = getHCALTowersInY(); + break; + } + case HCALDesgin::Spaghetti: { + nCol = getHCALTowersInX() * 7; // To be set from outside (number of channels in each tower on x) + nRow = getHCALTowersInY() * 7; // To be set from outside (number of channels in each tower on y) + break; + } + case HCALDesgin::Sheets: { + nCol = getHCALTowersInX(); + nRow = getHCALTowersInY(); + break; + } + default: + break; + } + } + return {true, nCol, nRow}; +} + +//_______________________________________________________________________ +int Geometry::getVirtualNSegments() const +{ + + return mVirtualNSegments; +} + +//_______________________________________________________________________ +std::tuple Geometry::getVirtualLayerSegment(float z) const +{ + + int layer = -1; + int segment = -1; + + z = z - getFOCALZ0() + getFOCALSizeZ() / 2; // z from front face (excluding fron matter) + float emLayersZ = mNPadLayers * mPadLayerThickness + mNPixelLayers * mPixelLayerThickness; // Pixel layers replace pad layers + if (z < emLayersZ) { + layer = mNPadLayers + mNPixelLayers - 1; + while (layer >= 0 && z < mLocalLayerZ[layer]) { + layer--; + } + } else { + z = z - emLayersZ; + layer = int(z / mHCalLayerThickness) + mNPadLayers + mNPixelLayers; + } + + if ((layer < 0) || (layer >= (mNPadLayers + mNPixelLayers + mNHCalLayers))) { + return {false, layer, segment}; + } + + segment = -1; + for (int nSeg = 0; nSeg < mVirtualNSegments; nSeg++) { + if ((layer >= mVirtualSegmentComposition[nSeg].mMinLayer) && (layer <= mVirtualSegmentComposition[nSeg].mMaxLayer)) { + segment = nSeg; + break; + } + } + + if (segment == mVirtualNSegments) { + return {false, layer, segment}; + } + return {true, layer, segment}; +} + +//_______________________________________________________________________ +std::tuple Geometry::getVirtualSegmentFromLayer(int layer) const +{ + + int segment = -1; + for (int nSeg = 0; nSeg < mVirtualNSegments; nSeg++) { + // cout << "Segment boundaries " << nSeg << " : " << mVirtualSegmentComposition[nSeg].fMinLayer << " " << mVirtualSegmentComposition[nSeg].fMaxLayer << endl; + if ((layer >= mVirtualSegmentComposition[nSeg].mMinLayer) && (layer <= mVirtualSegmentComposition[nSeg].mMaxLayer)) { + segment = nSeg; + break; + } + } + if (segment == mVirtualNSegments) { + return {false, segment}; + } + return {true, segment}; +} + +//_______________________________________________________________________ +int Geometry::getVirtualSegment(float z) const +{ + auto [status, layer, segment] = getVirtualLayerSegment(z); + return segment; +} + +//_______________________________________________________________________ +float Geometry::getVirtualPadSize(int segment) const +{ + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + return mVirtualSegmentComposition[segment].mPadSize; +} + +//_______________________________________________________________________ +float Geometry::getVirtualRelativeSensitiveThickness(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + return mVirtualSegmentComposition[segment].mRelativeSensitiveThickness; +} + +//_______________________________________________________________________ +float Geometry::getVirtualPixelTreshold(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + return mVirtualSegmentComposition[segment].mPixelTreshold; +} + +//________________________________________________________________________ +float Geometry::getVirtualSegmentSizeZ(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + + float size = 0; + for (int nLay = mVirtualSegmentComposition[segment].mMinLayer; nLay <= mVirtualSegmentComposition[segment].mMaxLayer; nLay++) { + size += mLayerThickness[nLay]; + } + return size; +} + +//________________________________________________________________________ +float Geometry::getVirtualSegmentZ(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + + float before = 0; + float thickness = 0; + + for (int nLay = 0; nLay < mVirtualSegmentComposition[segment].mMinLayer; nLay++) { + before += mLayerThickness[nLay]; + } + for (int nLay = mVirtualSegmentComposition[segment].mMinLayer; nLay <= mVirtualSegmentComposition[segment].mMaxLayer; nLay++) { + thickness += mLayerThickness[nLay]; + } + return getFOCALZ0() - getFOCALSizeZ() / 2 + before + thickness / 2; +} + +//________________________________________________________________________ +bool Geometry::getVirtualIsPixel(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return false; + } + + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return false; + } + + return (mVirtualSegmentComposition[segment].mIsPixel == 1); +} + +//________________________________________________________________________ +bool Geometry::getVirtualIsHCal(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return false; + } + + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return false; + } + return (mVirtualSegmentComposition[segment].mIsPixel == 2); +} + +//________________________________________________________________________ +int Geometry::getVirtualNLayersInSegment(int segment) const +{ + // + // Get the number of layers in a given segment + // + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return -1; + } + return (mVirtualSegmentComposition[segment].mMaxLayer - mVirtualSegmentComposition[segment].mMinLayer + 1); +} + +//_______________________________________________________________________ +int Geometry::getVirtualMinLayerInSegment(int segment) const +{ + // + // Get the number of first layer in a given segment + // + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return -1; + } + return mVirtualSegmentComposition[segment].mMinLayer; +} + +//_______________________________________________________________________ +int Geometry::getVirtualMaxLayerInSegment(int segment) const +{ + // + // Get the number of first layer in a given segment + // + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return -1; + } + return mVirtualSegmentComposition[segment].mMaxLayer; +} diff --git a/Detectors/FOCAL/base/src/Hit.cxx b/Detectors/FOCAL/base/src/Hit.cxx new file mode 100644 index 0000000000000..4155cdc3988a7 --- /dev/null +++ b/Detectors/FOCAL/base/src/Hit.cxx @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include +#include "FOCALBase/Hit.h" + +using namespace o2::focal; + +Hit::Hit(int primary, int trackID, int detID, Subsystem_t subsystem, double initialEnergy, const math_utils::Point3D& pos, + double tof, double eLoss) : o2::BasicXYZEHit(pos.X(), pos.Y(), pos.Z(), tof, eLoss, trackID, detID), + mSubSystem(subsystem), + mInitialEnergy(initialEnergy) + +{ +} + +bool Hit::operator==(const Hit& other) const +{ + return (GetDetectorID() == other.GetDetectorID()) && (GetTrackID() == other.GetTrackID() && mSubSystem == other.mSubSystem); +} + +bool Hit::operator<(const Hit& other) const +{ + if (GetTrackID() != other.GetTrackID()) { + return GetTrackID() < other.GetTrackID(); + } + if (mSubSystem != other.mSubSystem) { + return mSubSystem < other.mSubSystem; + } + return GetDetectorID() < other.GetDetectorID(); +} + +Hit& Hit::operator+=(const Hit& other) +{ + SetEnergyLoss(GetEnergyLoss() + other.GetEnergyLoss()); + return *this; +} + +void Hit::printStream(std::ostream& stream) const +{ + stream << "FOCAL point: Track " << GetTrackID() << " in detector segment " << GetDetectorID() + << " at position (" << GetX() << "|" << GetY() << "|" << GetZ() << "), energy loss " << GetEnergyLoss() + << ", initial (parent) energy " << mInitialEnergy << " from primary " << mPrimary; +} + +Hit o2::focal::operator+(const Hit& lhs, const Hit& rhs) +{ + Hit summed(lhs); + summed += rhs; + return summed; +} + +std::ostream& o2::focal::operator<<(std::ostream& stream, const o2::focal::Hit& point) +{ + point.printStream(stream); + return stream; +} \ No newline at end of file diff --git a/Detectors/FOCAL/calib/CMakeLists.txt b/Detectors/FOCAL/calib/CMakeLists.txt new file mode 100644 index 0000000000000..4ff37d91d89bc --- /dev/null +++ b/Detectors/FOCAL/calib/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(FOCALCalib + SOURCES src/PadPedestal.cxx + src/PadBadChannelMap.cxx + PUBLIC_LINK_LIBRARIES O2::DataFormatsFOCAL Boost::serialization +) + +o2_target_root_dictionary( + FOCALCalib + HEADERS include/FOCALCalib/PadPedestal.h + include/FOCALCalib/PadBadChannelMap.h +) \ No newline at end of file diff --git a/Detectors/FOCAL/calib/include/FOCALCalib/PadBadChannelMap.h b/Detectors/FOCAL/calib/include/FOCALCalib/PadBadChannelMap.h new file mode 100644 index 0000000000000..198c98a44c90d --- /dev/null +++ b/Detectors/FOCAL/calib/include/FOCALCalib/PadBadChannelMap.h @@ -0,0 +1,81 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_FOCAL_PADBADCHANNELMAP_H +#define ALICEO2_FOCAL_PADBADCHANNELMAP_H + +#include +#include +#include +#include +#include "Rtypes.h" + +#include "DataFormatsFOCAL/Constants.h" + +namespace o2::focal +{ + +class PadBadChannelMap +{ + public: + enum MaskType_t { + GOOD_CHANNEL = 0, + WARM_CHANNEL = 1, + BAD_CHANNEL = 2, + DEAD_CHANNEL = 3 + }; + + class ChannelIndexException final : public std::exception + { + public: + ChannelIndexException(std::size_t layer, std::size_t channel) : mLayer(layer), mChannel(channel) + { + mMessage = "Access to invalid channel: Layer " + std::to_string(mLayer) + ", channel " + std::to_string(mChannel); + } + ~ChannelIndexException() noexcept final = default; + + const char* what() const noexcept final { return mMessage.data(); } + + std::size_t getLayer() const noexcept { return mLayer; } + std::size_t getChannel() const noexcept { return mChannel; } + + private: + std::size_t mLayer; + std::size_t mChannel; + std::string mMessage; + }; + + PadBadChannelMap(); + ~PadBadChannelMap() = default; + + void reset(); + + void setChannelStatus(std::size_t layer, std::size_t channel, MaskType_t channeltype); + void setGoodChannel(std::size_t layer, std::size_t channel) { setChannelStatus(layer, channel, MaskType_t::WARM_CHANNEL); } + void setBadChannel(std::size_t layer, std::size_t channel) { setChannelStatus(layer, channel, MaskType_t::BAD_CHANNEL); } + void setDeadChannel(std::size_t layer, std::size_t channel) { setChannelStatus(layer, channel, MaskType_t::DEAD_CHANNEL); } + void setWarmChannel(std::size_t layer, std::size_t channel) { setChannelStatus(layer, channel, MaskType_t::WARM_CHANNEL); } + + MaskType_t getChannelStatus(std::size_t layer, std::size_t channel) const; + bool isGoodChannel(std::size_t layer, std::size_t channel) const { return getChannelStatus(layer, channel) == MaskType_t::GOOD_CHANNEL; } + bool isBadChannel(std::size_t layer, std::size_t channel) const { return getChannelStatus(layer, channel) == MaskType_t::GOOD_CHANNEL; } + bool isDeadChannel(std::size_t layer, std::size_t channel) const { return getChannelStatus(layer, channel) == MaskType_t::GOOD_CHANNEL; } + bool isWarmChannel(std::size_t layer, std::size_t channel) const { return getChannelStatus(layer, channel) == MaskType_t::GOOD_CHANNEL; } + + private: + void init(); + std::size_t getChannelIndex(std::size_t layer, std::size_t channel) const; + std::array mChannelStatus; + + ClassDefNV(PadBadChannelMap, 1) +}; + +} // namespace o2::focal +#endif // ALICEO2_FOCAL_PADBADCHANNELMAP_H \ No newline at end of file diff --git a/Detectors/FOCAL/calib/include/FOCALCalib/PadPedestal.h b/Detectors/FOCAL/calib/include/FOCALCalib/PadPedestal.h new file mode 100644 index 0000000000000..c074756d147cc --- /dev/null +++ b/Detectors/FOCAL/calib/include/FOCALCalib/PadPedestal.h @@ -0,0 +1,110 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_FOCAL_PADPEDESTAL_H +#define ALICEO2_FOCAL_PADPEDESTAL_H + +#include +#include +#include +#include + +#include + +#include "TH1.h" +#include "Rtypes.h" + +namespace o2::focal +{ + +class PadPedestal +{ + public: + struct ChannelID { + std::size_t mLayer; + std::size_t mChannel; + + bool operator==(const ChannelID& other) const + { + return mLayer == other.mLayer && mChannel == other.mChannel; + } + }; + + struct ChannelIDHasher { + + /// \brief Functor implementation + /// \param s Channel for which to determine a hash value + /// \return hash value for channel ID + size_t operator()(const ChannelID& s) const + { + std::size_t seed = 0; + boost::hash_combine(seed, s.mLayer); + boost::hash_combine(seed, s.mChannel); + return seed; + } + }; + + class InvalidChannelException final : public std::exception + { + public: + InvalidChannelException(std::size_t layer, std::size_t channel) : mLayer(layer), mChannel(channel) + { + mMessage = "Invalid channel: Layer " + std::to_string(mLayer) + ", channel " + std::to_string(mChannel); + } + ~InvalidChannelException() noexcept final = default; + + const char* what() const noexcept final { return mMessage.data(); } + + std::size_t getLayer() const noexcept { return mLayer; } + std::size_t getChannel() const noexcept { return mChannel; } + + private: + std::size_t mLayer; + std::size_t mChannel; + std::string mMessage; + }; + + class InvalidLayerException final : public std::exception + { + public: + InvalidLayerException(std::size_t layer) : mLayer(layer) + { + mMessage = "Access to invalid layer " + std::to_string(layer); + } + ~InvalidLayerException() noexcept final = default; + + const char* what() const noexcept final { return mMessage.data(); } + std::size_t getLayer() const noexcept { return mLayer; } + + private: + std::size_t mLayer = 0; + std::string mMessage; + }; + + PadPedestal() = default; + ~PadPedestal() = default; + + bool operator==(const PadPedestal& rhs) const; + + void clear(); + void setPedestal(std::size_t layer, std::size_t channel, double pedestal); + double getPedestal(std::size_t layer, std::size_t channel) const; + + TH1* getHistogramRepresentation(int layer) const; + std::array getLayerHistogramRepresentations() const; + int getNumberOfChannels() const { return mPedestalValues.size(); } + + private: + std::unordered_map mPedestalValues; + ClassDefNV(PadPedestal, 1); +}; + +} // namespace o2::focal +#endif // ALICEO2_FOCAL_PADPEDESTAL_H \ No newline at end of file diff --git a/Detectors/FOCAL/calib/src/FOCALCalibLinkDef.h b/Detectors/FOCAL/calib/src/FOCALCalibLinkDef.h new file mode 100644 index 0000000000000..8b7910a56aa76 --- /dev/null +++ b/Detectors/FOCAL/calib/src/FOCALCalibLinkDef.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::focal::PadPedestal + ; +#pragma link C++ class o2::focal::PadPedestal::ChannelID + ; +#pragma link C++ class o2::focal::PadBadChannelMap + ; +#endif diff --git a/Detectors/FOCAL/calib/src/PadBadChannelMap.cxx b/Detectors/FOCAL/calib/src/PadBadChannelMap.cxx new file mode 100644 index 0000000000000..e5cf8de042f70 --- /dev/null +++ b/Detectors/FOCAL/calib/src/PadBadChannelMap.cxx @@ -0,0 +1,50 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FOCALCalib/PadBadChannelMap.h" + +using namespace o2::focal; + +PadBadChannelMap::PadBadChannelMap() +{ + init(); +} + +void PadBadChannelMap::init() +{ + // Mark all channels as good channels + for (std::size_t index = 0; index < mChannelStatus.size(); index++) { + mChannelStatus[index] = static_cast(MaskType_t::GOOD_CHANNEL); + } +} + +void PadBadChannelMap::reset() +{ + init(); +} + +void PadBadChannelMap::setChannelStatus(std::size_t layer, std::size_t channel, MaskType_t masktype) +{ + mChannelStatus[getChannelIndex(layer, channel)] = static_cast(masktype); +} + +PadBadChannelMap::MaskType_t PadBadChannelMap::getChannelStatus(std::size_t layer, std::size_t channel) const +{ + return static_cast(mChannelStatus[getChannelIndex(layer, channel)]); +} + +std::size_t PadBadChannelMap::getChannelIndex(std::size_t layer, std::size_t channel) const +{ + if (layer >= constants::PADS_NLAYERS || channel >= constants::PADLAYER_MODULE_NCHANNELS) { + throw ChannelIndexException(layer, channel); + } + return channel + constants::PADLAYER_MODULE_NCHANNELS * layer; +} \ No newline at end of file diff --git a/Detectors/FOCAL/calib/src/PadPedestal.cxx b/Detectors/FOCAL/calib/src/PadPedestal.cxx new file mode 100644 index 0000000000000..e61d74a45817b --- /dev/null +++ b/Detectors/FOCAL/calib/src/PadPedestal.cxx @@ -0,0 +1,117 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include +#include +#include + +#include + +using namespace o2::focal; + +void PadPedestal::clear() +{ +} + +bool PadPedestal::operator==(const PadPedestal& rhs) const +{ + if (mPedestalValues.size() != rhs.mPedestalValues.size()) { + LOG(debug) << "Error size: this " << mPedestalValues.size() << ", other " << rhs.mPedestalValues.size(); + return false; + } + bool failure = false; + // check equalty of content based on other object + for (auto [channel, pedestal] : rhs.mPedestalValues) { + auto found = mPedestalValues.find(channel); + if (found == mPedestalValues.end()) { + LOG(debug) << "Key not found this: Layer " << channel.mLayer << ", channel " << channel.mChannel; + failure = true; + } else { + if (std::abs(found->second - pedestal) > DBL_EPSILON) { + LOG(debug) << "Value error channel layer " << channel.mLayer << ", channel " << channel.mChannel << " this " << found->second << ", other " << pedestal; + failure = true; + } + } + } + if (failure) { + return false; + } + // check equality of content based on this object + for (auto [channel, pedestal] : mPedestalValues) { + auto found = rhs.mPedestalValues.find(channel); + if (found == rhs.mPedestalValues.end()) { + LOG(debug) << "Key not found other: Layer " << channel.mLayer << ", channel: " << channel.mChannel; + failure = true; + } else { + if (std::abs(found->second - pedestal) > DBL_EPSILON) { + LOG(debug) << "Value error channel layer " << channel.mLayer << ", channel " << channel.mChannel << " other " << found->second << ", this " << pedestal; + failure = true; + } + } + } + return !failure; +} + +void PadPedestal::setPedestal(std::size_t layer, std::size_t channel, double pedestal) +{ + if (layer >= constants::PADS_NLAYERS) { + throw InvalidChannelException(layer, channel); + } + if (channel >= constants::PADLAYER_MODULE_NCHANNELS) { + throw InvalidChannelException(layer, channel); + } + auto found = mPedestalValues.find({layer, channel}); + if (found == mPedestalValues.end()) { + mPedestalValues.insert({{layer, channel}, pedestal}); + } else { + found->second = pedestal; + } +} + +double PadPedestal::getPedestal(std::size_t layer, std::size_t channel) const +{ + auto found = mPedestalValues.find({layer, channel}); + if (found == mPedestalValues.end()) { + throw InvalidChannelException(layer, channel); + } + return found->second; +} + +TH1* PadPedestal::getHistogramRepresentation(int layer) const +{ + if (layer > 18) { + throw InvalidLayerException(layer); + } + std::string histname = "PedestalsLayer" + std::to_string(layer), + histtitle = "Pedestals in layer " + std::to_string(layer); + auto pedestalHist = new TH1F(histname.data(), histtitle.data(), constants::PADLAYER_MODULE_NCHANNELS, -0.5, constants::PADLAYER_MODULE_NCHANNELS - 0.5); + pedestalHist->SetDirectory(nullptr); + pedestalHist->SetStats(false); + pedestalHist->SetXTitle("Channel"); + pedestalHist->SetYTitle("Pedestal (ADC counts)"); + for (std::size_t channel = 0; channel < constants::PADLAYER_MODULE_NCHANNELS; channel++) { + double pedestal = 0; + try { + pedestal = getPedestal(layer, channel); + } catch (InvalidChannelException& e) { + LOG(error) << e.what(); + } + pedestalHist->SetBinContent(channel + 1, pedestal); + } + return pedestalHist; +} +std::array PadPedestal::getLayerHistogramRepresentations() const +{ + std::array pedestalHistograms; + for (int layer = 0; layer < 18; layer++) { + pedestalHistograms[layer] = getHistogramRepresentation(layer); + } + return pedestalHistograms; +} \ No newline at end of file diff --git a/Detectors/FOCAL/calibration/CMakeLists.txt b/Detectors/FOCAL/calibration/CMakeLists.txt new file mode 100644 index 0000000000000..18785e74770a9 --- /dev/null +++ b/Detectors/FOCAL/calibration/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(FOCALCalibration + SOURCES src/PadPedestalCalibDevice.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsFOCAL + O2::DetectorsRaw + O2::FOCALCalib + O2::FOCALReconstruction + O2::DetectorsCalibration + ROOT::Hist) + +o2_add_executable(pad-pedestal-calibration-workflow + COMPONENT_NAME focal + SOURCES src/pad-pedestal-calibration-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsFOCAL + O2::FOCALCalibration + O2::DetectorsCalibration) diff --git a/Detectors/FOCAL/calibration/include/FOCALCalibration/PadPedestalCalibDevice.h b/Detectors/FOCAL/calibration/include/FOCALCalibration/PadPedestalCalibDevice.h new file mode 100644 index 0000000000000..d0cb95bc9b6b1 --- /dev/null +++ b/Detectors/FOCAL/calibration/include/FOCALCalibration/PadPedestalCalibDevice.h @@ -0,0 +1,63 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_FOCAL_PADPEDESTALCALIBDEVICE_H +#define ALICEO2_FOCAL_PADPEDESTALCALIBDEVICE_H + +#include +#include +#include +#include +#include +#include +#include "Framework/DataAllocator.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "FOCALCalib/PadPedestal.h" +#include "FOCALReconstruction/PadDecoder.h" + +namespace o2::focal +{ + +class PadPedestalCalibDevice : public framework::Task +{ + public: + enum Method_t { + MAX, + MEAN, + FIT + }; + PadPedestalCalibDevice(bool updateCCDB, const std::string& path, bool debugMode); + ~PadPedestalCalibDevice() final = default; + + void init(framework::InitContext& ctx) final; + void run(framework::ProcessingContext& ctx) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + void sendData(o2::framework::DataAllocator& output); + void calculatePedestals(); + double evaluatePedestal(TH1* channel); + void processRawData(const gsl::span padWords); + + PadDecoder mDecoder; + std::unique_ptr mPedestalContainer; + bool mUpdateCCDB = true; + bool mDebug = false; + std::string mPath = "./"; + Method_t mExtractionMethod = Method_t::MAX; + std::array, 18> mADCDistLayer; +}; + +o2::framework::DataProcessorSpec getPadPedestalCalibDevice(bool updateCCDB, const std::string& path, bool debug); + +} // namespace o2::focal + +#endif \ No newline at end of file diff --git a/Detectors/FOCAL/calibration/src/PadPedestalCalibDevice.cxx b/Detectors/FOCAL/calibration/src/PadPedestalCalibDevice.cxx new file mode 100644 index 0000000000000..86d3566cb5304 --- /dev/null +++ b/Detectors/FOCAL/calibration/src/PadPedestalCalibDevice.cxx @@ -0,0 +1,230 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include + +#include +#include +#include + +#include "CCDB/CcdbApi.h" +#include "CCDB/CcdbObjectInfo.h" +#include "CommonConstants/Triggers.h" +#include "CommonUtils/MemFileHelper.h" +#include "DataFormatsFOCAL/Constants.h" +#include "DetectorsCalibration/Utils.h" +#include "DetectorsRaw/RDHUtils.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/WorkflowSpec.h" +#include "FOCALCalibration/PadPedestalCalibDevice.h" + +using namespace o2::focal; + +PadPedestalCalibDevice::PadPedestalCalibDevice(bool updateCCDB, const std::string& path, bool debugMode) : mUpdateCCDB(updateCCDB), mPath(path), mDebug(debugMode) {} + +void PadPedestalCalibDevice::init(framework::InitContext& ctx) +{ + std::string histname, histtitle; + for (auto layer = 0; layer < 18; layer++) { + histname = "PADADC_Layer" + std::to_string(layer); + histtitle = "Pad ADC distribution Layer " + std::to_string(layer); + mADCDistLayer[layer] = std::make_unique(histname.data(), histtitle.data(), constants::PADLAYER_MODULE_NCHANNELS, -0.5, constants::PADLAYER_MODULE_NCHANNELS - 0.5, 2000, 0., 2000.); + mADCDistLayer[layer]->SetDirectory(nullptr); + } + + auto calibmethod = ctx.options().get("calibmethod"); + if (calibmethod == "mean") { + mExtractionMethod = Method_t::MEAN; + } else if (calibmethod == "fit") { + mExtractionMethod = Method_t::FIT; + } else { + LOG(error) << "Unknown calibration method - using default (mean) instead"; + } + LOG(info) << "Writing calibration histograms in path: " << mPath; +} + +void PadPedestalCalibDevice::run(framework::ProcessingContext& ctx) +{ + std::vector rawbuffer; + for (const auto& rawData : framework::InputRecordWalker(ctx.inputs())) { + if (rawData.header != nullptr && rawData.payload != nullptr) { + const auto payloadSize = o2::framework::DataRefUtils::getPayloadSize(rawData); + + gsl::span databuffer(rawData.payload, payloadSize); + int currentpos = 0; + int currentfee = 0; + bool firstHBF = true; + while (currentpos < databuffer.size()) { + auto rdh = reinterpret_cast(databuffer.data() + currentpos); + auto trigger = o2::raw::RDHUtils::getTriggerType(rdh); + if (trigger & o2::trigger::HB) { + // HB trigger received + if (o2::raw::RDHUtils::getStop(rdh)) { + // Data ready + if (rawbuffer.size()) { + // Only process if we actually have payload (skip empty HBF) + if (currentfee == 0xcafe) { // Use FEE ID 0xcafe for PAD data + processRawData(rawbuffer); + } + } else { + LOG(debug) << "Payload size 0 - skip empty HBF"; + } + } else { + rawbuffer.clear(); + currentfee = o2::raw::RDHUtils::getFEEID(rdh); + } + } + + if (o2::raw::RDHUtils::getMemorySize(rdh) == o2::raw::RDHUtils::getHeaderSize(rdh)) { + // Skip page if emtpy + currentpos += o2::raw::RDHUtils::getOffsetToNext(rdh); + continue; + } + + // non-0 payload size: + auto payloadsize = o2::raw::RDHUtils::getMemorySize(rdh) - o2::raw::RDHUtils::getHeaderSize(rdh); + auto page_payload = databuffer.subspan(currentpos + o2::raw::RDHUtils::getHeaderSize(rdh), payloadsize); + std::copy(page_payload.begin(), page_payload.end(), std::back_inserter(rawbuffer)); + currentpos += o2::raw::RDHUtils::getOffsetToNext(rdh); + } + } + } +} + +void PadPedestalCalibDevice::endOfStream(o2::framework::EndOfStreamContext& ec) +{ + LOG(info) << "Data collected - calculating pedestals and sending them"; + calculatePedestals(); + sendData(ec.outputs()); +} + +void PadPedestalCalibDevice::sendData(o2::framework::DataAllocator& output) +{ + + if (mUpdateCCDB) { + // prepare all info to be sent to CCDB + o2::ccdb::CcdbObjectInfo info; + auto image = o2::ccdb::CcdbApi::createObjectImage(mPedestalContainer.get(), &info); + + auto flName = o2::ccdb::CcdbApi::generateFileName("Pedestals"); + info.setPath("FOC/Calib/PadPedestals"); + info.setObjectType("Pedestals"); + info.setFileName(flName); + + const auto now = std::chrono::system_clock::now(); + long timeStart = std::chrono::duration_cast(now.time_since_epoch()).count(); + info.setStartValidityTimestamp(timeStart); + info.setEndValidityTimestamp(o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP); + std::map md; + info.setMetaData(md); + + LOG(info) << "Sending object FOC/Calib/PadPedestals"; + + header::DataHeader::SubSpecificationType subSpec{(header::DataHeader::SubSpecificationType)0}; + output.snapshot(framework::Output{o2::calibration::Utils::gDataOriginCDBPayload, "FOC_PADPEDESTALS", subSpec}, *image.get()); + output.snapshot(framework::Output{o2::calibration::Utils::gDataOriginCDBWrapper, "FOC_PADPEDESTALS", subSpec}, info); + } + + if (mDebug) { + TFile pedestalWriter("FOCALPedestalCalib.root", "RECREATE"); + pedestalWriter.cd(); + pedestalWriter.WriteObjectAny(mPedestalContainer.get(), o2::focal::PadPedestal::Class(), "ccdb_object"); + } + + // store ADC distributions in local file + std::filesystem::path filepath(mPath); + filepath.append("FOCALPadPedestals.root"); + LOG(info) << "Writing ADC distribution to " << filepath.c_str(); + TFile distwriter(filepath.c_str(), "RECREATE"); + distwriter.cd(); + for (int layer = 0; layer < mADCDistLayer.size(); layer++) { + mADCDistLayer[layer]->Write(); + } +} + +void PadPedestalCalibDevice::processRawData(const gsl::span padWords) +{ + constexpr std::size_t EVENTSIZEPADGBT = 1180, + EVENTSIZECHAR = EVENTSIZEPADGBT * sizeof(PadGBTWord) / sizeof(char); + auto nevents = padWords.size() / (EVENTSIZECHAR); + for (int ievent = 0; ievent < nevents; ievent++) { + gsl::span padWordsGBT(reinterpret_cast(padWords.data()), padWords.size() / sizeof(PadGBTWord)); + mDecoder.reset(); + mDecoder.decodeEvent(padWordsGBT); + // Fill histograms + for (auto layer = 0; layer < 18; layer++) { + auto layerdata = mDecoder.getData().getDataForASIC(layer).getASIC(); + for (std::size_t chan = 0; chan < constants::PADLAYER_MODULE_NCHANNELS; chan++) { + mADCDistLayer[layer]->Fill(chan, layerdata.getChannel(chan).getADC()); + } + } + } +} + +void PadPedestalCalibDevice::calculatePedestals() +{ + mPedestalContainer = std::make_unique(); + int nPedestals = 0; + for (std::size_t layer = 0; layer < 18; layer++) { + for (std::size_t channel = 0; channel < constants::PADLAYER_MODULE_NCHANNELS; channel++) { + std::unique_ptr channelADC(mADCDistLayer[layer]->ProjectionY("channelADC", channel + 1, channel + 1)); + try { + mPedestalContainer->setPedestal(layer, channel, evaluatePedestal(channelADC.get())); + nPedestals++; + } catch (PadPedestal::InvalidChannelException& e) { + LOG(error) << "Failure accessing setting pedestal: " << e.what(); + } + } + } + LOG(info) << "Found pedestals for " << nPedestals << " channels"; +} + +double PadPedestalCalibDevice::evaluatePedestal(TH1* channel) +{ + double pedestal = 0; + switch (mExtractionMethod) { + case Method_t::MAX: { + // Max model - get center of the maximum bin + int maxbin = channel->GetMaximumBin(); + pedestal = channel->GetBinCenter(maxbin); + break; + } + case Method_t::MEAN: { + // Mean model - get the mean of the distribution + pedestal = channel->GetMean(); + break; + } + case Method_t::FIT: { + // Fit model - fit full distribution with Gauss and take mean + TF1 pedmodal("pedmodel", "gaus", 0, 2000); + channel->Fit(&pedmodal, "Q"); + pedestal = pedmodal.GetParameter(1); + break; + } + } + return pedestal; +} + +o2::framework::DataProcessorSpec o2::focal::getPadPedestalCalibDevice(bool updateCCDB, const std::string& path, bool debug) +{ + std::vector outputs; + outputs.emplace_back(framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "FOC_PADPEDESTALS"}, o2::framework::Lifetime::Sporadic); + outputs.emplace_back(framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "FOC_PADPEDESTALS"}, o2::framework::Lifetime::Sporadic); + + return o2::framework::DataProcessorSpec{"FOCALPadPedestalCalibDevice", + o2::framework::select("A:FOC/RAWDATA"), + outputs, + o2::framework::adaptFromTask(updateCCDB, path, debug), + o2::framework::Options{ + {"calibmethod", o2::framework::VariantType::String, "max", {"Method used for pedestal evaluation"}}}}; +} \ No newline at end of file diff --git a/Detectors/FOCAL/calibration/src/pad-pedestal-calibration-workflow.cxx b/Detectors/FOCAL/calibration/src/pad-pedestal-calibration-workflow.cxx new file mode 100644 index 0000000000000..3433ba0c9c0aa --- /dev/null +++ b/Detectors/FOCAL/calibration/src/pad-pedestal-calibration-workflow.cxx @@ -0,0 +1,37 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "FOCALCalibration/PadPedestalCalibDevice.h" +#include "Framework/DataProcessorSpec.h" +#include "CommonUtils/ConfigurableParam.h" + +void customize(std::vector& workflowOptions) +{ + workflowOptions.push_back(o2::framework::ConfigParamSpec{"use-ccdb", o2::framework::VariantType::Bool, false, {"enable access to ccdb cpv calibration objects"}}); + workflowOptions.push_back(o2::framework::ConfigParamSpec{"debug", o2::framework::VariantType::Bool, false, {"debug mode (store calibration objects to local file)"}}); + workflowOptions.push_back(o2::framework::ConfigParamSpec{"path", o2::framework::VariantType::String, "./", {"path to store temp files"}}); + workflowOptions.push_back(o2::framework::ConfigParamSpec{"configKeyValues", o2::framework::VariantType::String, "", {"Semicolon separated key=value strings"}}); +} + +#include "Framework/runDataProcessing.h" + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& configcontext) +{ + o2::framework::WorkflowSpec specs; + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + auto useCCDB = configcontext.options().get("use-ccdb"); + auto debugMode = configcontext.options().get("debug"); + auto path = configcontext.options().get("path"); + + specs.emplace_back(o2::focal::getPadPedestalCalibDevice(useCCDB, path, debugMode)); + return specs; +} \ No newline at end of file diff --git a/Detectors/FOCAL/doxymodules.h b/Detectors/FOCAL/doxymodules.h new file mode 100644 index 0000000000000..5fee41475ce28 --- /dev/null +++ b/Detectors/FOCAL/doxymodules.h @@ -0,0 +1,72 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/** + * @defgroup DetectorFOCAL FOCAL + * @brief FOCAL simulation and reconstruction + * + * See \ref refDetectorsFOCAL for more information + */ + +/** + * @defgroup FOCALbase FOCAL base + * @brief Main FOCAL components + * @ingroup DetectorFOCAL + * + * Main FOCAL components used in various libraries + * - Geometry + * - Mapping + * - Basic data types not part of the FOCAL format + */ + +/** + * @defgroup FOCALcalibration FOCAL calibration + * @brief FOCAL calibration objects + * @ingroup DetectorFOCAL + * + * FOCAL calibration objects + */ + +/** + * @defgroup FOCALcalib FOCAL calib + * @brief FOCAL bad channel calibration + * @ingroup DetectorFOCAL + * + * FOCAL calibrators and calibration workflows + * + */ + +/** + * @defgroup FOCALsimulation FOCAL simulation + * @brief FOCAL simulation code + * @ingroup DetectorFOCAL + * + * FOCAL simulation package. See \ref refFOCALsimulation + * for more information + */ + +/** + * @defgroup FOCALreconstruction FOCAL reconstruction + * @brief FOCAL reconstruction code + * @ingroup DetectorFOCAL + * + * FOCAL reconstruction package. See \ref refFOCALreconstruction + * for more information + */ + +/** + * @defgroup FOCALworkflow FOCAL workflow + * @brief FOCAL reconstruction workflow + * @ingroup DetectorFOCAL + * + * FOCAL reconstruction workflow package. See \ref refFOCALworkflow + * for more information. + */ \ No newline at end of file diff --git a/Detectors/FOCAL/reconstruction/CMakeLists.txt b/Detectors/FOCAL/reconstruction/CMakeLists.txt index 289e83014b113..759430cc329e1 100644 --- a/Detectors/FOCAL/reconstruction/CMakeLists.txt +++ b/Detectors/FOCAL/reconstruction/CMakeLists.txt @@ -34,4 +34,6 @@ o2_target_root_dictionary( o2_add_executable(rawreader-pad-rootify COMPONENT_NAME focal PUBLIC_LINK_LIBRARIES O2::DataFormatsFOCAL O2::FOCALReconstruction O2::DetectorsRaw ROOT::RIO ROOT::Tree - SOURCES run/rawReaderPadRootify.cxx) \ No newline at end of file + SOURCES run/rawReaderPadRootify.cxx) + +o2_data_file(COPY files DESTINATION Detectors/FOC) \ No newline at end of file diff --git a/Detectors/FOCAL/reconstruction/files/mapping_ib.data b/Detectors/FOCAL/reconstruction/files/mapping_ib.data new file mode 100644 index 0000000000000..45978a6334324 --- /dev/null +++ b/Detectors/FOCAL/reconstruction/files/mapping_ib.data @@ -0,0 +1,41 @@ +// FEE, Lane, Chip, Layer, Col, Row, Inv Col, Inv Row +// Layer 0, version 0 +0, 0, 0, 0, 0, 4, 0, 0 +0, 0, 1, 0, 1, 4, 0, 0 +0, 0, 2, 0, 2, 4, 0, 0 +0, 0, 3, 0, 0, 2, 0, 0 +0, 0, 4, 0, 1, 2, 0, 0 +0, 0, 5, 0, 2, 2, 0, 0 +0, 0, 6, 0, 0, 0, 0, 0 +0, 0, 7, 0, 1, 0, 0, 0 +0, 0, 8, 0, 2, 0, 0, 0 +// Layer 0, version 1 +1, 0, 0, 0, 0, 5, 0, 0 +1, 0, 1, 0, 1, 5, 0, 0 +1, 0, 2, 0, 2, 5, 0, 0 +1, 0, 3, 0, 0, 3, 0, 0 +1, 0, 4, 0, 1, 3, 0, 0 +1, 0, 5, 0, 2, 3, 0, 0 +1, 0, 6, 0, 0, 1, 0, 0 +1, 0, 7, 0, 1, 1, 0, 0 +1, 0, 8, 0, 2, 1, 0, 0 +// Layer 1, version 0 +2, 0, 0, 1, 0, 4, 0, 0 +2, 0, 1, 1, 1, 4, 0, 0 +2, 0, 2, 1, 2, 4, 0, 0 +2, 0, 3, 1, 0, 2, 0, 0 +2, 0, 4, 1, 1, 2, 0, 0 +2, 0, 5, 1, 2, 2, 0, 0 +2, 0, 6, 1, 0, 0, 0, 0 +2, 0, 7, 1, 1, 0, 0, 0 +2, 0, 8, 1, 2, 0, 0, 0 +// Layer 1, version 1 +3, 0, 0, 1, 0, 5, 0, 0 +3, 0, 1, 1, 1, 5, 0, 0 +3, 0, 2, 1, 2, 5, 0, 0 +3, 0, 3, 1, 0, 3, 0, 0 +3, 0, 4, 1, 1, 3, 0, 0 +3, 0, 5, 1, 2, 3, 0, 0 +3, 0, 6, 1, 0, 1, 0, 0 +3, 0, 7, 1, 1, 1, 0, 0 +3, 0, 8, 1, 2, 1, 0, 0 \ No newline at end of file diff --git a/Detectors/FOCAL/reconstruction/files/mapping_ob.data b/Detectors/FOCAL/reconstruction/files/mapping_ob.data new file mode 100644 index 0000000000000..47e471ead73e6 --- /dev/null +++ b/Detectors/FOCAL/reconstruction/files/mapping_ob.data @@ -0,0 +1,89 @@ +// FEE, Lane, Chip, Layer, Col, Row, Inv Col, Inv Row +// Layer0, v0 (upper and lower) +0, 8, 6, 0, 0, 5, 1, 0 +0, 8, 5, 0, 1, 5, 1, 0 +0, 8, 4, 0, 2, 5, 1, 0 +0, 8, 3, 0, 3, 5, 1, 0 +0, 8, 2, 0, 4, 5, 1, 0 +0, 8, 1, 0, 5, 5, 1, 0 +0, 8, 0, 0, 6, 5, 1, 0 +0, 6, 8, 0, 0, 4, 0, 1 +0, 6, 9, 0, 1, 4, 0, 1 +0, 6, 10, 0, 2, 4, 0, 1 +0, 6, 11, 0, 3, 4, 0, 1 +0, 6, 12, 0, 4, 4, 0, 1 +0, 6, 13, 0, 5, 4, 0, 1 +0, 6, 14, 0, 6, 4, 0, 1 +0, 24, 6, 0, 0, 1, 1, 0 +0, 24, 5, 0, 1, 1, 1, 0 +0, 24, 4, 0, 2, 1, 1, 0 +0, 24, 3, 0, 3, 1, 1, 0 +0, 24, 2, 0, 4, 1, 1, 0 +0, 24, 1, 0, 5, 1, 1, 0 +0, 24, 0, 0, 6, 1, 1, 0 +0, 22, 8, 0, 0, 0, 0, 1 +0, 22, 9, 0, 1, 0, 0, 1 +0, 22, 10, 0, 2, 0, 0, 1 +0, 22, 11, 0, 3, 0, 0, 1 +0, 22, 12, 0, 4, 0, 0, 1 +0, 22, 13, 0, 5, 0, 0, 1 +0, 22, 14, 0, 6, 0, 0, 1 +// Layer0, v1 (middle) +1, 6, 8, 0, 0, 3, 0, 0 +1, 6, 9, 0, 1, 3, 0, 0 +1, 6, 10, 0, 2, 3, 0, 0 +1, 6, 11, 0, 3, 3, 0, 0 +1, 6, 12, 0, 4, 3, 0, 0 +1, 6, 13, 0, 5, 3, 0, 0 +1, 6, 14, 0, 6, 3, 0, 0 +1, 8, 6, 0, 0, 2, 1, 1 +1, 8, 5, 0, 1, 2, 1, 1 +1, 8, 4, 0, 2, 2, 1, 1 +1, 8, 3, 0, 3, 2, 1, 1 +1, 8, 2, 0, 4, 2, 1, 1 +1, 8, 1, 0, 5, 2, 1, 1 +1, 8, 0, 0, 6, 2, 1, 1 +// Layer1, v0 (upper and lower) +2, 8, 6, 1, 0, 5, 1, 0 +2, 8, 5, 1, 1, 5, 1, 0 +2, 8, 4, 1, 2, 5, 1, 0 +2, 8, 3, 1, 3, 5, 1, 0 +2, 8, 2, 1, 4, 5, 1, 0 +2, 8, 1, 1, 5, 5, 1, 0 +2, 8, 0, 1, 6, 5, 1, 0 +2, 6, 8, 1, 0, 4, 0, 1 +2, 6, 9, 1, 1, 4, 0, 1 +2, 6, 10, 1, 2, 4, 0, 1 +2, 6, 11, 1, 3, 4, 0, 1 +2, 6, 12, 1, 4, 4, 0, 1 +2, 6, 13, 1, 5, 4, 0, 1 +2, 6, 14, 1, 6, 4, 0, 1 +2, 24, 6, 1, 0, 1, 1, 0 +2, 24, 5, 1, 1, 1, 1, 0 +2, 24, 4, 1, 2, 1, 1, 0 +2, 24, 3, 1, 3, 1, 1, 0 +2, 24, 2, 1, 4, 1, 1, 0 +2, 24, 1, 1, 5, 1, 1, 0 +2, 24, 0, 1, 6, 1, 1, 0 +2, 22, 8, 1, 0, 0, 0, 1 +2, 22, 9, 1, 1, 0, 0, 1 +2, 22, 10, 1, 2, 0, 0, 1 +2, 22, 11, 1, 3, 0, 0, 1 +2, 22, 12, 1, 4, 0, 0, 1 +2, 22, 13, 1, 5, 0, 0, 1 +2, 22, 14, 1, 6, 0, 0, 1 +// Layer1, v1 (middle) +1, 22, 8, 1, 0, 3, 0, 0 +1, 22, 9, 1, 1, 3, 0, 0 +1, 22, 10, 1, 2, 3, 0, 0 +1, 22, 11, 1, 3, 3, 0, 0 +1, 22, 12, 1, 4, 3, 0, 0 +1, 22, 13, 1, 5, 3, 0, 0 +1, 22, 14, 1, 6, 3, 0, 0 +1, 24, 6, 1, 0, 2, 1, 1 +1, 24, 5, 1, 1, 2, 1, 1 +1, 24, 4, 1, 2, 2, 1, 1 +1, 24, 3, 1, 3, 2, 1, 1 +1, 24, 2, 1, 4, 2, 1, 1 +1, 24, 1, 1, 5, 2, 1, 1 +1, 24, 0, 1, 6, 2, 1, 1 \ No newline at end of file diff --git a/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PadData.h b/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PadData.h index d9e5e55d53666..ab07d10e47fc9 100644 --- a/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PadData.h +++ b/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PadData.h @@ -27,7 +27,7 @@ namespace o2::focal class ASICData { public: - class IndexException : public std::exception + class IndexException final : public std::exception { public: IndexException() = default; @@ -125,7 +125,7 @@ class ASICContainer class PadData { public: - class IndexException : public std::exception + class IndexException final : public std::exception { public: IndexException() = default; diff --git a/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PadMapper.h b/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PadMapper.h index 8e84f132543ff..b85e7191eb00c 100644 --- a/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PadMapper.h +++ b/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PadMapper.h @@ -27,7 +27,7 @@ class PadMapper static constexpr std::size_t NROW = 9; static constexpr std::size_t NCHANNELS = NCOLUMN * NROW; - class PositionException : public std::exception + class PositionException final : public std::exception { public: PositionException(unsigned int column, unsigned int row) : mColumn(column), mRow(row), mMessage() @@ -51,7 +51,7 @@ class PadMapper std::string mMessage; }; - class ChannelIDException : public std::exception + class ChannelIDException final : public std::exception { public: ChannelIDException(unsigned int channelID) : mChannelID(channelID), mMessage() diff --git a/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelLaneData.h b/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelLaneData.h index 44a2256cc7202..7c9138e73db9b 100644 --- a/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelLaneData.h +++ b/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelLaneData.h @@ -46,7 +46,7 @@ class PixelLaneHandler public: static constexpr std::size_t NLANES = 28; - class LaneIndexException : public std::exception + class LaneIndexException final : public std::exception { public: LaneIndexException(int index) : std::exception(), mIndex(index), mMessage() diff --git a/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelMapper.h b/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelMapper.h index c35a7d3671b7f..40fa73a08d921 100644 --- a/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelMapper.h +++ b/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelMapper.h @@ -19,26 +19,35 @@ #include #include #include +#include namespace o2::focal { -class PixelMapping +class PixelMapper { public: + enum class MappingType_t { + MAPPING_IB, + MAPPING_OB, + MAPPING_UNKNOWN + }; + struct ChipIdentifier { + unsigned int mFEEID; unsigned int mLaneID; unsigned int mChipID; - bool operator==(const ChipIdentifier& other) const { return mLaneID == other.mLaneID && mChipID == other.mChipID; } + bool operator==(const ChipIdentifier& other) const { return mFEEID == other.mFEEID && mLaneID == other.mLaneID && mChipID == other.mChipID; } }; struct ChipPosition { unsigned int mColumn; unsigned int mRow; + unsigned int mLayer; bool mInvertColumn; bool mInvertRow; - bool operator==(const ChipPosition& other) const { return mColumn == other.mColumn && mRow == other.mRow; } + bool operator==(const ChipPosition& other) const { return mLayer == other.mLayer && mColumn == other.mColumn && mRow == other.mRow; } }; struct ChipIdentifierHasher { @@ -48,121 +57,89 @@ class PixelMapping size_t operator()(const ChipIdentifier& s) const { std::size_t seed = 0; + boost::hash_combine(seed, s.mFEEID); boost::hash_combine(seed, s.mChipID); boost::hash_combine(seed, s.mLaneID); return seed; } }; - class InvalidChipException : public std::exception + class InvalidChipException final : public std::exception { public: - InvalidChipException(unsigned int mappingVersion, PixelMapping::ChipIdentifier& identifier) : mMappingVersion(mappingVersion), mIdentifier(identifier), mMessage() + InvalidChipException(PixelMapper::ChipIdentifier& identifier) : mIdentifier(identifier), mMessage() { - mMessage = "Invalid chip identifier for mapping " + std::to_string(mMappingVersion) + ": lane " + std::to_string(mIdentifier.mLaneID) + ", chip " + std::to_string(mIdentifier.mChipID); + mMessage = "Invalid chip identifier: FEE " + std::to_string(mIdentifier.mFEEID) + ", lane " + std::to_string(mIdentifier.mLaneID) + ", chip " + std::to_string(mIdentifier.mChipID); } ~InvalidChipException() noexcept final = default; const char* what() const noexcept final { return mMessage.data(); } - const PixelMapping::ChipIdentifier& getIdentifier() const { return mIdentifier; } + const PixelMapper::ChipIdentifier& getIdentifier() const { return mIdentifier; } unsigned int getLane() const noexcept { return mIdentifier.mLaneID; } unsigned int getChipID() const noexcept { return mIdentifier.mChipID; } - int getMappingVersion() const noexcept { return mMappingVersion; } + unsigned int getFEEID() const noexcept { return mIdentifier.mFEEID; } void print(std::ostream& stream) const; private: - unsigned int mMappingVersion; - PixelMapping::ChipIdentifier mIdentifier; + PixelMapper::ChipIdentifier mIdentifier; std::string mMessage; }; - class VersionException : public std::exception + class UninitException final : public std::exception { public: - VersionException(unsigned int version) : mMappingVersion(version), mMessage() {} - ~VersionException() noexcept final = default; + UninitException() = default; + ~UninitException() noexcept final = default; - const char* what() const noexcept final { return mMessage.data(); } - int getMappingVersion() const noexcept { return mMappingVersion; } + const char* what() const noexcept final { return "Mapping is not initalized"; } void print(std::ostream& stream) const; - - private: - unsigned int mMappingVersion; - std::string mMessage; }; - PixelMapping() = default; - PixelMapping(unsigned int version); - virtual ~PixelMapping() = default; - - ChipPosition getPosition(unsigned int laneID, unsigned int chipID) const; - ChipPosition getPosition(const PixelChip& chip) const + class MappingNotSetException final : public std::exception { - return getPosition(chip.mLaneID, chip.mChipID); + public: + MappingNotSetException() = default; + ~MappingNotSetException() noexcept final = default; + const char* what() const noexcept final { return "Mapping file not set"; } + void print(std::ostream& stream) const; }; - virtual unsigned int getNumberOfRows() const = 0; - virtual unsigned int getNumberOfColumns() const = 0; - - protected: - int mVersion = -1; - bool mUseLanes = false; - std::unordered_map mMapping; -}; - -class PixelMappingOB : public PixelMapping -{ - public: - PixelMappingOB() = default; - PixelMappingOB(unsigned int version); - ~PixelMappingOB() final = default; - - void init(unsigned int version); - unsigned int getNumberOfRows() const final { return 6; } - unsigned int getNumberOfColumns() const final { return 7; } - - private: - void buildVersion0(); - void buildVersion1(); -}; - -class PixelMappingIB : public PixelMapping -{ - public: - PixelMappingIB() = default; - PixelMappingIB(unsigned int version); - ~PixelMappingIB() final = default; - - void init(unsigned int version); - unsigned int getNumberOfRows() const final { return 6; } - unsigned int getNumberOfColumns() const final { return 3; } - - private: - void buildVersion0(); - void buildVersion1(); -}; - -class PixelMapper -{ - public: - enum class MappingType_t { - MAPPING_IB, - MAPPING_OB - }; PixelMapper(MappingType_t mappingtype); ~PixelMapper() = default; - const PixelMapping& getMapping(unsigned int feeID) const; + ChipPosition getPosition(unsigned int feeID, unsigned int laneID, unsigned int chipID) const; + ChipPosition getPosition(unsigned int feeID, const PixelChip& chip) const + { + return getPosition(feeID, chip.mLaneID, chip.mChipID); + }; + + int getNumberOfColumns() const { return mNumberOfColumns; } + int getNumberOfRows() const { return mNumberOfRows; } MappingType_t getMappingType() const { return mMappingType; } + void setMappingFile(const std::string_view mappingfile, MappingType_t mappingtype) + { + mMappingFile = mappingfile; + mMappingType = mappingtype; + init(); + } + private: - MappingType_t mMappingType; - std::array, 2> mMappings; + void checkInitialized() const; + void init(); + std::unordered_map mMapping; + std::string mMappingFile; + MappingType_t mMappingType = MappingType_t::MAPPING_UNKNOWN; + int mNumberOfColumns = 0; + int mNumberOfRows = 0; + + ClassDefNV(PixelMapper, 1); }; -std::ostream& operator<<(std::ostream& stream, const PixelMapping::InvalidChipException& error); -std::ostream& operator<<(std::ostream& stream, const PixelMapping::VersionException& error); +std::ostream& operator<<(std::ostream& stream, const PixelMapper::InvalidChipException& error); +std::ostream& operator<<(std::ostream& stream, const PixelMapper::UninitException& error); +std::ostream& operator<<(std::ostream& stream, const PixelMapper::MappingNotSetException& error); } // namespace o2::focal -#endif // ALICEO2_FOCAL_PIXELMAPPER_H \ No newline at end of file +#endif // ALICEO2_FOCAL_PixelMapper_H \ No newline at end of file diff --git a/Detectors/FOCAL/reconstruction/src/FOCALReconstructionLinkDef.h b/Detectors/FOCAL/reconstruction/src/FOCALReconstructionLinkDef.h index e021f5e4c81af..e54263f45f830 100644 --- a/Detectors/FOCAL/reconstruction/src/FOCALReconstructionLinkDef.h +++ b/Detectors/FOCAL/reconstruction/src/FOCALReconstructionLinkDef.h @@ -21,9 +21,6 @@ #pragma link C++ class o2::focal::PadDecoder + ; #pragma link C++ class o2::focal::PadMapper + ; #pragma link C++ class o2::focal::PixelMapper + ; -#pragma link C++ class o2::focal::PixelMapping + ; -#pragma link C++ class o2::focal::PixelMappingIB + ; -#pragma link C++ class o2::focal::PixelMappingOB + ; #pragma link C++ class o2::focal::PixelDecoder + ; #pragma link C++ class o2::focal::PixelLaneHandler + ; #pragma link C++ class o2::focal::PixelLanePayload + ; diff --git a/Detectors/FOCAL/reconstruction/src/PixelDecoder.cxx b/Detectors/FOCAL/reconstruction/src/PixelDecoder.cxx index 20104f1b25d51..68a662a67fa2c 100644 --- a/Detectors/FOCAL/reconstruction/src/PixelDecoder.cxx +++ b/Detectors/FOCAL/reconstruction/src/PixelDecoder.cxx @@ -25,7 +25,6 @@ void PixelDecoder::reset() void PixelDecoder::decodeEvent(gsl::span payload) { - o2::InteractionRecord currentIR; bool physicsTrigger = false; mPixelData.clear(); @@ -37,6 +36,9 @@ void PixelDecoder::decodeEvent(gsl::span payload) if (word.isDataTrailer()) { // to be defined } + if (word.isDiagnosticWord()) { + // to be defined + } if (word.isTriggerWord()) { auto lastIR = currentIR; currentIR.orbit = word.orbit; @@ -182,7 +184,7 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::spanmIdentifier) << 8; decodedChips.push_back({0, laneID, static_cast(chipID), currentChipStatus, hits}); } else { // std::cout << "Skipping existing empty frame" << std::endl; @@ -196,7 +198,7 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::spanmChipID; activeChip = true; - currentChipStatus = PixelChip::EMPTYFRAME; + currentChipStatus = (chipheader->mIdentifier) << 8; // std::cout << "New chip (" << std::bitset<4>(chipheader->mIdentifier) << ") " << int(chipheader->mChipID) << ", BC (" << int(chipheader->mBunchCrossing) << "), empty " << (chipheader->isEmptyFrame() ? "yes" : "no") << std::endl; LOG(debug) << "New chip (" << std::bitset<4>(chipheader->mIdentifier) << ") " << int(chipheader->mChipID) << ", BC (" << int(chipheader->mBunchCrossing) << "), empty " << (chipheader->isEmptyFrame() ? "yes" : "no"); break; @@ -204,10 +206,7 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::span(currentptr); wordsize = sizeof(PixelWord::ChipTrailer) / sizeof(uint8_t); - if (currentChipStatus & PixelChip::DATAFRAME) { - // remove emptyframe bit in case dataframe bit is found - currentChipStatus &= ~(PixelChip::EMPTYFRAME); - } + currentChipStatus |= trailer->mReadoutFlags; // -> Combine hits to chip // -> Write hits to output container // std::cout << "Finished chip (" << std::bitset<4>(trailer->mIdentifier) << ") " << int(currentChipID) << " with " << hits.size() << " hits .. (Readout flags " << std::bitset<4>(trailer->mReadoutFlags) << ")" << std::endl; @@ -216,7 +215,7 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::spanmHits.size(); std::copy(hits.begin(), hits.end(), std::back_inserter(found->mHits)); - found->mStatusCode |= currentChipStatus; + found->mStatusCode = currentChipStatus; found->removeEmptyframe(); // std::cout << "Merging data with existing chip, Hits before: " << hitsbefore << ", after: " << found->mHits.size() << std::endl; LOG(debug) << "Merging data with existing chip, Hits before: " << hitsbefore << ", after: " << found->mHits.size(); @@ -242,7 +241,6 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::span(datashort.mData) << "] word (" << std::bitset<2>(datashort.mIdentifier) << ") with encoder " << std::bitset<4>(datashort.mEncoderID) << " and address " << std::bitset<10>(datashort.mAddress); wordsize = sizeof(PixelWord::DataShort) / sizeof(uint8_t); hits.push_back({AlpideX(currentRegion, datashort.mEncoderID, datashort.mAddress), AlpideY(datashort.mAddress)}); - currentChipStatus |= PixelChip::DATAFRAME; break; } case PixelWord::PixelWordType::DATA_LONG: { @@ -259,16 +257,13 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::span #include -#include - -using namespace o2::focal; - -PixelMapping::PixelMapping(unsigned int version) : mVersion(version) {} - -PixelMapping::ChipPosition PixelMapping::getPosition(unsigned int laneID, unsigned int chipID) const -{ - ChipIdentifier identifier{mUseLanes ? laneID : 0, chipID}; - auto found = mMapping.find(identifier); - if (found == mMapping.end()) { - throw InvalidChipException(mVersion, identifier); - } - return found->second; -} +#include -void PixelMapping::InvalidChipException::print(std::ostream& stream) const -{ - stream << mMessage; -} +#include -void PixelMapping::VersionException::print(std::ostream& stream) const -{ - stream << mMessage; -} +#include +#include -PixelMappingOB::PixelMappingOB(unsigned int version) : PixelMapping(version) { init(version); } +using namespace o2::focal; -void PixelMappingOB::init(unsigned int version) +PixelMapper::PixelMapper(PixelMapper::MappingType_t mappingtype) : mMappingType(mappingtype) { - if (version >= 2) { - throw VersionException(version); - } - std::cout << "Initializing OB mapping (" << version << ")" << std::endl; - switch (version) { - case 0: - buildVersion0(); - break; - - case 1: - buildVersion1(); + switch (mappingtype) { + case MappingType_t::MAPPING_IB: + mMappingFile = Form("%s/share/Detectors/FOC/files/mapping_ib.data", gSystem->Getenv("O2_ROOT")); break; - + case MappingType_t::MAPPING_OB: + mMappingFile = Form("%s/share/Detectors/FOC/files/mapping_ob.data", gSystem->Getenv("O2_ROOT")); default: break; + }; + + if (mMappingFile.length()) { + init(); } - mUseLanes = true; } -void PixelMappingOB::buildVersion0() +void PixelMapper::init() { + if (gSystem->AccessPathName(mMappingFile.data())) { + throw MappingNotSetException(); + } + LOG(debug) << "Reading pixel mapping from file: " << mMappingFile; mMapping.clear(); - /** Lane, Chip, Col, Row, Inv Col, Inv Row **/ - mMapping.insert({{8, 6}, {0, 5, true, false}}); - mMapping.insert({{8, 5}, {1, 5, true, false}}); - mMapping.insert({{8, 4}, {2, 5, true, false}}); - mMapping.insert({{8, 3}, {3, 5, true, false}}); - mMapping.insert({{8, 2}, {4, 5, true, false}}); - mMapping.insert({{8, 1}, {5, 5, true, false}}); - mMapping.insert({{8, 0}, {6, 5, true, false}}); - mMapping.insert({{6, 8}, {0, 4, false, true}}); - mMapping.insert({{6, 9}, {1, 4, false, true}}); - mMapping.insert({{6, 10}, {2, 4, false, true}}); - mMapping.insert({{6, 11}, {3, 4, false, true}}); - mMapping.insert({{6, 12}, {4, 4, false, true}}); - mMapping.insert({{6, 13}, {5, 4, false, true}}); - mMapping.insert({{6, 14}, {6, 4, false, true}}); - mMapping.insert({{24, 6}, {0, 1, true, false}}); - mMapping.insert({{24, 5}, {1, 1, true, false}}); - mMapping.insert({{24, 4}, {2, 1, true, false}}); - mMapping.insert({{24, 3}, {3, 1, true, false}}); - mMapping.insert({{24, 2}, {4, 1, true, false}}); - mMapping.insert({{24, 1}, {5, 1, true, false}}); - mMapping.insert({{24, 0}, {6, 1, true, false}}); - mMapping.insert({{22, 8}, {0, 0, false, true}}); - mMapping.insert({{22, 9}, {1, 0, false, true}}); - mMapping.insert({{22, 10}, {2, 0, false, true}}); - mMapping.insert({{22, 11}, {3, 0, false, true}}); - mMapping.insert({{22, 12}, {4, 0, false, true}}); - mMapping.insert({{22, 13}, {5, 0, false, true}}); - mMapping.insert({{22, 14}, {6, 0, false, true}}); + std::ifstream reader(mMappingFile); + std::string buffer; + while (std::getline(reader, buffer)) { + auto delimiter = buffer.find("//"); + std::string data = buffer; + if (delimiter == 0) { + // whole line commented out + continue; + } + if (delimiter != std::string::npos) { + data = buffer.substr(0, delimiter); + } + LOG(debug) << "Processing line: " << data; + std::stringstream decoder(data); + std::string linebuffer; + std::vector identifiers; + while (std::getline(decoder, linebuffer, ',')) { + identifiers.push_back(std::stoi(linebuffer)); + } + if (identifiers.size() < 8) { + LOG(error) << "Chip coordinates not fully defined (" << data << "), skipping ..."; + } + ChipIdentifier nextIdentifier; + nextIdentifier.mFEEID = identifiers[0]; + nextIdentifier.mLaneID = identifiers[1]; + nextIdentifier.mChipID = identifiers[2]; + ChipPosition nextPosition; + nextPosition.mLayer = identifiers[3]; + nextPosition.mColumn = identifiers[4]; + nextPosition.mRow = identifiers[5]; + nextPosition.mInvertColumn = (identifiers[6] == 1 ? true : false); + nextPosition.mInvertRow = (identifiers[7] == 1 ? true : false); + LOG(debug) << "Inserting chip: (FEE " << nextIdentifier.mFEEID << ", Lane " << nextIdentifier.mLaneID << ", Chip " << nextIdentifier.mChipID << ") -> (Layer " << nextPosition.mLayer << ", Col " << nextPosition.mColumn << ", Row " << nextPosition.mRow << ", Inv Col " << (nextPosition.mInvertColumn ? "yes" : "no") << ", Inv Row " << (nextPosition.mInvertRow ? "yes" : "no") << ")"; + if (mMapping.find(nextIdentifier) != mMapping.end()) { + LOG(error) << "Chip with FEE" << nextIdentifier.mFEEID << ", Lane " << nextIdentifier.mLaneID << ", Chip " << nextIdentifier.mChipID << " already present, not overwriting ..."; + continue; + } + mMapping.insert({nextIdentifier, nextPosition}); + if (nextPosition.mRow + 1 > mNumberOfRows) { + mNumberOfRows = nextPosition.mRow + 1; + } + if (nextPosition.mColumn + 1 > mNumberOfColumns) { + mNumberOfColumns = nextPosition.mColumn + 1; + } + } + LOG(info) << "Pixel Mapper: Found " << mMapping.size() << " chips, in " << mNumberOfColumns << " colums and " << mNumberOfRows << " rows"; + reader.close(); } -void PixelMappingOB::buildVersion1() +PixelMapper::ChipPosition PixelMapper::getPosition(unsigned int feeID, unsigned int laneID, unsigned int chipID) const { - mMapping.clear(); - /** Lane, Chip, Col, Row, Inv Col, Inv Row **/ - mMapping.insert({{6, 8}, {0, 3, false, false}}); - mMapping.insert({{6, 9}, {1, 3, false, false}}); - mMapping.insert({{6, 10}, {2, 3, false, false}}); - mMapping.insert({{6, 11}, {3, 3, false, false}}); - mMapping.insert({{6, 12}, {4, 3, false, false}}); - mMapping.insert({{6, 13}, {5, 3, false, false}}); - mMapping.insert({{6, 14}, {6, 3, false, false}}); - mMapping.insert({{8, 6}, {0, 2, true, true}}); - mMapping.insert({{8, 5}, {1, 2, true, true}}); - mMapping.insert({{8, 4}, {2, 2, true, true}}); - mMapping.insert({{8, 3}, {3, 2, true, true}}); - mMapping.insert({{8, 2}, {4, 2, true, true}}); - mMapping.insert({{8, 1}, {5, 2, true, true}}); - mMapping.insert({{8, 0}, {6, 2, true, true}}); + auto cardindex = feeID & 0x00FF; + checkInitialized(); + ChipIdentifier identifier{cardindex, laneID, chipID}; + auto found = mMapping.find(identifier); + if (found == mMapping.end()) { + throw InvalidChipException(identifier); + } + return found->second; } -PixelMappingIB::PixelMappingIB(unsigned int version) : PixelMapping(version) { init(version); } - -void PixelMappingIB::init(unsigned int version) +void PixelMapper::checkInitialized() const { - if (version >= 2) { - throw VersionException(version); - } - std::cout << "Initializing IB mapping (" << version << ")" << std::endl; - switch (version) { - case 0: - buildVersion0(); - break; - - case 1: - buildVersion1(); - break; - - default: - break; + if (!mMapping.size()) { + throw UninitException(); } - mUseLanes = false; } -void PixelMappingIB::buildVersion0() +void PixelMapper::InvalidChipException::print(std::ostream& stream) const { - mMapping.clear(); - mMapping.insert({{0, 0}, {0, 4, false, false}}); - mMapping.insert({{0, 1}, {1, 4, false, false}}); - mMapping.insert({{0, 2}, {2, 4, false, false}}); - mMapping.insert({{0, 3}, {0, 2, false, false}}); - mMapping.insert({{0, 4}, {1, 2, false, false}}); - mMapping.insert({{0, 5}, {2, 2, false, false}}); - mMapping.insert({{0, 6}, {0, 0, false, false}}); - mMapping.insert({{0, 7}, {1, 0, false, false}}); - mMapping.insert({{0, 8}, {2, 0, false, false}}); + stream << mMessage; } -void PixelMappingIB::buildVersion1() +void PixelMapper::UninitException::print(std::ostream& stream) const { - mMapping.clear(); - mMapping.insert({{0, 0}, {0, 5, false, false}}); - mMapping.insert({{0, 1}, {1, 5, false, false}}); - mMapping.insert({{0, 2}, {2, 5, false, false}}); - mMapping.insert({{0, 3}, {0, 3, false, false}}); - mMapping.insert({{0, 4}, {1, 3, false, false}}); - mMapping.insert({{0, 5}, {2, 3, false, false}}); - mMapping.insert({{0, 6}, {0, 1, false, false}}); - mMapping.insert({{0, 7}, {1, 1, false, false}}); - mMapping.insert({{0, 8}, {2, 1, false, false}}); + stream << what(); } -PixelMapper::PixelMapper(PixelMapper::MappingType_t mappingtype) : mMappingType(mappingtype) +void PixelMapper::MappingNotSetException::print(std::ostream& stream) const { - switch (mappingtype) { - case MappingType_t::MAPPING_IB: - for (int iversion = 0; iversion < 2; iversion++) { - mMappings[iversion] = std::make_shared(iversion); - } - break; - case MappingType_t::MAPPING_OB: - for (int iversion = 0; iversion < 2; iversion++) { - mMappings[iversion] = std::make_shared(iversion); - } - }; + stream << what(); } -const PixelMapping& PixelMapper::getMapping(unsigned int feeID) const +std::ostream& o2::focal::operator<<(std::ostream& stream, const PixelMapper::InvalidChipException& error) { - return *(mMappings[feeID % 2]); + error.print(stream); + return stream; } -std::ostream& o2::focal::operator<<(std::ostream& stream, const PixelMapping::InvalidChipException& error) +std::ostream& o2::focal::operator<<(std::ostream& stream, const PixelMapper::UninitException& error) { error.print(stream); return stream; } -std::ostream& o2::focal::operator<<(std::ostream& stream, const PixelMapping::VersionException& error) +std::ostream& o2::focal::operator<<(std::ostream& stream, const PixelMapper::MappingNotSetException& error) { error.print(stream); return stream; -} +} \ No newline at end of file diff --git a/Detectors/FOCAL/simulation/CMakeLists.txt b/Detectors/FOCAL/simulation/CMakeLists.txt new file mode 100644 index 0000000000000..b838f563a2e0d --- /dev/null +++ b/Detectors/FOCAL/simulation/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(FOCALSimulation + SOURCES src/Detector.cxx + PUBLIC_LINK_LIBRARIES ROOT::TreePlayer O2::FOCALBase O2::DetectorsBase O2::SimConfig O2::SimulationDataFormat O2::Headers O2::DataFormatsFOCAL + AliceO2::InfoLogger + Microsoft.GSL::GSL) + +o2_target_root_dictionary( + FOCALSimulation + HEADERS include/FOCALSimulation/Detector.h +) + +o2_data_file(COPY geometryFiles DESTINATION Detectors/Geometry/FOC/) +o2_data_file(COPY data DESTINATION Detectors/FOC/simulation) diff --git a/Detectors/FOCAL/simulation/data/simcuts.dat b/Detectors/FOCAL/simulation/data/simcuts.dat new file mode 100644 index 0000000000000..870e38182f01c --- /dev/null +++ b/Detectors/FOCAL/simulation/data/simcuts.dat @@ -0,0 +1,18 @@ +* FOCAL +* ==== +* +* Med GAM ELEC NHAD CHAD MUON EBREM MUHAB EDEL MUDEL MUPA ANNI BREM COMP DCAY DRAY HADR LOSS MULS PAIR PHOT RAYL STRA +* Tungsten +FOC 0 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +* Si sensor +FOC 1 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* Si pixel +FOC 2 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* G10 plate +FOC 3 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +* Alloy +FOC 6 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +* Aluminium +FOC 11 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +* Air +FOC 13 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 diff --git a/Detectors/FOCAL/simulation/geometryFiles/geometry.txt b/Detectors/FOCAL/simulation/geometryFiles/geometry.txt new file mode 100644 index 0000000000000..a0508ab16e408 --- /dev/null +++ b/Detectors/FOCAL/simulation/geometryFiles/geometry.txt @@ -0,0 +1,86 @@ +# Pad layer materials updated to match the setup used in test beams. Total thickness is 8.5mm +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PAD_S0 Alloy 0 0 9. 8. 0.35 + COMPOSITION_PAD_S1 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S2 SiPad 0 0 9. 8. 0.03 + COMPOSITION_PAD_S3 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S4 Cu 0 0 9. 8. 0.014 + COMPOSITION_PAD_S5 Air 0 0 9. 8. 0.296 + # Replica of above pad layers to 50 layers + COMMAND_NUMBER_OF_PAD_LAYERS 18 +# HCAL layers +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_HCAL_S0 Pb 0 0 2.5 2.5 3.0 + COMPOSITION_HCAL_S1 Scint 0 0 2.5 2.5 0.2 + COMMAND_NUMBER_OF_HCAL_LAYERS 34 + COMMAND_NUMBER_OF_SEGMENTS 54 +# Pixel layers total thickness is 8.5mm as in test beam simulation. +# TODO: change G10 material to Al and remove copper ground (requires changes in AliFOCALv2.cxx) +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PIX_S0 Alloy 0 0 3.0 2.74 0.35 + COMPOSITION_PIX_S1 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S2 Si 0 0 3.0 2.74 0.047 + COMPOSITION_PIX_S3 SiPix 0 0 3.0 2.74 0.003 + COMPOSITION_PIX_S4 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S5 Cu 0 0 3.0 2.74 0.001 + COMPOSITION_PIX_S6 Air 0 0 3.0 2.74 0.249 +COMMAND_INSERT_PIX_AT_L4 +COMMAND_INSERT_PIX_AT_L9 +# COMMAND_INSERT_STR_AT_L4 +# COMMAND_INSERT_STR_AT_L6 +# COMMAND_INSERT_STR_AT_L8 +#Front Matter definition + COMPOSITION_FM_S0 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S1 SiStripX 0 0 5. 5. 0.05 + COMPOSITION_FM_S2 G10 0 0 5. 5. 0.05 + COMPOSITION_FM_S3 Air 0 0 5. 5. 0.01 + COMPOSITION_FM_S4 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S5 SiStripY 0 0 5. 5. 0.05 + COMPOSITION_FM_S6 G10 0 0 5. 5. 0.05 +# PIXEL readout + GLOBAL_PIX_NX 15 + GLOBAL_PIX_NY 3 + GLOBAL_PIX_OffsetX 1.2 + GLOBAL_PIX_OffsetY 0.09 + GLOBAL_PIX_SKIN 0.004 +# pxel size in cm + COMMAND_PIXEL_READOUT_ON 0.005 +# Pad information + GLOBAL_PAD_SIZE_X_Y 1 + GLOBAL_PAD_NX_NY 8 + GLOBAL_PAD_NX 9 + GLOBAL_PAD_NY 8 + GLOBAL_PAD_PPTOL 0. + GLOBAL_PAD_SKIN 0.2 +# Global information (TOL:1cm of T-T space filled with "AIR") + GLOBAL_PAD_SUPERMODULE_X 5 + GLOBAL_PAD_SUPERMODULE_Y 1 + GLOBAL_SUPERMODULE_TOLX 0. Air + GLOBAL_SUPERMODULE_TOLY 0. Air + GLOBAL_TOWER_TOL 0. Air + GLOBAL_TOWER_TOLX 0.02 Air + GLOBAL_TOWER_TOLY 0.8 Al + GLOBAL_FOCAL_Z 762.9 + GLOBAL_Tower_NX 2 + GLOBAL_Tower_NY 11 + GLOBAL_MIDDLE_TOWER_OFFSET 5 + GLOBAL_NSTRIPS 128 + GLOBAL_STRIPSIZE_LONG 9.0 + GLOBAL_STRIPSIZE_WIDTH 0.07 + GLOBAL_HCAL_TOWER_SIZE 2.5 + GLOBAL_HCAL_TOWER_NY 40 + GLOBAL_HCAL_TOWER_NX 38 +# COMMAND_INSERT_FRONT_PAD_LAYERS +# COMMAND_INSERT_HCAL_READOUT +# New VIRTUAL settings + VIRTUAL_N_SEGMENTS 7 + # N Start End PadSize RelThickness IsPixel PixelTreshold [eV] + # Layer Layer + VIRTUAL_SEGMENT_LAYOUT_N0 0 3 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N1 4 4 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N2 5 8 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N3 9 9 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N4 10 14 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N5 15 19 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N6 20 53 2.5 1.0 2 5000 +#EOF diff --git a/Detectors/FOCAL/simulation/geometryFiles/geometry_Sheets.txt b/Detectors/FOCAL/simulation/geometryFiles/geometry_Sheets.txt new file mode 100644 index 0000000000000..358fcef2cd29d --- /dev/null +++ b/Detectors/FOCAL/simulation/geometryFiles/geometry_Sheets.txt @@ -0,0 +1,86 @@ +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PAD_S0 Alloy 0 0 9. 8. 0.35 + COMPOSITION_PAD_S1 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S2 SiPad 0 0 9. 8. 0.03 + COMPOSITION_PAD_S3 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S4 Cu 0 0 9. 8. 0.014 + COMPOSITION_PAD_S5 Air 0 0 9. 8. 0.296 + # Replica of above pad layers to 50 layers + COMMAND_NUMBER_OF_PAD_LAYERS 18 +# HCAL layers +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_HCAL_S0 CuHCAL 0 0 49.81 0.20 110 + COMPOSITION_HCAL_S1 Scint 0 0 0.1 0.1 110 + COMPOSITION_HCAL_S2 CuHCAL 0 0 49.81 0.15 110 + COMMAND_NUMBER_OF_HCAL_LAYERS 1 + COMMAND_NUMBER_OF_SEGMENTS 21 +# Strip sectors +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PIX_S0 Alloy 0 0 3.0 2.74 0.35 + COMPOSITION_PIX_S1 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S2 Si 0 0 3.0 2.74 0.047 + COMPOSITION_PIX_S3 SiPix 0 0 3.0 2.74 0.003 + COMPOSITION_PIX_S4 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S5 Cu 0 0 3.0 2.74 0.001 + COMPOSITION_PIX_S6 Air 0 0 3.0 2.74 0.249 +COMMAND_INSERT_PIX_AT_L4 +COMMAND_INSERT_PIX_AT_L9 +# COMMAND_INSERT_STR_AT_L4 +# COMMAND_INSERT_STR_AT_L6 +# COMMAND_INSERT_STR_AT_L8 +#Front Matter definition + COMPOSITION_FM_S0 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S1 SiStripX 0 0 5. 5. 0.05 + COMPOSITION_FM_S2 G10 0 0 5. 5. 0.05 + COMPOSITION_FM_S3 Air 0 0 5. 5. 0.01 + COMPOSITION_FM_S4 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S5 SiStripY 0 0 5. 5. 0.05 + COMPOSITION_FM_S6 G10 0 0 5. 5. 0.05 +# PIXEL readout + GLOBAL_PIX_NX 15 + GLOBAL_PIX_NY 3 + GLOBAL_PIX_OffsetX 1.2 + GLOBAL_PIX_OffsetY 0.09 + GLOBAL_PIX_SKIN 0.004 +# pxel size in cm + COMMAND_PIXEL_READOUT_ON 0.005 +# Pad information + GLOBAL_PAD_SIZE_X_Y 1 + GLOBAL_PAD_NX_NY 8 + GLOBAL_PAD_NX 9 + GLOBAL_PAD_NY 8 + GLOBAL_PAD_PPTOL 0. + GLOBAL_PAD_SKIN 0.2 +# Global information (TOL:1cm of T-T space filled with "AIR") + GLOBAL_PAD_SUPERMODULE_X 5 + GLOBAL_PAD_SUPERMODULE_Y 1 + GLOBAL_SUPERMODULE_TOLX 0. Air + GLOBAL_SUPERMODULE_TOLY 0. Air + GLOBAL_TOWER_TOL 0. Air + GLOBAL_TOWER_TOLX 0.02 Air + GLOBAL_TOWER_TOLY 0.8 Al + GLOBAL_FOCAL_Z 764.47 + GLOBAL_Tower_NX 2 + GLOBAL_Tower_NY 11 + GLOBAL_MIDDLE_TOWER_OFFSET 5 + GLOBAL_NSTRIPS 128 + GLOBAL_STRIPSIZE_LONG 9.0 + GLOBAL_STRIPSIZE_WIDTH 0.07 + GLOBAL_HCAL_PITCH_SIZE 0.4 + GLOBAL_HCAL_TOWER_NY 72 + GLOBAL_HCAL_TOWER_NX 62 + GLOBAL_HCAL_BEAMPIPE 8.4 +# COMMAND_INSERT_FRONT_PAD_LAYERS +# COMMAND_INSERT_HCAL_READOUT +# New VIRTUAL settings + VIRTUAL_N_SEGMENTS 7 + # N Start End PadSize RelThickness IsPixel PixelTreshold [eV] + # Layer Layer + VIRTUAL_SEGMENT_LAYOUT_N0 0 3 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N1 4 4 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N2 5 8 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N3 9 9 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N4 10 14 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N5 15 19 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N6 20 20 0.4 1.0 2 5000 +#EOF diff --git a/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt b/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt new file mode 100644 index 0000000000000..31f6940224337 --- /dev/null +++ b/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt @@ -0,0 +1,88 @@ +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PAD_S0 Alloy 0 0 9. 8. 0.35 + COMPOSITION_PAD_S1 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S2 SiPad 0 0 9. 8. 0.03 + COMPOSITION_PAD_S3 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S4 Cu 0 0 9. 8. 0.014 + COMPOSITION_PAD_S5 Air 0 0 9. 8. 0.296 + # Replica of above pad layers to 50 layers + COMMAND_NUMBER_OF_PAD_LAYERS 18 +# HCAL layers +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_HCAL_S1 Scint 0 0 0.1 0.1 110 + COMPOSITION_HCAL_S0 CuHCAL 0 0 0.25 0.25 110 + COMMAND_NUMBER_OF_HCAL_LAYERS 1 + COMMAND_NUMBER_OF_SEGMENTS 21 +# Strip sectors +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PIX_S0 Alloy 0 0 3.0 2.74 0.35 + COMPOSITION_PIX_S1 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S2 Si 0 0 3.0 2.74 0.047 + COMPOSITION_PIX_S3 SiPix 0 0 3.0 2.74 0.003 + COMPOSITION_PIX_S4 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S5 Cu 0 0 3.0 2.74 0.001 + COMPOSITION_PIX_S6 Air 0 0 3.0 2.74 0.249 +COMMAND_INSERT_PIX_AT_L4 +COMMAND_INSERT_PIX_AT_L9 +# COMMAND_INSERT_STR_AT_L4 +# COMMAND_INSERT_STR_AT_L6 +# COMMAND_INSERT_STR_AT_L8 +#Front Matter definition + COMPOSITION_FM_S0 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S1 SiStripX 0 0 5. 5. 0.05 + COMPOSITION_FM_S2 G10 0 0 5. 5. 0.05 + COMPOSITION_FM_S3 Air 0 0 5. 5. 0.01 + COMPOSITION_FM_S4 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S5 SiStripY 0 0 5. 5. 0.05 + COMPOSITION_FM_S6 G10 0 0 5. 5. 0.05 +# PIXEL readout + GLOBAL_PIX_NX 15 + GLOBAL_PIX_NY 3 + GLOBAL_PIX_OffsetX 1.2 + GLOBAL_PIX_OffsetY 0.09 + GLOBAL_PIX_SKIN 0.004 +# pxel size in cm + COMMAND_PIXEL_READOUT_ON 0.005 +# Pad information + GLOBAL_PAD_SIZE_X_Y 1 + GLOBAL_PAD_NX_NY 8 + GLOBAL_PAD_NX 9 + GLOBAL_PAD_NY 8 + GLOBAL_PAD_PPTOL 0. + GLOBAL_PAD_SKIN 0.2 +# Global information (TOL:1cm of T-T space filled with "AIR") + GLOBAL_PAD_SUPERMODULE_X 5 + GLOBAL_PAD_SUPERMODULE_Y 1 + GLOBAL_SUPERMODULE_TOLX 0. Air + GLOBAL_SUPERMODULE_TOLY 0. Air + GLOBAL_TOWER_TOL 0. Air + GLOBAL_TOWER_TOLX 0.02 Air + GLOBAL_TOWER_TOLY 0.8 Al + GLOBAL_FOCAL_Z 763.5 +# Open the detector on the right and left in cm, +# can only work if the GLOBAL_HCAL_TOWER_NY is odd number and GLOBAL_HCAL_TOWER_NX is even number +# GLOBAL_DetectorOpen_Right 5 +# GLOBAL_DetectorOpen_Left 5 + GLOBAL_Tower_NX 2 + GLOBAL_Tower_NY 11 + GLOBAL_MIDDLE_TOWER_OFFSET 5 + GLOBAL_NSTRIPS 128 + GLOBAL_STRIPSIZE_LONG 9.0 + GLOBAL_STRIPSIZE_WIDTH 0.07 + GLOBAL_HCAL_TOWER_SIZE 6.55 + GLOBAL_HCAL_TOWER_NY 15 + GLOBAL_HCAL_TOWER_NX 15 +# COMMAND_INSERT_FRONT_PAD_LAYERS +# COMMAND_INSERT_HCAL_READOUT +# New VIRTUAL settings + VIRTUAL_N_SEGMENTS 7 + # N Start End PadSize RelThickness IsPixel PixelTreshold [eV] + # Layer Layer + VIRTUAL_SEGMENT_LAYOUT_N0 0 3 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N1 4 4 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N2 5 8 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N3 9 9 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N4 10 14 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N5 15 19 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N6 20 20 0.25 1.0 2 5000 +#EOF diff --git a/Detectors/FOCAL/simulation/include/FOCALSimulation/Detector.h b/Detectors/FOCAL/simulation/include/FOCALSimulation/Detector.h new file mode 100644 index 0000000000000..5f6bed3a037b0 --- /dev/null +++ b/Detectors/FOCAL/simulation/include/FOCALSimulation/Detector.h @@ -0,0 +1,240 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_FOCAL_DETECTOR_H_ +#define ALICEO2_FOCAL_DETECTOR_H_ + +#include + +#include "DetectorsBase/Detector.h" +#include "FOCALBase/Hit.h" +#include "FOCALBase/Geometry.h" +#include "TGeoManager.h" + +class FairVolume; + +namespace o2::focal +{ + +class Hit; +class Geometry; + +/// \struct Parent +/// \brief Information about superparent (particle entering any FOCAL volume) +/// \ingroup FOCALsimulation +struct Parent { + int mPDG; ///< PDG code + double mEnergy; ///< Total energy + bool mHasTrackReference; ///< Flag indicating whether parent has a track reference +}; + +/// \class Detector +/// \brief FOCAL detector simulation +/// \ingroup FOCALsimulation +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \since June 6, 2024 +/// \author Adam Matyja , Institute of Nuclear Physics, PAN, Cracov, Poland +/// \since June 24, 2024 +/// class based on AliFOCALv2.h in aliroot +/// It builds the ECAL (Adam) and HCAL (Hadi) seperately +/// For the ECAL: it builds it tower by tower +/// For the HCAL: +/// +/// The detector class handles the implementation of the FOCAL detector +/// within the virtual Monte-Carlo framework and the simulation of the +/// FOCAL detector up to hit generation +class Detector : public o2::base::DetImpl +{ + public: + enum MediumType_t { ID_TUNGSTEN = 0, + ID_SIPAD = 1, + ID_SIPIX = 2, + ID_G10 = 3, + ID_COPPER = 4, + ID_STEEL = 5, + ID_ALLOY = 6, + ID_CERAMIC = 7, + ID_PB = 8, + ID_SC = 9, + ID_SIINSENS = 10, + ID_ALUMINIUM = 11, + ID_VAC = 12, + ID_AIR = 13 }; + /// \brief Dummy constructor + Detector() = default; + + /// \brief Main constructor + /// + /// \param isActive Switch whether detector is active in simulation + Detector(Bool_t isActive, std::string geofilename = "default"); + + /// \brief Destructor + ~Detector() override; + + /// \brief Initializing detector + void InitializeO2Detector() override; + + /// \brief Processing hit creation in the FOCAL sensitive volume + /// \param v Current sensitive volume + Bool_t ProcessHits(FairVolume* v = nullptr) final; + + /// \brief Add FOCAL hit + /// \param trackID Index of the track in the MC stack + /// \param primary Index of the primary particle in the MC stack + /// \param initialEnergy Energy of the particle entering the FOCAL + /// \param detID Index of the detector (cell) for which the hit is created + /// \param pos Position vector of the particle at the hit + /// \param mom Momentum vector of the particle at the hit + /// \param time Time of the hit + /// \param energyloss Energy deposit in FOCAL + /// \return Pointer to the current hit + /// + /// Internally adding hits coming from the same track + Hit* AddHit(int trackID, int primary, double initialEnergy, int detID, o2::focal::Hit::Subsystem_t subsystem, + const math_utils::Point3D& pos, double time, double energyloss); + + /// \brief register container with hits + void Register() override; + + /// \brief Get access to the hits + /// \return Hit collection + std::vector* getHits(Int_t iColl) const + { + if (iColl == 0) { + return mHits; + } + return nullptr; + } + + /// \brief Clean point collection + void Reset() final; + + /// \brief Steps to be carried out at the end of the event + /// + /// For FOCAL cleaning the hit collection and the lookup table + void EndOfEvent() final; + + /// \brief Begin primaray + /// + /// Caching current primary ID and set current parent ID to the + /// current primary ID + void BeginPrimary() override; + + /// \brief Finish current primary + /// + /// Reset caches for current primary, current parent and current cell + void FinishPrimary() override; + + /// \brief Get the FOCAL geometry desciption + /// \return Access to the FOCAL Geometry description + /// + /// Will be created the first time the function is called + Geometry* getGeometry(std::string name = ""); + + /// \brief Try to find hit with same cell and parent track ID + /// \param parentID ID of the parent track + /// \param col Column of the cell + /// \param row Row of the cell + /// \param Layer Layer of cell + Hit* FindHit(int parentID, int col, int row, int layer); + + protected: + /// \brief Creating detector materials for the FOCAL detector + void CreateMaterials(); + + virtual void addAlignableVolumes() const override; + void addAlignableVolumesHCAL() const; + void addAlignableVolumesECAL() const; + + void ConstructGeometry() override; + + virtual void CreateHCALSpaghetti(); + virtual void CreateHCALSandwich(); + virtual void CreateHCALSheets(); + + TGeoVolumeAssembly* CreatePitchAssembly(double Lx = 498.1, + double Ly1 = 2.0, + double Ly2 = 1.5, + double Lz = 1100.0, + double hole_diameter = 1.1, + double hole_spacing = 4.0, + int nholes = 124, + double fiber_radius = 0.5, + std::string suffix = ""); + + /// \brief Generate ECAL geometry + void CreateECALGeometry(); + + /// \brief Add new superparent to the container + /// \param trackID Track ID of the superparent + /// \param pdg PDG code of the superparent + /// \param energy Energy of the superparent + Parent* AddSuperparent(int trackID, int pdg, double energy); + + /// \brief Processing hit creation in the ECAL Pad sensitive volume + /// \param v Current sensitive volume + bool ProcessHitsEPad(FairVolume* v = nullptr); + + /// \brief Processing hit creation in the ECAL Pixel sensitive volume + /// \param v Current sensitive volume + bool ProcessHitsEPix(FairVolume* v = nullptr); + + /// \brief Processing hit creation in the HCAL sensitive volume + /// \param v Current sensitive volume + bool ProcessHitsHCAL(FairVolume* v = nullptr); + + private: + /// \brief Copy constructor (used in MT) + Detector(const Detector& rhs); + + Geometry* mGeometry; //! mGeoCompositions; //!* mHits; ///< Container with hits + std::unordered_map mHitIndexMapping; ///< Mapping the hits to a cell in the detector + + std::unordered_map mSuperParentsIndices; //! mSuperParents; //! mSensitive; //! + friend class o2::base::DetImpl; + ClassDefOverride(Detector, 1); +}; +} // namespace o2::focal + +#ifdef USESHM +namespace o2 +{ +namespace base +{ +template <> +struct UseShm { + static constexpr bool value = true; +}; +} // namespace base +} // namespace o2 +#endif + +#endif // ALICEO2_FOCAL_DETECTOR_H_ diff --git a/Detectors/FOCAL/simulation/src/Detector.cxx b/Detectors/FOCAL/simulation/src/Detector.cxx new file mode 100644 index 0000000000000..464e57c07e676 --- /dev/null +++ b/Detectors/FOCAL/simulation/src/Detector.cxx @@ -0,0 +1,1438 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include +#include +#include + +#include + +#include "DetectorsBase/Stack.h" +#include "FOCALSimulation/Detector.h" +#include "FOCALBase/Geometry.h" +#include "FOCALBase/Hit.h" + +using namespace o2::focal; + +Detector::Detector(bool active, std::string geofilename) + : o2::base::DetImpl("FOC", active), + mHits(o2::utils::createSimVector()), + mHitIndexMapping(), + mGeometry(nullptr), + mMedSensHCal(-1), + mMedSensECalPad(-1), + mMedSensECalPix(-1), + mGeoCompositions(), + mSuperParentsIndices(), + mSuperParents(), + mCurrentSuperparent(nullptr), + mCurrentTrack(-1), + mCurrentPrimaryID(-1), + mCurrentParentID(-1), + mVolumeIDScintillator(-1) +{ + mGeometry = getGeometry(geofilename); + if (!mGeometry) { + LOG(fatal) << "Geometry is nullptr"; + } +} + +Detector::Detector(const Detector& rhs) + : o2::base::DetImpl(rhs) +{ + mGeometry = rhs.mGeometry; + mMedSensHCal = rhs.mMedSensHCal; + mMedSensECalPad = rhs.mMedSensECalPad; + mMedSensECalPix = rhs.mMedSensECalPix; + mSensitive = rhs.mSensitive; + // mSensitiveHCAL = rhs.mSensitiveHCAL; + // mSensitiveECALPad = rhs.mSensitiveECALPad; + // mSensitiveECALPix = rhs.mSensitiveECALPix; + mVolumeIDScintillator = rhs.mVolumeIDScintillator; +} + +Detector::~Detector() +{ + o2::utils::freeSimVector(mHits); +} + +Geometry* Detector::getGeometry(std::string name) +{ + if (!mGeometry) { + mGeometry = Geometry::getInstance(name); + } + if (!mGeometry) { + LOG(error) << "Failure accessing geometry"; + } + return mGeometry; +} + +void Detector::InitializeO2Detector() +{ + + LOG(info) << "Intializing FOCAL detector"; + + // All FOCAL volumes must be declared as sensitive, otherwise + // the decay chains are broken by volumes not processed in ProceeHits + for (const auto& child : mSensitive) { + LOG(debug1) << "Adding sensitive volume " << child; + auto svolID = registerSensitiveVolumeAndGetVolID(child); + // HCAL + if (child == "ScintFiber" || child == "HScint") { + LOG(debug1) << "Adding ScintFiber/HScint volume as sensitive volume with ID " << svolID; + mVolumeIDScintillator = svolID; + } + // ECAL Pads + else if (child == "EMSC1" || child == "EMSC2") { + LOG(debug1) << "Adding EMC SILICON volume as sensitive volume with ID " << svolID; + mVolumeIDScintillator = svolID; + } + } + + mMedSensHCal = getMediumID(ID_SC); + mMedSensECalPad = getMediumID(ID_SIPAD); + mMedSensECalPix = getMediumID(ID_SIPIX); +} + +Bool_t Detector::ProcessHits(FairVolume* v) +{ + int track = fMC->GetStack()->GetCurrentTrackNumber(), + directparent = fMC->GetStack()->GetCurrentParentTrackNumber(); + // Like other calorimeters FOCAL will create a huge amount of shower particles during tracking + // Instead, the hits should be assigned to the incoming particle in FOCAL. + // Implementation of the incoming particle search taken from implementation in EMCAL. + if (track != mCurrentTrack) { + LOG(debug4) << "Doing new track " << track << " current (" << mCurrentTrack << "), direct parent (" << directparent << ")"; + // new current track - check parentage + auto hasSuperParent = mSuperParentsIndices.find(directparent); + if (hasSuperParent != mSuperParentsIndices.end()) { + // same superparent as direct parent + mCurrentParentID = hasSuperParent->second; + mSuperParentsIndices[track] = hasSuperParent->second; + auto superparent = mSuperParents.find(mCurrentParentID); + if (superparent != mSuperParents.end()) { + mCurrentSuperparent = &(superparent->second); + } else { + LOG(error) << "Attention: No superparent object found (parent " << mCurrentParentID << ")"; + mCurrentSuperparent = nullptr; + } + LOG(debug4) << "Found superparent " << mCurrentParentID; + } else { + // start of new chain + // for new incoming tracks the super parent index is equal to the track ID (for recursion) + mSuperParentsIndices[track] = track; + mCurrentSuperparent = AddSuperparent(track, fMC->TrackPid(), fMC->Etot()); + mCurrentParentID = track; + } + mCurrentTrack = track; + } + + // Processing HCAL hits + bool flagHCAL = true; + if (fMC->CurrentMedium() == mMedSensHCal) { + flagHCAL = ProcessHitsHCAL(v); + } + + // Processing ECAL Pad hits + bool flagECALPad = true; + if (TVirtualMC::GetMC()->CurrentMedium() == mMedSensECalPad) { + flagECALPad = ProcessHitsEPad(v); + } + + // Processing ECAL Pixel hits + bool flagECALPix = true; + if (fMC->CurrentMedium() == mMedSensECalPix) { + flagECALPix = ProcessHitsEPix(v); + } + + return (flagHCAL || flagECALPad || flagECALPix); + // return true; +} + +Hit* Detector::AddHit(int trackID, int primary, double initialEnergy, int detID, o2::focal::Hit::Subsystem_t subsystem, + const math_utils::Point3D& pos, double time, double eLoss) +{ + LOG(debug3) << "Adding hit for track " << trackID << " with position (" << pos.X() << ", " + << pos.Y() << ", " << pos.Z() << ") with energy " << initialEnergy << " loosing " << eLoss; + mHits->emplace_back(primary, trackID, detID, subsystem, initialEnergy, pos, time, eLoss); + + auto [isin, col, row, layer, segment] = mGeometry->getVirtualInfo(pos.X(), pos.Y(), pos.Z()); + mHitIndexMapping.insert(std::pair({trackID, uint8_t(row), uint8_t(col), uint8_t(layer)}, static_cast(mHits->size() - 1))); + + return &(mHits->back()); +} + +Hit* Detector::FindHit(int parentID, int col, int row, int layer) +{ + Hit::HitID hitToFind{parentID, uint8_t(row), uint8_t(col), uint8_t(layer)}; + auto found = mHitIndexMapping.find(hitToFind); + + if (found == mHitIndexMapping.end()) { + return nullptr; + } + + return &((*mHits)[found->second]); +} + +Parent* Detector::AddSuperparent(int trackID, int pdg, double energy) +{ + LOG(debug3) << "Adding superparent for track " << trackID << " with PID " << pdg << " and energy " << energy; + auto entry = mSuperParents.insert({trackID, {pdg, energy, false}}); + return &(entry.first->second); +} + +void Detector::EndOfEvent() { Reset(); } + +void Detector::Register() +{ + FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, kTRUE); +} + +void Detector::Reset() +{ + LOG(debug) << "Cleaning FOCAL hits ..."; + if (!o2::utils::ShmManager::Instance().isOperational()) { + mHits->clear(); + } + mHitIndexMapping.clear(); + + mSuperParentsIndices.clear(); + mSuperParents.clear(); + mCurrentTrack = -1; + mCurrentParentID = -1; +} + +void Detector::CreateMaterials() +{ + + // --- Define the various materials for GEANT --- + + /// Silicon + float aSi = 28.09; + float zSi = 14.0; + float dSi = 2.33; + float x0Si = 9.36; + Material(1, "Si $", aSi, zSi, dSi, x0Si, 18.5); + + //// W Tungsten + float aW = 183.84; + float zW = 74.0; + float dW = 19.3; + float x0W = 0.35; + Material(0, "W $", aW, zW, dW, x0W, 17.1); + + // Cu + Material(3, "Cu $", 63.54, 29., 8.96, 1.43, 15.); + + // Al + Material(9, "Al$", 26.98, 13.0, 2.7, 8.9, 37.2); + + //// Pb + Material(10, "Pb $", 207.19, 82., 11.35, .56, 18.5); + + //// Scintillator (copied from EMCal) + // --- The polysterene scintillator (CH) --- + float aP[2] = {12.011, 1.00794}; + float zP[2] = {6.0, 1.0}; + float wP[2] = {1.0, 1.0}; + float dP = 1.032; + Mixture(11, "Polystyrene$", aP, zP, dP, -2, wP); + + // G10 + + float aG10[4] = {1., 12.011, 15.9994, 28.086}; + float zG10[4] = {1., 6., 8., 14.}; + // PH float wG10[4]={0.148648649,0.104054054,0.483499056,0.241666667}; + float wG10[4] = {0.15201, 0.10641, 0.49444, 0.24714}; + Mixture(2, "G10 $", aG10, zG10, 1.7, 4, wG10); + + //// 94W-4Ni-2Cu + float aAlloy[3] = {183.84, 58.6934, 63.54}; + float zAlloy[3] = {74.0, 28, 29}; + float wAlloy[3] = {0.94, 0.04, 0.02}; + float dAlloy = wAlloy[0] * 19.3 + wAlloy[1] * 8.908 + wAlloy[2] * 8.96; + Mixture(5, "Alloy $", aAlloy, zAlloy, dAlloy, 3, wAlloy); + + // Steel + float aSteel[4] = {55.847, 51.9961, 58.6934, 28.0855}; + float zSteel[4] = {26., 24., 28., 14.}; + float wSteel[4] = {.715, .18, .1, .005}; + float dSteel = 7.88; + Mixture(4, "STAINLESS STEEL$", aSteel, zSteel, dSteel, 4, wSteel); + + // Air + float aAir[4] = {12.0107, 14.0067, 15.9994, 39.948}; + float zAir[4] = {6., 7., 8., 18.}; + float wAir[4] = {0.000124, 0.755268, 0.231781, 0.012827}; + float dAir1 = 1.20479E-10; + float dAir = 1.20479E-3; + Mixture(98, "Vacum$", aAir, zAir, dAir1, 4, wAir); + Mixture(99, "Air $", aAir, zAir, dAir, 4, wAir); + + // Ceramic + // Ceramic 97.2% Al2O3 , 2.8% SiO2 + // float wcer[2]={0.972,0.028}; // Not used + float aal2o3[2] = {26.981539, 15.9994}; + float zal2o3[2] = {13., 8.}; + float wal2o3[2] = {2., 3.}; + float denscer = 3.6; + // SiO2 + float aglass[2] = {28.0855, 15.9994}; + float zglass[2] = {14., 8.}; + float wglass[2] = {1., 2.}; + float dglass = 2.65; + Mixture(6, "Al2O3 $", aal2o3, zal2o3, denscer, -2, wal2o3); + Mixture(7, "glass $", aglass, zglass, dglass, -2, wglass); + + // Ceramic is a mixtur of glass and Al2O3 ? + // Not clear how to do this with AliMixture + // Not needed; so skip for now + + /* +float acer[2],zcer[2]; +char namate[21]=""; +float a,z,d,radl,absl,buf[1]; +Int_t nbuf; +fMC->Gfmate((*fIdmate)[6], namate, a, z, d, radl, absl, buf, nbuf); +acer[0]=a; +zcer[0]=z; +fMC->Gfmate((*fIdmate)[7], namate, a, z, d, radl, absl, buf, nbuf); +acer[1]=a; +zcer[1]=z; + +AliMixture( 8, "Ceramic $", acer, zcer, denscer, 2, wcer); +*/ + + // Use Al2O3 instead: + + Mixture(8, "Ceramic $", aal2o3, zal2o3, denscer, -2, wal2o3); + + // Define tracking media + // format + + float tmaxfdSi = 10.0; // 0.1; // .10000E+01; // Degree + float stemaxSi = 0.1; // .10000E+01; // cm + float deemaxSi = 0.1; // 0.30000E-02; // Fraction of particle's energy 0 idtmed[3599]; + Medium(ID_TUNGSTEN, "W conv.$", 0, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.1, nullptr, 0); + /// Si plate -> idtmed[3600]; + Medium(ID_SIPAD, "Si sens pad$", 1, 0, + isxfld, sxmgmx, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi, nullptr, 0); + /// Si plate + Medium(ID_SIPIX, "Si sens pix$", 1, 0, + isxfld, sxmgmx, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi, nullptr, 0); + + //// G10 plate -> idtmed[3601]; + Medium(ID_G10, "G10 plate$", 2, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.01, nullptr, 0); + + //// Cu plate --> idtmed[3602]; + Medium(ID_COPPER, "Cu$", 3, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.0001, nullptr, 0); + + //// S steel --> idtmed[3603]; + Medium(ID_STEEL, "S steel$", 4, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.0001, nullptr, 0); + + //// Alloy --> idtmed[3604]; + Medium(ID_ALLOY, "Alloy conv.$", 5, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.1, nullptr, 0); + + //// Ceramic --> idtmed[3607] + Medium(ID_CERAMIC, "Ceramic$", 8, 0, + isxfld, sxmgmx, 10.0, 0.01, 0.1, 0.003, 0.003, nullptr, 0); + + // HCAL materials // Need to double-check tracking pars for this + /// Pb plate --> idtmed[3608] + Medium(ID_PB, "Pb // The Scintillator must be first in order in vector for Rin to be set$", 10, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.1, nullptr, 0); + /// Scintillator --> idtmed[3609] + Medium(ID_SC, "Scint$", 11, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.001, 0.001, nullptr, 0); + + /// Si plate -> idtmed[3610]; + Medium(ID_SIINSENS, "Si insens$", 1, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, epsil, 0.001, nullptr, 0); + + // Al for the cold plates + Medium(ID_ALUMINIUM, "Aluminium$", 9, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.1, nullptr, 0); + + /// idtmed[3697] + Medium(ID_VAC, "Vacuum $", 98, 0, + isxfld, sxmgmx, 10.0, 1.0, 0.1, 0.1, 1.0, nullptr, 0); + + /// idtmed[3698] + Medium(ID_AIR, "Air gaps$", 99, 0, + isxfld, sxmgmx, 10.0, 1.0, 0.1, epsil, 0.001, nullptr, 0); +} + +//____________________________________________________________________________ +void Detector::addAlignableVolumes() const +{ + // Create entries for alignable volumes associating the symbolic volume + // name with the corresponding volume path. Needs to be syncronized with + // eventual changes in the geometry + // Alignable volumes are: + + addAlignableVolumesECAL(); + addAlignableVolumesHCAL(); +} + +//____________________________________________________________________________ +void Detector::addAlignableVolumesHCAL() const +{ + const std::string vpsector = "/cave_1/barrel_1/FOCAL_1/HCAL_1"; + const std::string snsector = "FOCAL/HCAL"; + + if (!gGeoManager->SetAlignableEntry(snsector.c_str(), vpsector.c_str())) { + LOG(fatal) << "Alignable entry " << snsector << " not created. Volume path " << vpsector << "not valid."; + } +} + +//____________________________________________________________________________ +void Detector::addAlignableVolumesECAL() const +{ + const std::string vpsector = "/cave_1/barrel_1/FOCAL_1/ECAL_1"; + const std::string snsector = "FOCAL/ECAL"; + + if (!gGeoManager->SetAlignableEntry(snsector.c_str(), vpsector.c_str())) { + LOG(fatal) << "Alignable entry " << snsector << " not created. Volume path " << vpsector << "not valid."; + } +} + +void Detector::ConstructGeometry() +{ + + //// new geometry genetation + //// The FOCAL Geometry has std::vector of FOCAL Composition + //// This Composition knows + ///// 1. What is the material? + //// 2. Layer + //// 3. Stack + //// 4. center x (in local frame of layer and wafer) + //// 5. center y (in local frame of layer and wafer) + //// 6. center z (in local frame of layer and wafer) + //// 7. size of x, y, z + + LOG(debug) << "Creating FOCAL geometry\n"; + + CreateMaterials(); + + /// -1 means get all the material object + mGeoCompositions = mGeometry->getFOCALMicroModule(-1); + if (!mGeoCompositions.size()) { + LOG(error) << "FOCAL compositions not found!!"; + return; + } + + float pars[4]; + pars[0] = (mGeometry->getFOCALSizeX() + 2 * mGeometry->getMiddleTowerOffset() + mGeometry->getDetectorOpeningRight() + mGeometry->getDetectorOpeningLeft()) / 2; + pars[1] = mGeometry->getFOCALSizeY() / 2; + pars[2] = mGeometry->getFOCALSizeZ() / 2; + // Add space to place 2 SiPad layers in front of ECAL + // The global position of ECAL and HCAL remains the same, but the FOCAL box needs to be slightly larger to accomodate + // the 2 SiPad layers which will sit at z=698 and 699cm (2 and 1 cm in front of ECAL) + if (mGeometry->getInsertFrontPadLayers()) { + pars[2] += 1.0; + } + if (mGeometry->getInsertHCalReadoutMaterial()) { + pars[2] += (1.0 + 0.5); // place Aluminium 1cm thick box (0.5 means half) at 2cm behind HCal to simulate SiPM readout material + pars[1] += (10.0 + 1.0); // place Aluminium 1cm thick box at 10 cm below FOCAL + } + pars[3] = 0; + + LOG(info) << "Creating FOCAL with dimensions X: " << (mGeometry->getFOCALSizeX() + 2 * mGeometry->getMiddleTowerOffset()) << ", Y: " + << mGeometry->getFOCALSizeY() << ", Z: " << mGeometry->getFOCALSizeZ() + (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) + (mGeometry->getInsertHCalReadoutMaterial() ? 3.0 : 0.0); + + TVirtualMC::GetMC()->Gsvolu("FOCAL", "BOX", getMediumID(ID_AIR), pars, 4); + mSensitive.push_back("FOCAL"); + // mSensitiveHCAL.push_back("FOCAL"); + // mSensitiveECALPad.push_back("FOCAL"); + // mSensitiveECALPix.push_back("FOCAL"); + + // ECAL part + LOG(debug2) << "ECAL geometry : " << GetTitle(); + CreateECALGeometry(); + + // HCAL part + switch (mGeometry->getHCALDesign()) { + case Geometry::HCALDesgin::Sandwich: + CreateHCALSandwich(); + break; + + case Geometry::HCALDesgin::Spaghetti: + CreateHCALSpaghetti(); + break; + + case Geometry::HCALDesgin::Sheets: + CreateHCALSheets(); + break; + + default: + break; + } + + // const float z0 = 1312.5; // center of barrel mother volume + TVirtualMC::GetMC()->Gspos("FOCAL", 1, "barrel", 0, 30., mGeometry->getFOCALZ0() - (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) + (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0), 0, "ONLY"); +} + +void Detector::CreateHCALSpaghetti() +{ + TGeoVolumeAssembly* volHCAL = new TGeoVolumeAssembly("HCAL"); + + TGeoVolumeAssembly* HcalTube = gGeoManager->MakeVolumeAssembly("ScintCuTubes"); + + TGeoVolume* volCuTube; + TGeoVolume* volSciFi; + + float RScint = 0.; + float Rin = 0.; + float Rout = 0.; + float Length = 0.; + + for (auto& icomp : mGeoCompositions) { + Length = icomp->sizeZ() / 2; + + if (icomp->material() == "Pb") { + Rout = icomp->sizeX() / 2; + TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_PB)); + volCuTube = gGeoManager->MakeTube("Tube", medium, Rin, Rout, Length); // The Scintillator must be first in order in vector for Rin to be set + volCuTube->SetLineWidth(2); + volCuTube->SetLineColor(kRed); + mSensitive.push_back(volCuTube->GetName()); + // mSensitiveHCAL.push_back(volCuTube->GetName()); + HcalTube->AddNode(volCuTube, 1, nullptr); + } + if (icomp->material() == "Scint") { + RScint = icomp->sizeX() / 2; + Rin = RScint + 0.005; + TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_SC)); + volSciFi = gGeoManager->MakeTube("ScintFiber", medium, 0., RScint, Length); + volSciFi->SetLineWidth(2); + volSciFi->SetLineColor(kBlue); + // mSensitiveHCAL.push_back(volSciFi->GetName()); + mSensitive.push_back(volSciFi->GetName()); + HcalTube->AddNode(volSciFi, 1, nullptr); + } + if (icomp->material() == "CuHCAL") { + Rout = icomp->sizeX() / 2; + TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_COPPER)); + volCuTube = gGeoManager->MakeTube("Tube", medium, Rin, Rout, Length); // The Scintillator must be first in order in vector for Rin to be set + volCuTube->SetLineWidth(2); + volCuTube->SetLineColor(kRed); + // mSensitiveHCAL.push_back(volCuTube->GetName()); + mSensitive.push_back(volCuTube->GetName()); + HcalTube->AddNode(volCuTube, 1, nullptr); + } + } + + bool splitDet = mGeometry->getDetectorOpeningRight() > 0.0 || mGeometry->getDetectorOpeningLeft() > 0.0; + + double TowerSize = mGeometry->getHCALTowerSize(); + double CuBoxThickness = 0.3; // Thickness of the Cu box carrying capillary tubes + + TGeoBBox* ODBox = new TGeoBBox("TowerOD", TowerSize / 2, TowerSize / 2, Length); + TGeoBBox* IDBox = new TGeoBBox("TowerID", (TowerSize - CuBoxThickness) / 2, (TowerSize - CuBoxThickness) / 2, Length + 0.01); + TGeoCompositeShape* TowerHCAL = new TGeoCompositeShape("TowerHCAL", "TowerOD - TowerID"); + TGeoVolume* volTower = new TGeoVolume("volTower", TowerHCAL, gGeoManager->GetMedium(getMediumID(ID_COPPER))); + volTower->SetLineWidth(2); + volTower->SetLineColor(42); + // mSensitiveHCAL.push_back(volTower->GetName()); + mSensitive.push_back(volTower->GetName()); + + TGeoVolumeAssembly* volTowerHCAL = new TGeoVolumeAssembly("volTowerHCAL"); + volTowerHCAL->AddNode(volTower, 1, nullptr); + + int Rows = 0; + float RowPos = 0.; + int Columns = 0; + int NumTubes = 1; + + // Packing circles in Hexagonal shape + while (RowPos + CuBoxThickness / 2 + Rout + 2 * Rout < TowerSize) { + Columns = 0; + float ColumnPos = (Rows % 2 == 0) ? 0. : Rout; + while (ColumnPos + CuBoxThickness / 2 + Rout + 2 * Rout < TowerSize) { + + TGeoTranslation* trans = new TGeoTranslation(ColumnPos - TowerSize / 2 + CuBoxThickness / 2 + Rout, RowPos - TowerSize / 2 + CuBoxThickness / 2 + Rout, 0.); + + trans->SetName(Form("trans_Num_%d", NumTubes)); + trans->RegisterYourself(); + + volTowerHCAL->AddNode(HcalTube, NumTubes, trans); + // volTowerHCAL->AddNode(volCuTube, NumTubes, trans); + // volTowerHCAL->AddNode(volSciFi, NumTubes, trans); + + Columns++; + ColumnPos = Columns * 2 * Rout + ((Rows % 2 == 0) ? 0. : Rout); + NumTubes++; + } + + Rows++; + RowPos = Rows * 2 * Rout * TMath::Sin(TMath::Pi() / 3); + } + + // Define the distance from the beam pipe in which towers will ommitted + Double_t BeamPipeRadius = 3.0; // in cm To be changed later + Double_t TowerHalfDiag = TMath::Sqrt2() * 0.5 * TowerSize; // tower half diagonal + Double_t MinRadius = BeamPipeRadius + TowerSize / 2; + + float SizeXHCAL = mGeometry->getHCALTowersInX() * TowerSize; + float SizeYHCAL = mGeometry->getHCALTowersInY() * TowerSize; + + int nTowersX = mGeometry->getHCALTowersInX(); + int nTowersY = mGeometry->getHCALTowersInY(); + + Rows = 0; + Columns = 0; + RowPos = 0.; + Int_t NumTowers = 1; + + if (splitDet) { + SizeXHCAL = SizeXHCAL / 2; + + TGeoVolumeAssembly* volHalfHCAL = new TGeoVolumeAssembly("HalfHCAL"); + + for (Rows = 0; Rows < nTowersY; Rows++) { + + float ColumnPos = 0.; + RowPos = Rows * TowerSize; + for (Columns = 0; Columns < nTowersX / 2; Columns++) { + ColumnPos = Columns * TowerSize; + TGeoTranslation* trans = new TGeoTranslation(ColumnPos - SizeXHCAL / 2 + TowerSize / 2, RowPos - SizeYHCAL / 2 + TowerSize / 2, 0.); + + // Shit the beampipe towers by TowerSize/2 + if (Rows == nTowersY / 2) { + trans->SetDx(trans->GetTranslation()[0] + TowerSize / 2); + } + + // Adding the Tower to the HCAL + volHalfHCAL->AddNode(volTowerHCAL, NumTowers, trans); + + NumTowers++; + } + volHCAL->AddNode(volHalfHCAL, 1, new TGeoTranslation(SizeXHCAL / 2 + mGeometry->getDetectorOpeningRight(), 0, 0)); + TGeoRotation* rotFlipZ = new TGeoRotation(); + rotFlipZ->RotateY(180); // Flip around Y to reverse Z + TGeoCombiTrans* combHalf = new TGeoCombiTrans(-SizeXHCAL / 2 - mGeometry->getDetectorOpeningLeft(), 0., 0., rotFlipZ); + volHCAL->AddNode(volHalfHCAL, 2, combHalf); + } + } else { + for (Rows = 0; Rows < nTowersY; Rows++) { + + float ColumnPos = 0.; + RowPos = Rows * TowerSize; + for (Columns = 0; Columns < nTowersX; Columns++) { + ColumnPos = Columns * TowerSize; + TGeoTranslation* trans = new TGeoTranslation(ColumnPos - SizeXHCAL / 2 + TowerSize / 2, RowPos - SizeYHCAL / 2 + TowerSize / 2, 0.); + + // Remove the Towers that overlaps with the beam pipe + Double_t RadialDistance = TMath::Power(trans->GetTranslation()[0], 2) + TMath::Power(trans->GetTranslation()[1], 2); + + if (RadialDistance < MinRadius * MinRadius || TMath::Abs(trans->GetTranslation()[0]) > SizeXHCAL / 2) { + continue; + } + + // Adding the Tower to the HCAL + volHCAL->AddNode(volTowerHCAL, NumTowers, trans); + + NumTowers++; + } + } + } + + LOG(info) << "Number of Towers is: " << (NumTowers - 1); + LOG(info) << "Number of tubes is: " << (NumTubes - 1) * (NumTowers - 1); + + // Create Aluminium plate at the back of HCal to simulate the electronics readout material + // Hardcoded thickness of 1cm and placement at 2 cm behind HCAL + TGeoBBox* alHcalBox = new TGeoBBox("AlHCalBox", SizeXHCAL / 2.0, SizeYHCAL / 2.0, 0.5 / 2.0); + TGeoVolume* volumeAlHcalBox = new TGeoVolume("volAlHcalBox", alHcalBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); + volumeAlHcalBox->SetLineColor(kOrange); + if (mGeometry->getInsertHCalReadoutMaterial()) { + TVirtualMC::GetMC()->Gspos("volAlHcalBox", 9999, "FOCAL", 0.0, 0.0, +1.0 * mGeometry->getFOCALSizeZ() / 2.0 + 1.0, 0, "ONLY"); + // mSensitiveHCAL.push_back("volAlHcalBox"); + mSensitive.push_back("volAlHcalBox"); + } + TGeoBBox* alUnderBox = new TGeoBBox("AlUnderBox", SizeXHCAL / 2.0, 0.5, mGeometry->getFOCALSizeZ() / 2.0 + 1.5); + TGeoVolume* volumeAlUnderBox = new TGeoVolume("volAlUnderBox", alUnderBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); + volumeAlUnderBox->SetLineColor(kOrange); + if (mGeometry->getInsertHCalReadoutMaterial()) { + TVirtualMC::GetMC()->Gspos("volAlUnderBox", 9999, "FOCAL", 0.0, -1.0 * mGeometry->getFOCALSizeY() / 2 - 10.5, 0.0, 0, "ONLY"); + // mSensitiveHCAL.push_back("volAlUnderBox"); + mSensitive.push_back("volAlUnderBox"); + } + + volHCAL->SetVisibility(); + volHCAL->SetVisDaughters(); + TVirtualMC::GetMC()->Gspos("HCAL", 1, "FOCAL", 0, 0, mGeometry->getHCALCenterZ() - mGeometry->getFOCALSizeZ() / 2 + 0.01 + (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) - (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0), 0, "ONLY"); +} + +//_____________________________________________________________________________ +TGeoVolumeAssembly* Detector::CreatePitchAssembly(double Lx, + double Ly1, + double Ly2, + double Lz, + double hole_diameter, + double hole_spacing, + int nholes, + double fiber_radius, + std::string suffix) +{ + + // Z-alignment doesn't change + double zpos = 0; + + TGeoMedium* copper = gGeoManager->GetMedium(getMediumID(ID_COPPER)); + TGeoMedium* scint = gGeoManager->GetMedium(getMediumID(ID_SC)); + + TGeoVolumeAssembly* pitchAssembly = new TGeoVolumeAssembly("pitchAssembly"); + + // Hardcoded values for hole placement, to be set from outside + float holeStart = 0.15; // cm + float holeEnd = 0.35; // cm + + TGeoVolumeAssembly* volLowerSheetwHoles = new TGeoVolumeAssembly(Form("volLowerSheetwHoles_%s", suffix.c_str())); + TGeoVolume* cuSheet = gGeoManager->MakeBox("cuSheet", copper, Lx / 2, (Ly1 - fiber_radius * 2) / 2, Lz / 2); + cuSheet->SetLineColor(kOrange + 2); + mSensitive.push_back(cuSheet->GetName()); + TGeoVolume* boxbegin = gGeoManager->MakeBox("BoxBegin", copper, holeStart / 2, fiber_radius, Lz / 2); + boxbegin->SetLineColor(kOrange + 2); + mSensitive.push_back(boxbegin->GetName()); + TGeoVolume* boxMiddle = gGeoManager->MakeBox("BoxMiddle", copper, (hole_spacing - hole_diameter) / 2, fiber_radius, Lz / 2); + boxMiddle->SetLineColor(kOrange + 2); + mSensitive.push_back(boxMiddle->GetName()); + TGeoVolume* boxEnd = gGeoManager->MakeBox("BoxEnd", copper, holeEnd / 2, fiber_radius, Lz / 2); + boxEnd->SetLineColor(kOrange + 2); + mSensitive.push_back(boxEnd->GetName()); + + double yPlacement = Ly1 / 2 - fiber_radius; + + // ----------------- + // Layer 1: Lower sheet with holes (y = 0) + // ----------------- + + volLowerSheetwHoles->AddNode(cuSheet, 0, new TGeoTranslation(0, -Ly1 / 2 + (Ly1 - fiber_radius * 2) / 2, zpos)); + + // Add holes starting at x = 1.5 mm + float start_x = -Lx / 2 + holeStart; + + for (int ihole = 0; ihole < nholes; ++ihole) { + float holePlacement = start_x + ihole * hole_spacing + hole_diameter / 2; + if (ihole == 0) { + volLowerSheetwHoles->AddNode(boxbegin, ihole, new TGeoTranslation(holePlacement - holeStart / 2 - hole_diameter / 2, yPlacement, zpos)); + volLowerSheetwHoles->AddNode(boxMiddle, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + (hole_spacing - hole_diameter) / 2, yPlacement, zpos)); + } else if (ihole == nholes - 1) { + if ((holePlacement + hole_diameter / 2 + holeStart) < Lx / 2 - 0.005) { + volLowerSheetwHoles->AddNode(boxEnd, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + holeEnd / 2, yPlacement, zpos)); + } else { + volLowerSheetwHoles->AddNode(boxbegin, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + holeStart / 2, yPlacement, zpos)); + } + } else { + volLowerSheetwHoles->AddNode(boxMiddle, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + (hole_spacing - hole_diameter) / 2, yPlacement, zpos)); + } + } + + pitchAssembly->AddNode(volLowerSheetwHoles, 0, new TGeoTranslation(0, Ly1 / 2, zpos)); // Add Ly1 / 2 so the lower edge of the sheets start y=0 + + // ----------------- + // Layer 2: Full copper sheet + // ----------------- + TGeoVolume* fullSheet1 = gGeoManager->MakeBox("FullSheet1", copper, Lx / 2, Ly2 / 2, Lz / 2); + fullSheet1->SetLineColor(kOrange + 2); + mSensitive.push_back(fullSheet1->GetName()); + pitchAssembly->AddNode(fullSheet1, 0, new TGeoTranslation(0, Ly1 / 2 + Ly2 / 2 + Ly1 / 2, zpos)); // Add Ly1 / 2 so the lower edge of the sheets start y=0 + + // ----------------- + // Layer 3: Upper sheet with holes (shifted) + // ----------------- + + TGeoVolumeAssembly* volUpperSheetwHoles = new TGeoVolumeAssembly(Form("volUpperSheetwHoles_%s", suffix.c_str())); + + volUpperSheetwHoles->AddNode(cuSheet, 0, new TGeoTranslation(0, -Ly1 / 2 + (Ly1 - fiber_radius * 2) / 2, zpos)); + + // Add holes starting at x = 3.5 mm + float start_x2 = -Lx / 2 + holeEnd; + + for (int ihole = 0; ihole < nholes; ++ihole) { + float holePlacement = start_x2 + ihole * hole_spacing + hole_diameter / 2; + if (ihole == 0) { + volUpperSheetwHoles->AddNode(boxEnd, ihole, new TGeoTranslation(holePlacement - hole_diameter / 2 - holeEnd / 2, yPlacement, zpos)); + volUpperSheetwHoles->AddNode(boxMiddle, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + (hole_spacing - hole_diameter) / 2, yPlacement, zpos)); + } else if (ihole == nholes - 1) { + volUpperSheetwHoles->AddNode(boxbegin, ihole, new TGeoTranslation(holePlacement + holeStart / 2 + hole_diameter / 2, yPlacement, zpos)); + } else { + if ((holePlacement + hole_spacing + hole_diameter / 2) < Lx / 2 - 0.005) { + volUpperSheetwHoles->AddNode(boxMiddle, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + (hole_spacing - hole_diameter) / 2, yPlacement, zpos)); + } else { + volUpperSheetwHoles->AddNode(boxEnd, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + holeEnd / 2, yPlacement, zpos)); + break; + } + } + } + + pitchAssembly->AddNode(volUpperSheetwHoles, 0, new TGeoTranslation(0, Ly1 / 2 + Ly2 + Ly1 / 2 + Ly1 / 2, zpos)); // Add Ly1 / 2 so the lower edge of the sheets start y=0 + + // ----------------- + // Layer 4: Full copper sheet + // ----------------- + pitchAssembly->AddNode(fullSheet1, 1, new TGeoTranslation(0, Ly1 / 2 + Ly2 + Ly1 + Ly2 / 2 + Ly1 / 2, zpos)); // Add Ly1 / 2 so the lower edge of the sheets start y=0 + + // ----------------- + // Scintillator Fibers + // ----------------- + // Lower set of fibers + TGeoVolume* fiber = gGeoManager->MakeTube("Fiber", scint, 0, fiber_radius, Lz / 2); + fiber->SetLineColor(kBlue); + mSensitive.push_back(fiber->GetName()); + for (int i = 0; i < nholes; ++i) { + float x_fiber = start_x + i * hole_spacing + hole_diameter / 2; + pitchAssembly->AddNode(fiber, i, new TGeoTranslation(x_fiber, Ly1 / 2 - fiber_radius + Ly1 / 2, zpos)); + } + + // Upper set of fibers + for (int i = 0; i < nholes; ++i) { + float x_fiber = start_x2 + i * hole_spacing + hole_diameter / 2; + if (x_fiber > Lx / 2 - 0.05) { + break; + } + pitchAssembly->AddNode(fiber, i + nholes, new TGeoTranslation(x_fiber, Ly1 / 2 + Ly2 + Ly1 / 2 + Ly1 / 2 - fiber_radius + Ly1 / 2, zpos)); + } + + return pitchAssembly; +} + +//_____________________________________________________________________________ +void Detector::CreateHCALSheets() +{ + TGeoVolumeAssembly* volHCAL = new TGeoVolumeAssembly("HCAL"); + + // Dimensions + double Lx = 49.81; // cm + double Ly1 = 0.20; // cm (sheets with holes) + double Ly2 = 0.15; // cm (full sheets) + double Lz = 110.0; // cm + + double fiber_radius = 0.05; + + // HCal materials + int icomp = 0; + for (auto& comp : mGeoCompositions) { + Lz = comp->sizeZ(); + + if (comp->material() == "Scint") { + fiber_radius = comp->sizeX() / 2; + } + if (comp->material() == "CuHCAL" && icomp == 0) { + Lx = comp->sizeX(); + Ly1 = comp->sizeY(); + } + if (comp->material() == "CuHCAL" && icomp == 2) { + Ly2 = comp->sizeY(); + } + icomp++; + } + + double hole_diameter = fiber_radius * 2 + 0.01; // hole radius + double hole_spacing = mGeometry->getHCALPitchSize(); + int nholes = (int)(Lx / hole_spacing); // Number of holes in one HCAL sheet + + double beamPipeHole = mGeometry->getHCALBeamPipeHoleSize(); // cm The size of the beam pipe opening + int nBeamPipeHoles = (int)((Lx - beamPipeHole / 2) / hole_spacing); // Number of beam pipe holes + + // Compute module height (two sheets with holes + two full sheets) + float pitch_height = Ly1 + Ly2 + Ly1 + Ly2; + + int totalNumberOfPitches = mGeometry->getHCALTowersInY() * 2; // Number of pitches in the whole HCAL + int numberOfPitchesBeamPipe = (int)((beamPipeHole + 0.001) / pitch_height); // Number of pitches in the beam pipe region + int numberofPitchesOnYaxis = (totalNumberOfPitches - numberOfPitchesBeamPipe); // Number of pitches in the HCAL ouside the beam pipe region + + TGeoVolumeAssembly* pitchAssembly = CreatePitchAssembly(Lx, Ly1, Ly2, Lz, hole_diameter, hole_spacing, nholes, fiber_radius, "Main"); + pitchAssembly->SetVisibility(true); + TGeoVolumeAssembly* beamPipeAssembly = CreatePitchAssembly(Lx - beamPipeHole / 2, Ly1, Ly2, Lz, hole_diameter, hole_spacing, nBeamPipeHoles, fiber_radius, "BeamPipe"); + beamPipeAssembly->SetVisibility(true); + + TGeoVolumeAssembly* HalfHCAL = new TGeoVolumeAssembly("HalfHCAL"); + + for (int iPitch = 0; iPitch < numberofPitchesOnYaxis; iPitch++) { + float placement = iPitch * pitch_height - pitch_height * (totalNumberOfPitches) / 2.0; + if (placement < -beamPipeHole / 2.0) { + HalfHCAL->AddNode(pitchAssembly, iPitch, new TGeoTranslation(0, placement, 0.)); + } else { + placement += beamPipeHole; + HalfHCAL->AddNode(pitchAssembly, iPitch, new TGeoTranslation(0, placement, 0.)); + } + } + + for (int iPitch = 0; iPitch < numberOfPitchesBeamPipe; iPitch++) { + float placement = iPitch * pitch_height - beamPipeHole / 2.0; + HalfHCAL->AddNode(beamPipeAssembly, iPitch, new TGeoTranslation(-beamPipeHole / 4, placement, 0.)); + } + + HalfHCAL->SetVisibility(true); + HalfHCAL->SetVisDaughters(true); + + volHCAL->AddNode(HalfHCAL, 0, new TGeoTranslation(-Lx / 2, 0, 0.)); + TGeoRotation* rotFlipZ = new TGeoRotation(); + rotFlipZ->RotateY(180); // Flip around Y to reverse Z + TGeoCombiTrans* combHalf = new TGeoCombiTrans(Lx / 2, 0., 0., rotFlipZ); + volHCAL->AddNode(HalfHCAL, 1, combHalf); + + gMC->Gspos("HCAL", 1, "FOCAL", 0, 0, mGeometry->getHCALCenterZ() - mGeometry->getFOCALSizeZ() / 2 + 0.01 + (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) - (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0), 0, "ONLY"); +} + +//_____________________________________________________________________________ +void Detector::CreateHCALSandwich() +{ + TGeoVolumeAssembly* volHCAL = new TGeoVolumeAssembly("HCAL"); + + /// make big volume containing all the longitudinal layers + Float_t pars[4]; // this is HMSC Assembly + pars[0] = mGeometry->getHCALTowerSize() / 2; + pars[1] = mGeometry->getHCALTowerSize() / 2; + pars[2] = mGeometry->getECALSizeZ() + mGeometry->getHCALSizeZ() / 2; // ECAL sizeZ is already added to the HCAL materials CenterZ, so it is also treated as offset + pars[3] = 0; + + float offset = pars[2]; + + TGeoVolumeAssembly* volTower = new TGeoVolumeAssembly("Tower"); + + int iCu(0), iScint(0); + for (auto& icomp : mGeoCompositions) { + + pars[0] = icomp->sizeX() / 2; + pars[1] = icomp->sizeY() / 2; + pars[2] = icomp->sizeZ() / 2; + pars[3] = 0; + + // HCal materials + + if (icomp->material() == "Pb") { + iCu++; + const TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_PB)); + const TGeoBBox* HPadBox = new TGeoBBox("HPadBox", pars[0], pars[1], pars[2]); + TGeoVolume* HPad = new TGeoVolume("HPad", HPadBox, medium); + HPad->SetLineColor(kGray); + // mSensitiveHCAL.push_back(HPad->GetName()); + mSensitive.push_back(HPad->GetName()); + TGeoTranslation* trans = new TGeoTranslation(icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset); + volTower->AddNode(HPad, iCu, trans); + } + if (icomp->material() == "Scint") { + iScint++; + const TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_SC)); + const TGeoBBox* HScintBox = new TGeoBBox("HScintBox", pars[0], pars[1], pars[2]); + TGeoVolume* HScint = new TGeoVolume("HScint", HScintBox, medium); + HScint->SetLineColor(kBlue); + // mSensitiveHCAL.push_back(HScint->GetName()); + mSensitive.push_back(HScint->GetName()); + TGeoTranslation* trans = new TGeoTranslation(icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset); + volTower->AddNode(HScint, iScint, trans); + } + if (icomp->material() == "CuHCAL") { + iCu++; + const TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_COPPER)); + const TGeoBBox* HPadBox = new TGeoBBox("HPadBox", pars[0], pars[1], pars[2]); + TGeoVolume* HPad = new TGeoVolume("HPad", HPadBox, medium); + HPad->SetLineColor(kRed); + // mSensitiveHCAL.push_back(HPad->GetName()); + mSensitive.push_back(HPad->GetName()); + TGeoTranslation* trans = new TGeoTranslation(icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset); + volTower->AddNode(HPad, iCu, trans); + } + } + double TowerSize = mGeometry->getHCALTowerSize(); + + // Define the distance from the beam pipe in which towers will ommitted + double BeamPipeRadius = 3.6; // in cm + double TowerHalfDiag = TMath::Sqrt2() * 0.5 * TowerSize; // tower half diagonal + double MinRadius = BeamPipeRadius + TowerHalfDiag; + + float SizeXHCAL = mGeometry->getHCALTowersInX() * TowerSize; + float SizeYHCAL = mGeometry->getHCALTowersInY() * TowerSize; + + int nTowersX = mGeometry->getHCALTowersInX(); + int nTowersY = mGeometry->getHCALTowersInY(); + + int Rows = 0; + int Columns = 0; + double RowPos = 0.; + int NumTowers = 1; + + // Arranging towers + for (Rows = 0; Rows < nTowersY; Rows++) { + Columns = 0; + float ColumnPos = 0.; + RowPos = Rows * TowerSize; + for (Columns = 0; Columns < nTowersX; Columns++) { + ColumnPos = Columns * TowerSize; + + TGeoTranslation* trans = new TGeoTranslation(ColumnPos - SizeXHCAL / 2 + TowerSize / 2, RowPos - SizeYHCAL / 2 + TowerSize / 2, 0.); + + // Remove the Towers that overlaps with the beam pipe + double RadialDistance = TMath::Power(ColumnPos - SizeXHCAL / 2 + TowerSize / 2, 2) + TMath::Power(RowPos - SizeYHCAL / 2 + TowerSize / 2, 2); + + if (RadialDistance < MinRadius * MinRadius) { + continue; + } + + // Adding the Tower to the HCAL + volHCAL->AddNode(volTower, NumTowers, trans); + + NumTowers++; + } + } + LOG(info) << "Number of Towers is: " << (NumTowers - 1); + + // Create an Aluminium plate at the back of HCal to simulate the electronics readout material + // Hardcoded thickness of 1cm and placement at 2 cm behind HCAL + TGeoBBox* alHcalBox = new TGeoBBox("AlHCalBox", SizeXHCAL / 2.0, SizeYHCAL / 2.0, 0.5 / 2.0); + TGeoVolume* volumeAlHcalBox = new TGeoVolume("volAlHcalBox", alHcalBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); + volumeAlHcalBox->SetLineColor(kOrange); + if (mGeometry->getInsertHCalReadoutMaterial()) { + TVirtualMC::GetMC()->Gspos("volAlHcalBox", 9999, "FOCAL", 0.0, 0.0, +1.0 * mGeometry->getFOCALSizeZ() / 2.0 + 1.0, 0, "ONLY"); + } + TGeoBBox* alUnderBox = new TGeoBBox("AlUnderBox", SizeXHCAL / 2.0, 0.5, mGeometry->getFOCALSizeZ() / 2.0 + 1.5); + TGeoVolume* volumeAlUnderBox = new TGeoVolume("volAlUnderBox", alUnderBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); + volumeAlUnderBox->SetLineColor(kOrange); + if (mGeometry->getInsertHCalReadoutMaterial()) { + TVirtualMC::GetMC()->Gspos("volAlUnderBox", 9999, "FOCAL", 0.0, -1.0 * mGeometry->getFOCALSizeY() / 2 - 10.5, 0.0, 0, "ONLY"); + } + + TGeoVolume* volFOCAL = gGeoManager->GetVolume("FOCAL"); + volFOCAL->AddNode(volHCAL, 1, new TGeoTranslation(0, 0, mGeometry->getHCALCenterZ() - mGeometry->getFOCALSizeZ() / 2 + 0.01 + (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) - (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0))); // 0.01 to avoid overlap with ECAL +} + +//_____________________________________________________________________________ +void Detector::CreateECALGeometry() +{ + // using boost::algorithm::contains; // only when string operations + Geometry* geom = getGeometry(); + + // Int_t *idtmed = fIdtmed->GetArray() - 3599; //599 -> 3599 + + ////// strategy to create the supermodule (tower) + ////// 1. create tower correspinding to 5 PAD wafer + ////// 2. create tower with PIX layers (NX:NY) + + /// make big volume containing all the longitudinal layers + double pars[4]; // this is EMSC Assembly + pars[0] = geom->getTowerSizeX() / 2. + geom->getTowerGapSizeX() / 2.; + pars[1] = geom->getTowerSizeY() / 2. + geom->getTowerGapSizeY() / 2.; + // pars[2] = mGeometry->GetFOCALSizeZ() / 2; + pars[2] = geom->getECALSizeZ() / 2; + pars[3] = 0; + // this shifts all the pixel layers to the center near the beampipe + double pixshift = geom->getTowerSizeX() - (geom->getGlobalPixelWaferSizeX() * geom->getNumberOfPIXsInX()); + + bool splitDet = mGeometry->getDetectorOpeningRight() > 0.0 || mGeometry->getDetectorOpeningLeft() > 0.0; + + float offset = pars[2]; + // gMC->Gsvolu("EMSC1", "BOX", idtmed[3698], pars, 4);//Left towers (pixels shifted right) + // gMC->Gsvolu("EMSC2", "BOX", idtmed[3698], pars, 4);//Right towers (pixels shifted left) + + TVirtualMC::GetMC()->Gsvolu("EMSC1", "BOX", getMediumID(ID_AIR), pars, 4); // Left towers (pixels shifted right) + TVirtualMC::GetMC()->Gsvolu("EMSC2", "BOX", getMediumID(ID_AIR), pars, 4); // Right towers (pixels shifted left) + // mSensitiveECALPad.push_back("EMSC1"); + // mSensitiveECALPad.push_back("EMSC2"); + mSensitive.push_back("EMSC1"); + mSensitive.push_back("EMSC2"); + + // const Composition *icomp = new Composition(); //to be removed + // for(int i = 0; i < 20; i++){ // old + // icomp = geom->getComposition(i, 0); // obsolete + + // loop over geometry composition elements + for (auto& icomp : mGeoCompositions) { + + pars[0] = icomp->sizeX() / 2.; + pars[1] = icomp->sizeY() / 2.; + pars[2] = icomp->sizeZ() / 2.; + pars[3] = 0; + + if (icomp->material() == "PureW") { + // TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", idtmed[3599], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", getMediumID(ID_TUNGSTEN), pars, 4); + // mSensitiveECALPad.push_back("EW1"); + mSensitive.push_back("EW1"); + gGeoManager->GetVolume("EW1")->SetLineColor(kBlue); + TVirtualMC::GetMC()->Gspos("EW1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EW1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + if (icomp->material() == "Alloy") { + // TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", idtmed[3604], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", getMediumID(ID_ALLOY), pars, 4); + // mSensitiveECALPad.push_back("EW1"); + mSensitive.push_back("EW1"); + TVirtualMC::GetMC()->Gspos("EW1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EW1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + if (icomp->material() == "G10") { + // TVirtualMC::GetMC()->Gsvolu("G10RO1", "BOX", idtmed[3601], pars, 4); + TVirtualMC::GetMC()->Gsvolu("G10RO1", "BOX", getMediumID(ID_G10), pars, 4); + // mSensitiveECALPad.push_back("G10RO1"); + mSensitive.push_back("G10RO1"); + gGeoManager->GetVolume("G10RO1")->SetLineColor(kGreen); + TVirtualMC::GetMC()->Gspos("G10RO1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("G10RO1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + if (icomp->material() == "Cu") { + // TVirtualMC::GetMC()->Gsvolu("EWCU", "BOX", idtmed[3602], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWCU", "BOX", getMediumID(ID_COPPER), pars, 4); + // mSensitiveECALPad.push_back("EWCU"); + mSensitive.push_back("EWCU"); + gGeoManager->GetVolume("EWCU")->SetLineColor(kViolet); + TVirtualMC::GetMC()->Gspos("EWCU", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWCU", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + if (icomp->material() == "Air") { + // TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", idtmed[3698], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", getMediumID(ID_AIR), pars, 4); + // mSensitiveECALPad.push_back("EWAIR1"); + mSensitive.push_back("EWAIR1"); + gGeoManager->GetVolume("EWAIR1")->SetLineColor(kGray); + TVirtualMC::GetMC()->Gspos("EWAIR1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWAIR1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + if (icomp->material() == "Ceramic") { + // TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", idtmed[3607], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", getMediumID(ID_CERAMIC), pars, 4); + // mSensitiveECALPad.push_back("EWAIR1"); + mSensitive.push_back("EWAIR1"); + TVirtualMC::GetMC()->Gspos("EWAIR1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWAIR1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + if (icomp->material() == "SiPad") { + // TVirtualMC::GetMC()->Gsvolu("EWSIPAD1", "BOX", idtmed[3600], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWSIPAD1", "BOX", getMediumID(ID_SIPAD), pars, 4); + // mSensitiveECALPad.push_back("EWSIPAD1"); + mSensitive.push_back("EWSIPAD1"); + gGeoManager->GetVolume("EWSIPAD1")->SetLineColor(kOrange - 7); + int number = (icomp->id()) + (icomp->stack() << 12) + (icomp->layer() << 16); + // cout<<" pad : "<< icomp->material()<<" "<centerZ()-offset <Gspos("EWSIPAD1", number + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWSIPAD1", number + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + // Pixels (sensitive layer) + if (icomp->material() == "SiPix") { + // TVirtualMC::GetMC()->Gsvolu("EWSIPIX1", "BOX", idtmed[3600], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWSIPIX1", "BOX", getMediumID(ID_SIPIX), pars, 4); + // mSensitiveECALPix.push_back("EWSIPIX1"); + mSensitive.push_back("EWSIPIX1"); + gGeoManager->GetVolume("EWSIPIX1")->SetLineColor(kPink); + + int number = (icomp->id()) + (icomp->stack() << 12) + (icomp->layer() << 16); + TVirtualMC::GetMC()->Gspos("EWSIPIX1", number + 1, "EMSC1", + icomp->centerX() - geom->getGlobalPixelOffsetX() + pixshift, icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWSIPIX1", number + 1, "EMSC2", + icomp->centerX() + geom->getGlobalPixelOffsetX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + // Passive silicon + if (icomp->material() == "Si") { + // TVirtualMC::GetMC()->Gsvolu("EWSI1", "BOX", idtmed[3610], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWSI1", "BOX", getMediumID(ID_SIINSENS), pars, 4); + // mSensitiveECALPix.push_back("EWSI1"); + mSensitive.push_back("EWSI1"); + gGeoManager->GetVolume("EWSI1")->SetLineColor(kPink); + TVirtualMC::GetMC()->Gspos("EWSI1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWSI1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + } // end of loop over composition elements + + // Add the coldplates to each of the left and right towers + TGeoBBox* coldPlateBox = new TGeoBBox("ColdPlateBox", geom->getTowerSizeX() / 2.0, geom->getTowerGapSizeY() / 2.0, geom->getECALSizeZ() / 2.0); + TGeoVolume* volumeColdPlate = nullptr; + + if (geom->getTowerGapMaterial() == "Cu") { // Copper + // if (contains(geom->getTowerGapMaterial(), "Cu")) { // Copper + volumeColdPlate = new TGeoVolume("volColdPlate", coldPlateBox, gGeoManager->GetMedium(getMediumID(ID_COPPER))); + } else if (geom->getTowerGapMaterial() == "Al") { // Aluminium + // else if (contains(geom->getTowerGapMaterial(), "Al")) { // Aluminium + volumeColdPlate = new TGeoVolume("volColdPlate", coldPlateBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); + } else { + volumeColdPlate = new TGeoVolume("volColdPlate", coldPlateBox, gGeoManager->GetMedium(getMediumID(ID_AIR))); + } + // mSensitiveECALPad.push_back(volumeColdPlate->GetName()); + mSensitive.push_back(volumeColdPlate->GetName()); + volumeColdPlate->SetLineColor(kOrange); + TVirtualMC::GetMC()->Gspos("volColdPlate", 1, "EMSC1", 0.0, geom->getTowerSizeY() / 2.0 + geom->getTowerGapSizeY() / 2.0, 0.0, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("volColdPlate", 1, "EMSC2", 0.0, geom->getTowerSizeY() / 2.0 + geom->getTowerGapSizeY() / 2.0, 0.0, 0, "ONLY"); + + // Place the towers in the ECAL + // --- Place the ECAL in FOCAL + float fcal_pars[4]; + fcal_pars[0] = (geom->getFOCALSizeX() + 2. * geom->getMiddleTowerOffset() + mGeometry->getDetectorOpeningRight() + mGeometry->getDetectorOpeningLeft()) / 2.; + fcal_pars[1] = geom->getFOCALSizeY() / 2.; + fcal_pars[2] = geom->getECALSizeZ() / 2.; + fcal_pars[3] = 0.; + + // TVirtualMC::GetMC()->Gsvolu("ECAL", "BOX", idtmed[3698], fcal_pars, 4); + TVirtualMC::GetMC()->Gsvolu("ECAL", "BOX", getMediumID(ID_AIR), fcal_pars, 4); + // mSensitiveECALPad.push_back("ECAL"); + mSensitive.push_back("ECAL"); + + // Create SiPad box for the two sensitive layers to be placed in front of ECAL + TGeoBBox* siPadBox = new TGeoBBox("SiPadBox", geom->getTowerSizeX() / 2. + geom->getTowerGapSizeX() / 2., + geom->getTowerSizeY() / 2. + geom->getTowerGapSizeY() / 2., 0.03 / 2.0); + TGeoVolume* volumeSiPad = new TGeoVolume("volSiPad", siPadBox, gGeoManager->GetMedium(getMediumID(ID_SIPAD))); + volumeSiPad->SetLineColor(kOrange + 7); + // mSensitiveECALPad.push_back(volumeSiPad->GetName()); + if (geom->getInsertFrontPadLayers()) { + mSensitive.push_back(volumeSiPad->GetName()); + } + + double xp, yp, zp; + int itowerx, itowery; + // int number = i + j * geom->getNumberOfTowersInX(); + for (int number = 0; number < geom->getNumberOfTowersInX() * geom->getNumberOfTowersInY(); number++) { + itowerx = number % geom->getNumberOfTowersInX(); + itowery = number / geom->getNumberOfTowersInX(); + // const auto towerCenter = geom->getGeoTowerCenter(number); //only ECAL part, second parameter = -1 by default + // xp = std::get<0>towerCenter; + // std::tie(xp, yp, zp) = geom->getGeoTowerCenter(number); + auto [xp, yp, zp] = geom->getGeoTowerCenter(number); // only ECAL part, second parameter = -1 by default + + if (itowerx == 0) { + if (splitDet) { + xp -= geom->getDetectorOpeningLeft(); + } + + TVirtualMC::GetMC()->Gspos("EMSC1", number + 1, "ECAL", xp, yp, 0, 0, "ONLY"); + // Add the SiPad front volumes directly under the FOCAL volume + if (geom->getInsertFrontPadLayers()) { + TVirtualMC::GetMC()->Gspos("volSiPad", -1 * (number + 1), "FOCAL", xp, yp, -1.0 * geom->getFOCALSizeZ() / 2.0, 0, "ONLY"); + // mSensitiveECALPad.push_back("volSiPad"); + mSensitive.push_back("volSiPad"); + TVirtualMC::GetMC()->Gspos("volSiPad", -1 * (geom->getNumberOfTowersInX() * geom->getNumberOfTowersInY() + number + 1), "FOCAL", xp - 0.5, yp + 0.5, -1.0 * geom->getFOCALSizeZ() / 2.0 + 1.0, 0, "ONLY"); + // mSensitiveECALPad.push_back("volSiPad"); + mSensitive.push_back("volSiPad"); + } + } + if (itowerx == 1) { + if (splitDet) { + xp += geom->getDetectorOpeningRight(); + } + + TVirtualMC::GetMC()->Gspos("EMSC2", number + 1, "ECAL", xp, yp, 0, 0, "ONLY"); + // Add the SiPad front volumes directly under the FOCAL volume + if (geom->getInsertFrontPadLayers()) { + TVirtualMC::GetMC()->Gspos("volSiPad", -1 * (number + 1), "FOCAL", xp, yp, -1.0 * geom->getFOCALSizeZ() / 2.0, 0, "ONLY"); + // mSensitiveECALPad.push_back("volSiPad"); + mSensitive.push_back("volSiPad"); + TVirtualMC::GetMC()->Gspos("volSiPad", -1 * (geom->getNumberOfTowersInX() * geom->getNumberOfTowersInY() + number + 1), "FOCAL", xp + 0.5, yp + 0.5, -1.0 * geom->getFOCALSizeZ() / 2.0 + 1.0, 0, "ONLY"); + // mSensitiveECALPad.push_back("volSiPad"); + mSensitive.push_back("volSiPad"); + } + } + } // end of loop over ECAL towers (TowersInX x TowersInY) + + TVirtualMC::GetMC()->Gspos("ECAL", 1, "FOCAL", 0, 0, geom->getECALCenterZ() - geom->getFOCALSizeZ() / 2.0 + (geom->getInsertFrontPadLayers() ? 2.0 : 0.0) - (geom->getInsertHCalReadoutMaterial() ? 1.5 : 0.0), 0, "ONLY"); +} + +void Detector::BeginPrimary() +{ + mCurrentPrimaryID = fMC->GetStack()->GetCurrentTrackNumber(); + LOG(debug) << "Starting primary " << mCurrentPrimaryID << " with energy " << fMC->GetStack()->GetCurrentTrack()->Energy(); +} + +void Detector::FinishPrimary() +{ + LOG(debug) << "Finishing primary " << mCurrentPrimaryID; + // Resetting primary and parent ID + mCurrentPrimaryID = -1; +} + +bool Detector::ProcessHitsEPad(FairVolume* v) +{ + LOG(debug) << "We are in sensitive volume " << v->GetName() << ": " << TVirtualMC::GetMC()->CurrentVolPath(); + + double eloss = TVirtualMC::GetMC()->Edep() * 1e9; // energy in eV (GeV->eV) + if (eloss < DBL_EPSILON) { + return false; // only process hits which actually deposit some energy in the FOCAL + } + + // In case of new parent track create new track reference + auto o2stack = static_cast(TVirtualMC::GetMC()->GetStack()); + if (!mCurrentSuperparent->mHasTrackReference) { + float x, y, z, px, py, pz, e; + TVirtualMC::GetMC()->TrackPosition(x, y, z); + TVirtualMC::GetMC()->TrackMomentum(px, py, pz, e); + o2::TrackReference trackref(x, y, z, px, py, pz, TVirtualMC::GetMC()->TrackLength(), TVirtualMC::GetMC()->TrackTime(), mCurrentParentID, GetDetId()); + o2stack->addTrackReference(trackref); + mCurrentSuperparent->mHasTrackReference = true; + } + + float posX, posY, posZ; + TVirtualMC::GetMC()->TrackPosition(posX, posY, posZ); + + auto [indetector, col, row, layer, segment] = mGeometry->getVirtualInfo(posX, posY, posZ); + + if (!indetector) { + // particle outside the detector + return true; + } + + auto currenthit = FindHit(mCurrentParentID, col, row, layer); + if (!currenthit) { + // Condition for new hit: + // - Processing different partent track (parent track must be produced outside FOCAL) + // - Inside different cell + // - First track of the event + Double_t time = TVirtualMC::GetMC()->TrackTime() * 1e9; // time in ns + LOG(debug3) << "Adding new hit for parent " << mCurrentParentID << " and cell Col: " << col << " Row: " << row << " segment: " << segment; + + /// check handling of primary particles + AddHit(mCurrentParentID, mCurrentPrimaryID, mCurrentSuperparent->mEnergy, row * col + col, o2::focal::Hit::Subsystem_t::EPADS, math_utils::Point3D(posX, posY, posZ), time, eloss); + o2stack->addHit(GetDetId()); + } else { + LOG(debug3) << "Adding energy to the current hit"; + currenthit->SetEnergyLoss(currenthit->GetEnergyLoss() + eloss); + } + return true; +} + +bool Detector::ProcessHitsHCAL(FairVolume* v) +{ + LOG(debug) << "We are in sensitive volume " << v->GetName() << ": " << fMC->CurrentVolPath(); + + double eloss = fMC->Edep() * 1e9; // energy in eV (GeV->eV) + if (eloss < DBL_EPSILON) { + return false; // only process hits which actually deposit some energy in the FOCAL + } + + // In case of new parent track create new track reference + auto o2stack = static_cast(fMC->GetStack()); + if (!mCurrentSuperparent->mHasTrackReference) { + float x, y, z, px, py, pz, e; + fMC->TrackPosition(x, y, z); + fMC->TrackMomentum(px, py, pz, e); + o2::TrackReference trackref(x, y, z, px, py, pz, fMC->TrackLength(), fMC->TrackTime(), mCurrentParentID, GetDetId()); + o2stack->addTrackReference(trackref); + mCurrentSuperparent->mHasTrackReference = true; + } + + float posX, posY, posZ; + fMC->TrackPosition(posX, posY, posZ); + + auto [indetector, col, row, layer, segment] = mGeometry->getVirtualInfo(posX, posY, posZ); + + if (!indetector) { + // particle outside the detector + return true; + } + + auto currenthit = FindHit(mCurrentParentID, col, row, layer); + if (!currenthit) { + // Condition for new hit: + // - Processing different partent track (parent track must be produced outside FOCAL) + // - Inside different cell + // - First track of the event + Double_t time = fMC->TrackTime() * 1e9; // time in ns + LOG(debug3) << "Adding new hit for parent " << mCurrentParentID << " and cell Col: " << col << " Row: " << row << " segment: " << segment; + + /// check handling of primary particles + AddHit(mCurrentParentID, mCurrentPrimaryID, mCurrentSuperparent->mEnergy, row * col + col, o2::focal::Hit::Subsystem_t::HCAL, math_utils::Point3D(posX, posY, posZ), time, eloss); + o2stack->addHit(GetDetId()); + } else { + LOG(debug3) << "Adding energy to the current hit"; + currenthit->SetEnergyLoss(currenthit->GetEnergyLoss() + eloss); + } + return true; +} + +bool Detector::ProcessHitsEPix(FairVolume* v) +{ + LOG(debug) << "We are in sensitive volume " << v->GetName() << ": " << fMC->CurrentVolPath(); + + double eloss = fMC->Edep() * 1e9; // energy in eV (GeV->eV) + if (eloss < DBL_EPSILON) { + return false; // only process hits which actually deposit some energy in the FOCAL + } + + // In case of new parent track create new track reference + auto o2stack = static_cast(fMC->GetStack()); + if (!mCurrentSuperparent->mHasTrackReference) { + float x, y, z, px, py, pz, e; + fMC->TrackPosition(x, y, z); + fMC->TrackMomentum(px, py, pz, e); + o2::TrackReference trackref(x, y, z, px, py, pz, fMC->TrackLength(), fMC->TrackTime(), mCurrentParentID, GetDetId()); + o2stack->addTrackReference(trackref); + mCurrentSuperparent->mHasTrackReference = true; + } + + float posX, posY, posZ; + fMC->TrackPosition(posX, posY, posZ); + + auto [indetector, col, row, layer, segment] = mGeometry->getVirtualInfo(posX, posY, posZ); + + if (!indetector) { + // particle outside the detector + return true; + } + + auto currenthit = FindHit(mCurrentParentID, col, row, layer); + if (!currenthit) { + // Condition for new hit: + // - Processing different partent track (parent track must be produced outside FOCAL) + // - Inside different cell + // - First track of the event + Double_t time = fMC->TrackTime() * 1e9; // time in ns + LOG(debug3) << "Adding new hit for parent " << mCurrentParentID << " and cell Col: " << col << " Row: " << row << " segment: " << segment; + + /// check handling of primary particles + AddHit(mCurrentParentID, mCurrentPrimaryID, mCurrentSuperparent->mEnergy, row * col + col, o2::focal::Hit::Subsystem_t::EPIXELS, math_utils::Point3D(posX, posY, posZ), time, eloss); + o2stack->addHit(GetDetId()); + } else { + LOG(debug3) << "Adding energy to the current hit"; + currenthit->SetEnergyLoss(currenthit->GetEnergyLoss() + eloss); + } + return true; +} diff --git a/Detectors/FOCAL/simulation/src/FOCALSimulationLinkDef.h b/Detectors/FOCAL/simulation/src/FOCALSimulationLinkDef.h new file mode 100644 index 0000000000000..3105f04ae2c99 --- /dev/null +++ b/Detectors/FOCAL/simulation/src/FOCALSimulationLinkDef.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::focal::Detector + ; +#pragma link C++ class o2::base::DetImpl < o2::focal::Detector> + ; + +#endif diff --git a/Detectors/FOCAL/workflow/include/FOCALWorkflow/RawDecoderSpec.h b/Detectors/FOCAL/workflow/include/FOCALWorkflow/RawDecoderSpec.h index c2eed9426e89e..5e8e64bc00fd8 100644 --- a/Detectors/FOCAL/workflow/include/FOCALWorkflow/RawDecoderSpec.h +++ b/Detectors/FOCAL/workflow/include/FOCALWorkflow/RawDecoderSpec.h @@ -61,7 +61,7 @@ class RawDecoderSpec : public framework::Task void decodePadEvent(const gsl::span padWords, o2::InteractionRecord& hbIR); int decodePixelData(const gsl::span pixelWords, o2::InteractionRecord& hbIR, int fecID); std::array createPadLayerEvent(const o2::focal::PadData& data) const; - void fillChipsToLayer(PixelLayerEvent& pixellayer, const gsl::span& chipData, int feeID); + void fillChipToLayer(PixelLayerEvent& pixellayer, const PixelChip& chipData, int feeID); void fillEventPixeHitContainer(std::vector& eventHits, std::vector& eventChips, const PixelLayerEvent& pixelLayer, int layerIndex); int filterIncompletePixelsEventsHBF(HBFData& data, const std::vector& expectFEEs); void buildEvents(); @@ -77,9 +77,12 @@ class RawDecoderSpec : public framework::Task bool mUsePadData = true; bool mUsePixelData = true; bool mFilterIncomplete = false; + bool mTimeframeHasPadData = false; + bool mTimeframeHasPixelData = false; uint32_t mOutputSubspec = 0; PadDecoder mPadDecoder; PixelDecoder mPixelDecoder; + std::unique_ptr mPixelMapping; std::map mHBFs; std::vector mOutputTriggerRecords; std::vector mOutputPixelHits; diff --git a/Detectors/FOCAL/workflow/src/RawDecoderSpec.cxx b/Detectors/FOCAL/workflow/src/RawDecoderSpec.cxx index fede35abff1ea..c7260ae3c9815 100644 --- a/Detectors/FOCAL/workflow/src/RawDecoderSpec.cxx +++ b/Detectors/FOCAL/workflow/src/RawDecoderSpec.cxx @@ -37,16 +37,38 @@ void RawDecoderSpec::init(framework::InitContext& ctx) LOG(info) << "Display additional information in case of inconsistency between pixel links"; mDisplayInconsistent = true; } + auto mappingfile = ctx.options().get("pixelmapping"); + PixelMapper::MappingType_t mappingtype = PixelMapper::MappingType_t::MAPPING_UNKNOWN; + auto chiptype = ctx.options().get("pixeltype"); + if (chiptype == "IB") { + LOG(info) << "Using mapping type: IB"; + mappingtype = PixelMapper::MappingType_t::MAPPING_IB; + } else if (chiptype == "OB") { + LOG(info) << "Using mapping type: OB"; + mappingtype = PixelMapper::MappingType_t::MAPPING_OB; + } else { + LOG(fatal) << "Unkown mapping type for pixels: " << chiptype; + } + if (mappingfile == "default") { + LOG(info) << "Using default pixel mapping for pixel type " << chiptype; + mPixelMapping = std::make_unique(mappingtype); + } else { + LOG(info) << "Using user-defined mapping: " << mappingfile; + mPixelMapping = std::make_unique(PixelMapper::MappingType_t::MAPPING_UNKNOWN); + mPixelMapping->setMappingFile(mappingfile, mappingtype); + } } void RawDecoderSpec::run(framework::ProcessingContext& ctx) { LOG(info) << "Running FOCAL decoding"; resetContainers(); + mTimeframeHasPadData = false; + mTimeframeHasPixelData = false; int inputs = 0; std::vector rawbuffer; - int currentendpoint = 0; + uint16_t currentfee = 0; o2::InteractionRecord currentIR; std::unordered_map numHBFFEE, numEventsFEE; std::unordered_map> numEventsHBFFEE; @@ -66,6 +88,24 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) if (mDebugMode) { o2::raw::RDHUtils::printRDH(rdh); } + + if (o2::raw::RDHUtils::getMemorySize(rdh) > o2::raw::RDHUtils::getHeaderSize(rdh)) { + // non-0 payload size: + auto payloadsize = o2::raw::RDHUtils::getMemorySize(rdh) - o2::raw::RDHUtils::getHeaderSize(rdh); + int endpoint = static_cast(o2::raw::RDHUtils::getEndPointID(rdh)); + auto fee = o2::raw::RDHUtils::getFEEID(rdh); + LOG(debug) << "Next RDH: "; + LOG(debug) << "Found fee 0x" << std::hex << fee << std::dec << " (System " << (fee == 0xcafe ? "Pads" : "Pixels") << ")"; + LOG(debug) << "Found trigger BC: " << o2::raw::RDHUtils::getTriggerBC(rdh); + LOG(debug) << "Found trigger Oribt: " << o2::raw::RDHUtils::getTriggerOrbit(rdh); + LOG(debug) << "Found payload size: " << payloadsize; + LOG(debug) << "Found offset to next: " << o2::raw::RDHUtils::getOffsetToNext(rdh); + LOG(debug) << "Stop bit: " << (o2::raw::RDHUtils::getStop(rdh) ? "yes" : "no"); + LOG(debug) << "Number of GBT words: " << (payloadsize * sizeof(char) / (fee == 0xcafe ? sizeof(o2::focal::PadGBTWord) : sizeof(o2::itsmft::GBTWord))); + auto page_payload = databuffer.subspan(currentpos + o2::raw::RDHUtils::getHeaderSize(rdh), payloadsize); + std::copy(page_payload.begin(), page_payload.end(), std::back_inserter(rawbuffer)); + } + auto trigger = o2::raw::RDHUtils::getTriggerType(rdh); if (trigger & o2::trigger::HB) { // HB trigger received @@ -74,11 +114,12 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) // Data ready if (rawbuffer.size()) { // Only process if we actually have payload (skip empty HBF) - if (currentendpoint == 1) { + if (currentfee == 0xcafe) { // Use FEE ID 0xcafe for PAD data // Pad data if (mUsePadData) { LOG(debug) << "Processing PAD data"; auto nEventsPads = decodePadData(rawbuffer, currentIR); + mTimeframeHasPadData = true; auto found = mNumEventsHBFPads.find(nEventsPads); if (found != mNumEventsHBFPads.end()) { found->second += 1; @@ -88,7 +129,7 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) numEventsPadsTF += nEventsPads; numHBFPadsTF++; } - } else if (currentendpoint == 0) { + } else { // All other FEEs are pixel FEEs // Pixel data if (mUsePixelData) { auto feeID = o2::raw::RDHUtils::getFEEID(rdh); @@ -101,6 +142,7 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) } LOG(debug) << "Processing Pixel data from FEE " << feeID; auto neventsPixels = decodePixelData(rawbuffer, currentIR, feeID); + mTimeframeHasPixelData = true; auto found = numHBFFEE.find(feeID); if (found != numHBFFEE.end()) { found->second++; @@ -122,41 +164,20 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) numEventsHBFFEE.insert(std::pair>{feeID, evbuffer}); } } - } else { - LOG(error) << "Unsupported endpoint " << currentendpoint; } } else { LOG(debug) << "Payload size 0 - skip empty HBF"; } - } else { rawbuffer.clear(); + } else { // Get interaction record for HBF currentIR.bc = o2::raw::RDHUtils::getTriggerBC(rdh); currentIR.orbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); - currentendpoint = o2::raw::RDHUtils::getEndPointID(rdh); - LOG(debug) << "New HBF " << currentIR.orbit << " / " << currentIR.bc << ", endpoint " << currentendpoint; + currentfee = o2::raw::RDHUtils::getFEEID(rdh); + LOG(debug) << "New HBF " << currentIR.orbit << " / " << currentIR.bc << ", FEE 0x" << std::hex << currentfee << std::dec; } } - if (o2::raw::RDHUtils::getMemorySize(rdh) == o2::raw::RDHUtils::getHeaderSize(rdh)) { - // Skip page if emtpy - currentpos += o2::raw::RDHUtils::getOffsetToNext(rdh); - continue; - } - - // non-0 payload size: - auto payloadsize = o2::raw::RDHUtils::getMemorySize(rdh) - o2::raw::RDHUtils::getHeaderSize(rdh); - int endpoint = static_cast(o2::raw::RDHUtils::getEndPointID(rdh)); - LOG(debug) << "Next RDH: "; - LOG(debug) << "Found endpoint " << endpoint; - LOG(debug) << "Found trigger BC: " << o2::raw::RDHUtils::getTriggerBC(rdh); - LOG(debug) << "Found trigger Oribt: " << o2::raw::RDHUtils::getTriggerOrbit(rdh); - LOG(debug) << "Found payload size: " << payloadsize; - LOG(debug) << "Found offset to next: " << o2::raw::RDHUtils::getOffsetToNext(rdh); - LOG(debug) << "Stop bit: " << (o2::raw::RDHUtils::getStop(rdh) ? "yes" : "no"); - LOG(debug) << "Number of GBT words: " << (payloadsize * sizeof(char) / (endpoint == 1 ? sizeof(o2::focal::PadGBTWord) : sizeof(o2::itsmft::GBTWord))); - auto page_payload = databuffer.subspan(currentpos + o2::raw::RDHUtils::getHeaderSize(rdh), payloadsize); - std::copy(page_payload.begin(), page_payload.end(), std::back_inserter(rawbuffer)); currentpos += o2::raw::RDHUtils::getOffsetToNext(rdh); } } else { @@ -165,43 +186,48 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) inputs++; } - // Consistency check PixelEvents - if (!consistencyCheckPixelFEE(numHBFFEE)) { - LOG(alarm) << "Mismatch in number of HBF / TF between pixel FEEs"; - if (mDisplayInconsistent) { - printCounters(numHBFFEE); + int numHBFPixelsTF = 0; + if (mTimeframeHasPixelData) { + // Consistency check PixelEvents + if (!consistencyCheckPixelFEE(numHBFFEE)) { + LOG(alarm) << "Mismatch in number of HBF / TF between pixel FEEs"; + if (mDisplayInconsistent) { + printCounters(numHBFFEE); + } + mNumInconsistencyPixelHBF++; } - mNumInconsistencyPixelHBF++; - } - int numHBFPixelsTF = maxCounter(numHBFFEE); - mNumHBFPixels += numHBFPixelsTF; - if (!consistencyCheckPixelFEE(numEventsFEE)) { - LOG(alarm) << "Mismatch in number of events / TF between pixel FEEs"; - if (mDisplayInconsistent) { - printCounters(numEventsFEE); + numHBFPixelsTF = maxCounter(numHBFFEE); + mNumHBFPixels += numHBFPixelsTF; + if (!consistencyCheckPixelFEE(numEventsFEE)) { + LOG(alarm) << "Mismatch in number of events / TF between pixel FEEs"; + if (mDisplayInconsistent) { + printCounters(numEventsFEE); + } + mNumInconsistencyPixelEvent++; } - mNumInconsistencyPixelEvent++; - } - mNumEventsPixels += maxCounter(numEventsFEE); - if (!checkEventsHBFConsistency(numEventsHBFFEE)) { - LOG(alarm) << "Mistmatch number of events / HBF between pixel FEEs"; - if (mDisplayInconsistent) { - printEvents(numEventsHBFFEE); + mNumEventsPixels += maxCounter(numEventsFEE); + if (!checkEventsHBFConsistency(numEventsHBFFEE)) { + LOG(alarm) << "Mistmatch number of events / HBF between pixel FEEs"; + if (mDisplayInconsistent) { + printEvents(numEventsHBFFEE); + } + mNumInconsistencyPixelEventHBF++; } - mNumInconsistencyPixelEventHBF++; - } - fillPixelEventHBFCount(numEventsHBFFEE); + fillPixelEventHBFCount(numEventsHBFFEE); - if (mUsePixelData && mFilterIncomplete) { - LOG(debug) << "Filtering incomplete pixel events"; - for (auto& hbf : mHBFs) { - auto numErased = filterIncompletePixelsEventsHBF(hbf.second, expectFEEs); - mNumEventsPixels -= numErased; + if (mFilterIncomplete) { + LOG(debug) << "Filtering incomplete pixel events"; + for (auto& hbf : mHBFs) { + auto numErased = filterIncompletePixelsEventsHBF(hbf.second, expectFEEs); + mNumEventsPixels -= numErased; + } } } LOG(info) << "Found " << mHBFs.size() << " HBFs in timeframe"; + LOG(debug) << "EventBuilder: Pixels: " << (mTimeframeHasPixelData ? "yes" : "no"); + LOG(debug) << "EventBuilder: Pads: " << (mTimeframeHasPadData ? "yes" : "no"); buildEvents(); LOG(info) << "Found " << mOutputTriggerRecords.size() << " events in timeframe"; @@ -248,10 +274,10 @@ void RawDecoderSpec::endOfStream(o2::framework::EndOfStreamContext& ec) void RawDecoderSpec::sendOutput(framework::ProcessingContext& ctx) { - ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PADLAYERS", mOutputSubspec, framework::Lifetime::Timeframe}, mOutputPadLayers); - ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PIXELHITS", mOutputSubspec, framework::Lifetime::Timeframe}, mOutputPixelHits); - ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PIXELCHIPS", mOutputSubspec, framework::Lifetime::Timeframe}, mOutputPixelChips); - ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "TRIGGERS", mOutputSubspec, framework::Lifetime::Timeframe}, mOutputTriggerRecords); + ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PADLAYERS", mOutputSubspec}, mOutputPadLayers); + ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PIXELHITS", mOutputSubspec}, mOutputPixelHits); + ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PIXELCHIPS", mOutputSubspec}, mOutputPixelChips); + ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "TRIGGERS", mOutputSubspec}, mOutputTriggerRecords); } void RawDecoderSpec::resetContainers() @@ -293,9 +319,6 @@ void RawDecoderSpec::decodePadEvent(const gsl::span padWords, o2::In int RawDecoderSpec::decodePixelData(const gsl::span pixelWords, o2::InteractionRecord& hbIR, int feeID) { LOG(debug) << "Decoding pixel data for Orbit " << hbIR.orbit << ", BC " << hbIR.bc; - auto fee = feeID & 0x00FF, - branch = (feeID & 0x0F00) >> 8; - int layer = fee < 2 ? 0 : 1; gsl::span pixelpayload(reinterpret_cast(pixelWords.data()), pixelWords.size() / sizeof(o2::itsmft::GBTWord)); LOG(debug) << pixelWords.size() << " Bytes -> " << pixelpayload.size() << " GBT words"; @@ -336,7 +359,14 @@ int RawDecoderSpec::decodePixelData(const gsl::span pixelWords, o2:: if (triggerfound != foundHBF->second.mPixelTriggers.end()) { // auto index = triggerfound - foundHBF->second.mPixelTriggers.begin(); - fillChipsToLayer(foundHBF->second.mPixelEvent[index][layer], chipdata, fee); + for (const auto& chip : chipdata) { + try { + auto chipPosition = mPixelMapping->getPosition(feeID, chip); + fillChipToLayer(foundHBF->second.mPixelEvent[index][chipPosition.mLayer], chip, feeID); + } catch (PixelMapper::InvalidChipException& e) { + LOG(warning) << e; + } + } foundHBF->second.mFEEs[index].push_back(feeID); } else { // new trigger @@ -344,7 +374,14 @@ int RawDecoderSpec::decodePixelData(const gsl::span pixelWords, o2:: foundHBF->second.mPixelEvent.push_back(nextevent); foundHBF->second.mPixelTriggers.push_back(trigger); auto& current = foundHBF->second.mPixelEvent.back(); - fillChipsToLayer(current[layer], chipdata, fee); + for (const auto& chip : chipdata) { + try { + auto chipPosition = mPixelMapping->getPosition(feeID, chip); + fillChipToLayer(current[chipPosition.mLayer], chip, feeID); + } catch (PixelMapper::InvalidChipException& e) { + LOG(warning) << e; + } + } foundHBF->second.mFEEs.push_back({feeID}); } nevents++; @@ -390,16 +427,14 @@ std::array RawDeco return result; } -void RawDecoderSpec::fillChipsToLayer(o2::focal::PixelLayerEvent& pixellayer, const gsl::span& chipData, int feeID) +void RawDecoderSpec::fillChipToLayer(o2::focal::PixelLayerEvent& pixellayer, const o2::focal::PixelChip& chipData, int feeID) { int nhitsBefore = 0; int nchipsBefore = pixellayer.getChips().size(); for (auto& chip : pixellayer.getChips()) { nhitsBefore += chip.mHits.size(); } - for (const auto& chip : chipData) { - pixellayer.addChip(feeID, chip.mLaneID, chip.mChipID, chip.mStatusCode, chip.mHits); - } + pixellayer.addChip(feeID, chipData.mLaneID, chipData.mChipID, chipData.mStatusCode, chipData.mHits); int nhitsAfter = 0; int nchipsAfter = pixellayer.getChips().size(); for (auto& chip : pixellayer.getChips()) { @@ -422,7 +457,7 @@ void RawDecoderSpec::buildEvents() { LOG(debug) << "Start building events" << std::endl; for (const auto& [hbir, hbf] : mHBFs) { - if (mUsePadData && mUsePixelData) { + if (mTimeframeHasPadData && mTimeframeHasPixelData) { LOG(debug) << "Processing HBF with IR: " << hbir.orbit << " / " << hbir.bc << std::endl; // check consistency in number of events between triggers, pixels and pads // in case all events are in the stream @@ -448,7 +483,7 @@ void RawDecoderSpec::buildEvents() // std::cout << "Orbit " << hbf.mPixelTriggers[itrg].orbit << ", BC " << hbf.mPixelTriggers[itrg].bc << ": " << eventPixels.size() << " chips with " << eventHits.size() << " hits ..." << std::endl; mOutputTriggerRecords.emplace_back(hbf.mPixelTriggers[itrg], startPads, constants::PADS_NLAYERS, startChips, eventPixels.size(), startHits, eventHits.size()); } - } else if (mUsePixelData) { + } else if (mTimeframeHasPixelData) { // only pixel data available, merge pixel layers and interaction record if (!(hbf.mPixelEvent.size() == hbf.mPixelTriggers.size())) { LOG(error) << "Inconsistent number of pixel events (" << hbf.mPixelEvent.size() << ") and triggers (" << hbf.mPixelTriggers.size() << ") in HBF"; @@ -465,9 +500,9 @@ void RawDecoderSpec::buildEvents() } std::copy(eventHits.begin(), eventHits.end(), std::back_inserter(mOutputPixelHits)); std::copy(eventPixels.begin(), eventPixels.end(), std::back_inserter(mOutputPixelChips)); - mOutputTriggerRecords.emplace_back(hbf.mPixelTriggers[itrg], startPads, 0, startHits, eventPixels.size(), startHits, eventHits.size()); + mOutputTriggerRecords.emplace_back(hbf.mPixelTriggers[itrg], startPads, 0, startChips, eventPixels.size(), startHits, eventHits.size()); } - } else if (mUsePadData) { + } else if (mTimeframeHasPadData) { // only pad data available, set pad layers and use IR of the HBF for (std::size_t itrg = 0; itrg < hbf.mPadEvents.size(); itrg++) { auto startPads = mOutputPadLayers.size(), @@ -479,7 +514,7 @@ void RawDecoderSpec::buildEvents() for (std::size_t ilayer = 0; ilayer < constants::PADS_NLAYERS; ilayer++) { mOutputPadLayers.push_back(hbf.mPadEvents[itrg][ilayer]); } - mOutputTriggerRecords.emplace_back(hbir, startPads, constants::PADS_NLAYERS, startHits, 0, startHits, 0); + mOutputTriggerRecords.emplace_back(hbir, startPads, constants::PADS_NLAYERS, startChips, 0, startHits, 0); } } } @@ -640,7 +675,7 @@ o2::framework::DataProcessorSpec o2::focal::reco_workflow::getRawDecoderSpec(boo outputs.emplace_back(originFOC, "PIXELCHIPS", outputSubspec, o2::framework::Lifetime::Timeframe); outputs.emplace_back(originFOC, "TRIGGERS", outputSubspec, o2::framework::Lifetime::Timeframe); - std::vector inputs{{"stf", o2::framework::ConcreteDataTypeMatcher{originFOC, o2::header::gDataDescriptionRawData}, o2::framework::Lifetime::Optional}}; + std::vector inputs{{"stf", o2::framework::ConcreteDataTypeMatcher{originFOC, o2::header::gDataDescriptionRawData}, o2::framework::Lifetime::Timeframe}}; if (askDISTSTF) { inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe); } @@ -651,5 +686,7 @@ o2::framework::DataProcessorSpec o2::focal::reco_workflow::getRawDecoderSpec(boo o2::framework::adaptFromTask(outputSubspec, usePadData, usePixelData, debugMode), o2::framework::Options{ {"filterIncomplete", o2::framework::VariantType::Bool, false, {"Filter incomplete pixel events"}}, - {"displayInconsistent", o2::framework::VariantType::Bool, false, {"Display information about inconsistent timeframes"}}}}; -} \ No newline at end of file + {"displayInconsistent", o2::framework::VariantType::Bool, false, {"Display information about inconsistent timeframes"}}, + {"pixeltype", o2::framework::VariantType::String, "OB", {"Pixel mapping type"}}, + {"pixelmapping", o2::framework::VariantType::String, "default", {"File with pixel mapping"}}}}; +} diff --git a/Detectors/FOCAL/workflow/src/event-writer-workflow.cxx b/Detectors/FOCAL/workflow/src/event-writer-workflow.cxx index ed779ebacc19e..dabab2e9c5415 100644 --- a/Detectors/FOCAL/workflow/src/event-writer-workflow.cxx +++ b/Detectors/FOCAL/workflow/src/event-writer-workflow.cxx @@ -29,6 +29,7 @@ void customize(std::vector& workflowOptions) { std::vector options{{"event-writer-name", VariantType::String, "focal-event-writer", {"Workflow name"}}, {"subspec", VariantType::UInt32, 0U, {"Input subspecification"}}, + {"no-subspec", VariantType::Bool, false, {"No subspecification (for output from raw STFs)"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); } @@ -48,10 +49,20 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); WorkflowSpec specs; - specs.emplace_back(MakeRootTreeWriterSpec(workflowname.data(), "focalevents.root", "o2sim", - MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"dataapd", "FOC", "PADLAYERS", subspec}, "FOCALPadLayer", "pad-branch-name"}, - MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelhit", "FOC", "PIXELHITS", subspec}, "FOCALPixelHit", "pixel-hit-branch-name"}, - MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelchip", "FOC", "PIXELCHIPS", subspec}, "FOCALPixelChip", "pixel-chip-branch-name"}, - MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datatrigger", "FOC", "TRIGGERS", subspec}, "FOCALTrigger", "trigger-branch-name"})()); + if (cfgc.options().get("no-subspec")) { + // No subspecification defined (i.e. output from the raw-tf-reader workflow): Use concrete data type matcher + specs.emplace_back(MakeRootTreeWriterSpec(workflowname.data(), "focalevents.root", "o2sim", + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"dataapd", o2::framework::ConcreteDataTypeMatcher{"FOC", "PADLAYERS"}}, "FOCALPadLayer", "pad-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelhit", o2::framework::ConcreteDataTypeMatcher{"FOC", "PIXELHITS"}}, "FOCALPixelHit", "pixel-hit-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelchip", o2::framework::ConcreteDataTypeMatcher{"FOC", "PIXELCHIPS"}}, "FOCALPixelChip", "pixel-chip-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datatrigger", o2::framework::ConcreteDataTypeMatcher{"FOC", "TRIGGERS"}}, "FOCALTrigger", "trigger-branch-name"})()); + } else { + // Subspecification specified: Use full specification + specs.emplace_back(MakeRootTreeWriterSpec(workflowname.data(), "focalevents.root", "o2sim", + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"dataapd", "FOC", "PADLAYERS", subspec}, "FOCALPadLayer", "pad-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelhit", "FOC", "PIXELHITS", subspec}, "FOCALPixelHit", "pixel-hit-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelchip", "FOC", "PIXELCHIPS", subspec}, "FOCALPixelChip", "pixel-chip-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datatrigger", "FOC", "TRIGGERS", subspec}, "FOCALTrigger", "trigger-branch-name"})()); + } return specs; } diff --git a/Detectors/Filtering/src/FilteredTFReaderSpec.cxx b/Detectors/Filtering/src/FilteredTFReaderSpec.cxx index 3165208aef246..22fe1370040db 100644 --- a/Detectors/Filtering/src/FilteredTFReaderSpec.cxx +++ b/Detectors/Filtering/src/FilteredTFReaderSpec.cxx @@ -45,15 +45,15 @@ void FilteredTFReader::run(ProcessingContext& pc) LOG(info) << "Pushing filtered TF: " << mFiltTF.header.asString(); // ITS - pc.outputs().snapshot(Output{"ITS", "ITSTrackROF", 0, Lifetime::Timeframe}, mFiltTF.ITSTrackROFs); - pc.outputs().snapshot(Output{"ITS", "TRACKS", 0, Lifetime::Timeframe}, mFiltTF.ITSTracks); - pc.outputs().snapshot(Output{"ITS", "TRACKCLSID", 0, Lifetime::Timeframe}, mFiltTF.ITSClusterIndices); + pc.outputs().snapshot(Output{"ITS", "ITSTrackROF", 0}, mFiltTF.ITSTrackROFs); + pc.outputs().snapshot(Output{"ITS", "TRACKS", 0}, mFiltTF.ITSTracks); + pc.outputs().snapshot(Output{"ITS", "TRACKCLSID", 0}, mFiltTF.ITSClusterIndices); if (mUseMC) { - pc.outputs().snapshot(Output{"ITS", "TRACKSMCTR", 0, Lifetime::Timeframe}, mFiltTF.ITSTrackMCTruth); + pc.outputs().snapshot(Output{"ITS", "TRACKSMCTR", 0}, mFiltTF.ITSTrackMCTruth); } - pc.outputs().snapshot(Output{"ITS", "CLUSTERSROF", 0, Lifetime::Timeframe}, mFiltTF.ITSClusterROFs); - pc.outputs().snapshot(Output{"ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe}, mFiltTF.ITSClusters); - pc.outputs().snapshot(Output{"ITS", "PATTERNS", 0, Lifetime::Timeframe}, mFiltTF.ITSClusterPatterns); + pc.outputs().snapshot(Output{"ITS", "CLUSTERSROF", 0}, mFiltTF.ITSClusterROFs); + pc.outputs().snapshot(Output{"ITS", "COMPCLUSTERS", 0}, mFiltTF.ITSClusters); + pc.outputs().snapshot(Output{"ITS", "PATTERNS", 0}, mFiltTF.ITSClusterPatterns); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/Filtering/src/FilteringSpec.cxx b/Detectors/Filtering/src/FilteringSpec.cxx index 93a58b20890ab..ea82b1456d955 100644 --- a/Detectors/Filtering/src/FilteringSpec.cxx +++ b/Detectors/Filtering/src/FilteringSpec.cxx @@ -38,7 +38,6 @@ #include "Framework/InputRecordWalker.h" #include "Framework/Logger.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "Framework/CCDBParamSpec.h" #include "FDDBase/Constants.h" #include "FT0Base/Geometry.h" @@ -47,7 +46,7 @@ #include "ReconstructionDataFormats/Cascade.h" #include "MCHTracking/TrackExtrap.h" #include "MCHTracking/TrackParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsVertexing/PVertexerParams.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "ReconstructionDataFormats/Track.h" @@ -327,13 +326,13 @@ DataProcessorSpec getDataFilteringSpec(GID::mask_t src, bool enableSV, bool useM auto dataRequest = std::make_shared(); dataRequest->requestTracks(src, useMC); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); if (src[GID::CTP]) { LOGF(info, "Requesting CTP digits"); dataRequest->requestCTPDigits(useMC); } if (enableSV) { - dataRequest->requestSecondaryVertertices(useMC); + dataRequest->requestSecondaryVertices(useMC); } if (src[GID::TPC]) { dataRequest->requestClusters(GIndex::getSourcesMask("TPC"), false); // no need to ask for TOF clusters as they are requested with TOF tracks diff --git a/Detectors/Filtering/src/filtering-workflow.cxx b/Detectors/Filtering/src/filtering-workflow.cxx index 8e36cfc36b197..faf5463281ed8 100644 --- a/Detectors/Filtering/src/filtering-workflow.cxx +++ b/Detectors/Filtering/src/filtering-workflow.cxx @@ -17,6 +17,7 @@ #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -36,6 +37,7 @@ void customize(std::vector& workflowOptions) {"disable-secondary-vertices", o2::framework::VariantType::Bool, false, {"disable filling secondary vertices"}}, {"data-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/ForwardAlign/CMakeLists.txt b/Detectors/ForwardAlign/CMakeLists.txt new file mode 100644 index 0000000000000..bb7d35444e4ff --- /dev/null +++ b/Detectors/ForwardAlign/CMakeLists.txt @@ -0,0 +1,50 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(ForwardAlign + SOURCES src/MatrixSparse.cxx + src/MatrixSq.cxx + src/MillePede2.cxx + src/MillePedeRecord.cxx + src/MilleRecordReader.cxx + src/MilleRecordWriter.cxx + src/MinResSolve.cxx + src/RectMatrix.cxx + src/SymBDMatrix.cxx + src/SymMatrix.cxx + src/VectorSparse.cxx + src/MilleRecordWriterSpec.cxx + PUBLIC_LINK_LIBRARIES O2::CCDB + O2::Steer + ROOT::TreePlayer) + +o2_target_root_dictionary(ForwardAlign + HEADERS include/ForwardAlign/MatrixSparse.h + include/ForwardAlign/MatrixSq.h + include/ForwardAlign/MillePede2.h + include/ForwardAlign/MillePedeRecord.h + include/ForwardAlign/MilleRecordReader.h + include/ForwardAlign/MilleRecordWriter.h + include/ForwardAlign/MinResSolve.h + include/ForwardAlign/RectMatrix.h + include/ForwardAlign/SymBDMatrix.h + include/ForwardAlign/SymMatrix.h + include/ForwardAlign/VectorSparse.h + include/ForwardAlign/MilleRecordWriterSpec.h + LINKDEF src/ForwardAlignLinkDef.h) + + + +o2_add_executable( + millerecord-writer-workflow + SOURCES src/MilleRecordWriterSpec.cxx src/millerecord-writer-workflow.cxx + COMPONENT_NAME fwdalign + PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils O2::ReconstructionDataFormats O2::SimulationDataFormat O2::ForwardAlign) diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSparse.h b/Detectors/ForwardAlign/include/ForwardAlign/MatrixSparse.h similarity index 95% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSparse.h rename to Detectors/ForwardAlign/include/ForwardAlign/MatrixSparse.h index 25ea6da8928a6..6c60b4275b583 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSparse.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/MatrixSparse.h @@ -13,15 +13,15 @@ /// \brief Sparse matrix class (from AliROOT), used as a global matrix for MillePede2 /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_MATRIXSPARSE_H -#define ALICEO2_MFT_MATRIXSPARSE_H +#ifndef ALICEO2_FWDALIGN_MATRIXSPARSE_H +#define ALICEO2_FWDALIGN_MATRIXSPARSE_H -#include "MFTAlignment/MatrixSq.h" -#include "MFTAlignment/VectorSparse.h" +#include "ForwardAlign/MatrixSq.h" +#include "ForwardAlign/VectorSparse.h" namespace o2 { -namespace mft +namespace fwdalign { /// \class MatrixSparse @@ -157,7 +157,7 @@ inline Double_t& MatrixSparse::DiagElem(Int_t row) } } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSq.h b/Detectors/ForwardAlign/include/ForwardAlign/MatrixSq.h similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSq.h rename to Detectors/ForwardAlign/include/ForwardAlign/MatrixSq.h index 8c154b359f816..e79fc19004629 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSq.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/MatrixSq.h @@ -13,15 +13,15 @@ /// \brief Abstract class (from AliROOT) for square matrix used for millepede2 operation /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_MATRIXSQ_H -#define ALICEO2_MFT_MATRIXSQ_H +#ifndef ALICEO2_FWDALIGN_MATRIXSQ_H +#define ALICEO2_FWDALIGN_MATRIXSQ_H #include #include namespace o2 { -namespace mft +namespace fwdalign { /// \class MatrixSq @@ -153,7 +153,7 @@ inline void MatrixSq::MultiplyByVec(const TVectorD& vecIn, TVectorD& vecOut) con MultiplyByVec(vecIn.GetMatrixArray(), vecOut.GetMatrixArray()); } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePede2.h b/Detectors/ForwardAlign/include/ForwardAlign/MillePede2.h similarity index 88% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePede2.h rename to Detectors/ForwardAlign/include/ForwardAlign/MillePede2.h index 1ec7ab7bc8df7..2c5bb8ef6dd8d 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePede2.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/MillePede2.h @@ -16,20 +16,20 @@ /// Based on the original milliped2 by Volker Blobel /// http://www.desy.de/~blobel/mptalks.html -#ifndef ALICEO2_MFT_MILLEPEDE2_H -#define ALICEO2_MFT_MILLEPEDE2_H +#ifndef ALICEO2_FWDALIGN_MILLEPEDE2_H +#define ALICEO2_FWDALIGN_MILLEPEDE2_H #include #include #include -#include "MFTAlignment/MinResSolve.h" -#include "MFTAlignment/MillePedeRecord.h" -#include "MFTAlignment/SymMatrix.h" -#include "MFTAlignment/RectMatrix.h" -#include "MFTAlignment/MatrixSparse.h" -#include "MFTAlignment/MatrixSq.h" -#include "MFTAlignment/MilleRecordWriter.h" -#include "MFTAlignment/MilleRecordReader.h" +#include "ForwardAlign/MinResSolve.h" +#include "ForwardAlign/MillePedeRecord.h" +#include "ForwardAlign/SymMatrix.h" +#include "ForwardAlign/RectMatrix.h" +#include "ForwardAlign/MatrixSparse.h" +#include "ForwardAlign/MatrixSq.h" +#include "ForwardAlign/MilleRecordWriter.h" +#include "ForwardAlign/MilleRecordReader.h" class TFile; class TStopwatch; @@ -38,7 +38,7 @@ class TArrayF; namespace o2 { -namespace mft +namespace fwdalign { class MillePede2 @@ -156,6 +156,7 @@ class MillePede2 fNGroupsSet = grID; } } + void ResetRecord() { fRecord->Reset(); } void SetNGloPar(const int n) { fNGloPar = n; } void SetNLocPar(const int n) { fNLocPar = n; } void SetNMaxIterations(const int n = 10) { fMaxIter = n; } @@ -256,17 +257,17 @@ class MillePede2 /// \brief write tree and close file where are stored chi2 from LocalFit() void EndChi2Storage(); - o2::mft::MillePedeRecord* GetRecord() const { return fRecord; } + o2::fwdalign::MillePedeRecord* GetRecord() const { return fRecord; } long GetSelFirst() const { return fSelFirst; } long GetSelLast() const { return fSelLast; } void SetSelFirst(long v) { fSelFirst = v; } void SetSelLast(long v) { fSelLast = v; } - void SetRecord(o2::mft::MillePedeRecord* aRecord) { fRecord = aRecord; } - void SetRecordWriter(o2::mft::MilleRecordWriter* myP) { fRecordWriter = myP; } - void SetConstraintsRecWriter(o2::mft::MilleRecordWriter* myP) { fConstraintsRecWriter = myP; } - void SetRecordReader(o2::mft::MilleRecordReader* myP) { fRecordReader = myP; } - void SetConstraintsRecReader(o2::mft::MilleRecordReader* myP) { fConstraintsRecReader = myP; } + void SetRecord(o2::fwdalign::MillePedeRecord* aRecord) { fRecord = aRecord; } + void SetRecordWriter(o2::fwdalign::MilleRecordWriter* myP) { fRecordWriter = myP; } + void SetConstraintsRecWriter(o2::fwdalign::MilleRecordWriter* myP) { fConstraintsRecWriter = myP; } + void SetRecordReader(o2::fwdalign::MilleRecordReader* myP) { fRecordReader = myP; } + void SetConstraintsRecReader(o2::fwdalign::MilleRecordReader* myP) { fConstraintsRecReader = myP; } /// \brief return the limit in chi^2/nd for n sigmas stdev authorized /// @@ -285,6 +286,9 @@ class MillePede2 fIsLinear[id] = !v; } + /// \brief Disable record writer for DPL process + void DisableRecordWriter() { fDisableRecordWriter = true; } + protected: /// \brief read data record (if any) at entry recID void ReadRecordData(const long recID, const bool doPrint = false); @@ -340,11 +344,11 @@ class MillePede2 std::vector fCGlo2Glo; ///< [fNGloPar] compressed ID to global ID buffer // Matrices - o2::mft::SymMatrix* fMatCLoc; ///< Matrix C local - o2::mft::MatrixSq* fMatCGlo; ///< Matrix C global - o2::mft::RectMatrix* fMatCGloLoc; ///< Rectangular matrix C g*l - std::vector fFillIndex; ///< [fNGloPar] auxilary index array for fast matrix fill - std::vector fFillValue; ///< [fNGloPar] auxilary value array for fast matrix fill + o2::fwdalign::SymMatrix* fMatCLoc; ///< Matrix C local + o2::fwdalign::MatrixSq* fMatCGlo; ///< Matrix C global + o2::fwdalign::RectMatrix* fMatCGloLoc; ///< Rectangular matrix C g*l + std::vector fFillIndex; ///< [fNGloPar] auxilary index array for fast matrix fill + std::vector fFillValue; ///< [fNGloPar] auxilary value array for fast matrix fill TFile* fRecChi2File; TString fRecChi2FName; @@ -354,12 +358,13 @@ class MillePede2 bool fIsChi2BelowLimit; int fRecNDoF; - o2::mft::MillePedeRecord* fRecord; ///< Buffer of measurements records + o2::fwdalign::MillePedeRecord* fRecord; ///< Buffer of measurements records long fCurrRecDataID; ///< ID of the current data record long fCurrRecConstrID; ///< ID of the current constraint record bool fLocFitAdd; ///< Add contribution of carrent track (and not eliminate it) bool fUseRecordWeight; ///< force or ignore the record weight + bool fDisableRecordWriter; ///< disable record writer for DPL process int fMinRecordLength; ///< ignore shorter records int fSelFirst; ///< event selection start int fSelLast; ///< event selection end @@ -380,15 +385,15 @@ class MillePede2 static int fgNKrylovV; ///< size of Krylov vectors buffer in FGMRES // processed data record bufferization - o2::mft::MilleRecordWriter* fRecordWriter; ///< data record writer - o2::mft::MilleRecordWriter* fConstraintsRecWriter; ///< constraints record writer - o2::mft::MilleRecordReader* fRecordReader; ///< data record reader - o2::mft::MilleRecordReader* fConstraintsRecReader; ///< constraints record reader + o2::fwdalign::MilleRecordWriter* fRecordWriter; ///< data record writer + o2::fwdalign::MilleRecordWriter* fConstraintsRecWriter; ///< constraints record writer + o2::fwdalign::MilleRecordReader* fRecordReader; ///< data record reader + o2::fwdalign::MilleRecordReader* fConstraintsRecReader; ///< constraints record reader ClassDef(MillePede2, 0); }; -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePedeRecord.h b/Detectors/ForwardAlign/include/ForwardAlign/MillePedeRecord.h similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePedeRecord.h rename to Detectors/ForwardAlign/include/ForwardAlign/MillePedeRecord.h index ecc28d6f04b98..43dcbf2671c71 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePedeRecord.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/MillePedeRecord.h @@ -16,14 +16,14 @@ /// The records for all processed tracks are stored in the temporary tree in orgder to be /// reused for multiple iterations of MillePede -#ifndef ALICEO2_MFT_MILLEPEDERECORD_H -#define ALICEO2_MFT_MILLEPEDERECORD_H +#ifndef ALICEO2_FWDALIGN_MILLEPEDERECORD_H +#define ALICEO2_FWDALIGN_MILLEPEDERECORD_H #include namespace o2 { -namespace mft +namespace fwdalign { /// \brief Store residuals and local/global deriavtives from a single track processing /// @@ -152,7 +152,7 @@ inline Bool_t MillePedeRecord::IsGroupPresent(Int_t id) const return kFALSE; } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordReader.h b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordReader.h new file mode 100644 index 0000000000000..61c710f509f31 --- /dev/null +++ b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordReader.h @@ -0,0 +1,87 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MilleRecordReader.h +/// \author arakotoz@cern.ch +/// \brief Class dedicated to read MillePedeRecords from ROOT files + +#ifndef ALICEO2_FWDALIGN_MILLERECORD_READER_H +#define ALICEO2_FWDALIGN_MILLERECORD_READER_H + +#include +#include +#include +#include +#include + +#include "ForwardAlign/MillePedeRecord.h" + +namespace o2 +{ +namespace fwdalign +{ + +class MilleRecordReader +{ + public: + /// \brief constructor + MilleRecordReader(); + + /// \brief destructor + virtual ~MilleRecordReader(); + + /// \brief choose data records filename + void changeDataBranchName(const bool isConstraintsRec = true); + + /// \brief connect to input TChain + void connectToChain(TChain* ch); + + /// \brief check if connect to input TChain went well + bool isReaderOk() const { return mIsSuccessfulInit; } + + /// \brief check if the last operation readNextEntry() was ok + bool isReadEntryOk() const { return mIsReadEntryOk; } + + /// \brief return the record + o2::fwdalign::MillePedeRecord* getRecord() { return mRecord; }; + + /// \brief return the ID of the current record in the TTree + long getCurrentDataID() const { return mCurrentDataID; } + + /// \brief read the next entry in the tree + void readNextEntry(const bool doPrint = false); + + /// \brief read the entry # id in the tree + void readEntry(const Long_t id, const bool doPrint = false); + + /// \brief return the number of entries + Long64_t getNEntries() const { return mNEntries; } + + /// \brief return the name of record data tree + TString getDataTreeName() const { return mDataTreeName; } + + protected: + TChain* mDataTree; ///< TChain container that stores the records + bool mIsSuccessfulInit; ///< boolean to monitor the success of the initialization + bool mIsConstraintsRec; ///< boolean to know if these are data records or constraints records + bool mIsReadEntryOk; ///< boolean to know if the last operation readNextEntry() was ok + TString mDataTreeName; ///< name of the record TTree/TChain + TString mDataBranchName; ///< name of the branch where records will be stored + o2::fwdalign::MillePedeRecord* mRecord; ///< the running record + Long64_t mCurrentDataID; ///< counter indicating the ID of the current record in the tree + Long64_t mNEntries; ///< number of entries in the read TChain + + ClassDef(MilleRecordReader, 0); +}; +} // namespace fwdalign +} // namespace o2 + +#endif diff --git a/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriter.h b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriter.h new file mode 100644 index 0000000000000..7af35092c6ff9 --- /dev/null +++ b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriter.h @@ -0,0 +1,90 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MilleRecordWriter.h +/// \author arakotoz@cern.ch +/// \brief Class dedicated to write MillePedeRecords to output file for FWDALIGN + +#ifndef ALICEO2_FWDALIGN_MILLERECORD_WRITER_H +#define ALICEO2_FWDALIGN_MILLERECORD_WRITER_H + +#include +#include + +#include "ForwardAlign/MillePedeRecord.h" + +class TFile; +class TTree; +namespace o2 +{ +namespace fwdalign +{ + +class MilleRecordWriter +{ + public: + /// \brief constructor + MilleRecordWriter(); + + /// \brief destructor + virtual ~MilleRecordWriter(); + + /// \brief Set the number of entries to be used by TTree::AutoSave() + void setCyclicAutoSave(const long nEntries); + + /// \brief choose data records filename + void setDataFileName(TString fname) { mDataFileName = fname; } + + /// \brief choose data records filename + void changeDataBranchName(const bool isConstraintsRec = true); + + /// \brief init output file and tree + void init(); + + /// \brief check if init went well + bool isInitOk() const { return mIsSuccessfulInit; } + + /// \brief return the record + o2::fwdalign::MillePedeRecord* getRecord() { return mRecord; }; + + /// \brief return the ID of the current record in the TTree + Long64_t getCurrentDataID() const { return mCurrentDataID; } + + /// \brief fill tree + void fillRecordTree(const bool doPrint = false); + + /// \brief write tree and close output file + void terminate(); + + /// \brief assign run + void setRecordRun(int run); + + /// \brief assign weight + void setRecordWeight(double wgh); + + protected: + TTree* mDataTree; ///< TTree container that stores the records + TFile* mDataFile; ///< output file where the records are written + bool mIsSuccessfulInit; ///< boolean to monitor the success of the initialization + bool mIsConstraintsRec; ///< boolean to know if these are data records or constraints records + long mNEntriesAutoSave; ///< max entries in the buffer after which TTree::AutoSave() is automatically used + TString mDataFileName; ///< name of the output file that will store the record TTree + TString mDataTreeName; ///< name of the record TTree + TString mDataBranchName; ///< name of the branch where records will be stored + o2::fwdalign::MillePedeRecord* mRecord; ///< the running record + Long64_t mCurrentDataID; ///< counter increasing when adding a record to the tree + + ClassDef(MilleRecordWriter, 0); +}; +} // namespace fwdalign +} // namespace o2 + +#endif diff --git a/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriterSpec.h b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriterSpec.h new file mode 100644 index 0000000000000..b10ab502dace8 --- /dev/null +++ b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriterSpec.h @@ -0,0 +1,33 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MilleRecordWriterSpec.h +/// \brief Implementation of a data processor to write MillePede record in a root file +/// +/// \author Chi Zhang, CEA-Saclay, chi.zhang@cern.ch + +#ifndef ALICEO2_FWDALIGN_MILLERECORDWRITERSPEC_H +#define ALICEO2_FWDALIGN_MILLERECORDWRITERSPEC_H + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace fwdalign +{ + +framework::DataProcessorSpec getMilleRecordWriterSpec(bool useMC, const char* specName = "fwdalign-millerecord-writer", + const char* fileName = "millerecords.root"); + +} // namespace fwdalign +} // namespace o2 + +#endif // ALICEO2_FWDALIGN_MILLERECORDWRITERSPEC_H \ No newline at end of file diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MinResSolve.h b/Detectors/ForwardAlign/include/ForwardAlign/MinResSolve.h similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MinResSolve.h rename to Detectors/ForwardAlign/include/ForwardAlign/MinResSolve.h index 69cbe7faec624..5c0f06e93c49a 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MinResSolve.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/MinResSolve.h @@ -13,8 +13,8 @@ /// \brief General class (from AliROOT) for solving large system of linear equations /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_MINRESSOLVE_H -#define ALICEO2_MFT_MINRESSOLVE_H +#ifndef ALICEO2_FWDALIGN_MINRESSOLVE_H +#define ALICEO2_FWDALIGN_MINRESSOLVE_H #include #include @@ -22,7 +22,7 @@ namespace o2 { -namespace mft +namespace fwdalign { class MatrixSq; @@ -135,7 +135,7 @@ class MinResSolve : public TObject ClassDefOverride(MinResSolve, 0); }; -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/RectMatrix.h b/Detectors/ForwardAlign/include/ForwardAlign/RectMatrix.h similarity index 94% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/RectMatrix.h rename to Detectors/ForwardAlign/include/ForwardAlign/RectMatrix.h index 125478f087b61..fae47c5b22eb4 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/RectMatrix.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/RectMatrix.h @@ -13,15 +13,15 @@ /// \brief Class for rectangular matrix used for millepede2 operation /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_RECTMATRIX_H -#define ALICEO2_MFT_RECTMATRIX_H +#ifndef ALICEO2_FWDALIGN_RECTMATRIX_H +#define ALICEO2_FWDALIGN_RECTMATRIX_H #include "TObject.h" class TString; namespace o2 { -namespace mft +namespace fwdalign { /// \brief Class for rectangular matrix used for millepede2 operation @@ -82,7 +82,7 @@ inline Double_t& RectMatrix::operator()(Int_t row, Int_t col) return (Double_t&)fRows[row][col]; } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymBDMatrix.h b/Detectors/ForwardAlign/include/ForwardAlign/SymBDMatrix.h similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymBDMatrix.h rename to Detectors/ForwardAlign/include/ForwardAlign/SymBDMatrix.h index 8ae1056c1c184..27b7e9df0d1ff 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymBDMatrix.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/SymBDMatrix.h @@ -13,16 +13,16 @@ /// \brief Symmetric Band Diagonal matrix (from AliROOT) with half band width W (+1 for diagonal) /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_SYMBDMATRIX_H -#define ALICEO2_MFT_SYMBDMATRIX_H +#ifndef ALICEO2_FWDALIGN_SYMBDMATRIX_H +#define ALICEO2_FWDALIGN_SYMBDMATRIX_H #include #include -#include "MFTAlignment/MatrixSq.h" +#include "ForwardAlign/MatrixSq.h" namespace o2 { -namespace mft +namespace fwdalign { /// \class SymBDMatrix @@ -170,7 +170,7 @@ inline void SymBDMatrix::MultiplyByVec(const TVectorD& vecIn, TVectorD& vecOut) MultiplyByVec(vecIn.GetMatrixArray(), vecOut.GetMatrixArray()); } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymMatrix.h b/Detectors/ForwardAlign/include/ForwardAlign/SymMatrix.h similarity index 98% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymMatrix.h rename to Detectors/ForwardAlign/include/ForwardAlign/SymMatrix.h index e86c3bb1f9886..8de3867419202 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymMatrix.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/SymMatrix.h @@ -13,17 +13,17 @@ /// \brief Fast symmetric matrix (from AliROOT) with dynamically expandable size /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_SYMMATRIX_H -#define ALICEO2_MFT_SYMMATRIX_H +#ifndef ALICEO2_FWDALIGN_SYMMATRIX_H +#define ALICEO2_FWDALIGN_SYMMATRIX_H #include #include -#include "MFTAlignment/MatrixSq.h" +#include "ForwardAlign/MatrixSq.h" namespace o2 { -namespace mft +namespace fwdalign { /// \class SymMatrix @@ -277,7 +277,7 @@ inline void SymMatrix::AddToRow(Int_t r, Double_t* valc, Int_t* indc, Int_t n) } } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/VectorSparse.h b/Detectors/ForwardAlign/include/ForwardAlign/VectorSparse.h similarity index 95% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/VectorSparse.h rename to Detectors/ForwardAlign/include/ForwardAlign/VectorSparse.h index 993ce59014ce7..0e36af5a62c7a 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/VectorSparse.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/VectorSparse.h @@ -13,15 +13,15 @@ /// \brief Sparse vector class (from AliROOT), used as row of the MatrixSparse class /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_VECTORSPARSE_H -#define ALICEO2_MFT_VECTORSPARSE_H +#ifndef ALICEO2_FWDALIGN_VECTORSPARSE_H +#define ALICEO2_FWDALIGN_VECTORSPARSE_H #include #include namespace o2 { -namespace mft +namespace fwdalign { /// \class VectorSparse @@ -97,7 +97,7 @@ inline Double_t& VectorSparse::operator()(Int_t ind) return FindIndexAdd(ind); } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ForwardAlign/src/ForwardAlignLinkDef.h b/Detectors/ForwardAlign/src/ForwardAlignLinkDef.h new file mode 100644 index 0000000000000..eb447d28e7868 --- /dev/null +++ b/Detectors/ForwardAlign/src/ForwardAlignLinkDef.h @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::fwdalign::MatrixSparse + ; +#pragma link C++ class o2::fwdalign::MatrixSq + ; +#pragma link C++ class o2::fwdalign::MillePede2 + ; +#pragma link C++ class o2::fwdalign::MillePedeRecord + ; +#pragma link C++ class std::vector < o2::fwdalign::MillePedeRecord> + ; +#pragma link C++ class o2::fwdalign::MilleRecordReader + ; +#pragma link C++ class o2::fwdalign::MilleRecordWriter + ; +#pragma link C++ class o2::fwdalign::MinResSolve + ; +#pragma link C++ class o2::fwdalign::RectMatrix + ; +#pragma link C++ class o2::fwdalign::SymBDMatrix + ; +#pragma link C++ class o2::fwdalign::SymMatrix + ; +#pragma link C++ class o2::fwdalign::VectorSparse + ; + +#endif diff --git a/Detectors/ITSMFT/MFT/alignment/src/MatrixSparse.cxx b/Detectors/ForwardAlign/src/MatrixSparse.cxx similarity index 98% rename from Detectors/ITSMFT/MFT/alignment/src/MatrixSparse.cxx rename to Detectors/ForwardAlign/src/MatrixSparse.cxx index acbb7ea94a334..210b0c3a77f5c 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MatrixSparse.cxx +++ b/Detectors/ForwardAlign/src/MatrixSparse.cxx @@ -15,9 +15,9 @@ #include #include "Framework/Logger.h" -#include "MFTAlignment/MatrixSparse.h" +#include "ForwardAlign/MatrixSparse.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(MatrixSparse); diff --git a/Detectors/ITSMFT/MFT/alignment/src/MatrixSq.cxx b/Detectors/ForwardAlign/src/MatrixSq.cxx similarity index 96% rename from Detectors/ITSMFT/MFT/alignment/src/MatrixSq.cxx rename to Detectors/ForwardAlign/src/MatrixSq.cxx index abb794def134d..1aadc3b6563d5 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MatrixSq.cxx +++ b/Detectors/ForwardAlign/src/MatrixSq.cxx @@ -17,9 +17,9 @@ #include "TMath.h" #include "Framework/Logger.h" -#include "MFTAlignment/MatrixSq.h" +#include "ForwardAlign/MatrixSq.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(MatrixSq); diff --git a/Detectors/ITSMFT/MFT/alignment/src/MillePede2.cxx b/Detectors/ForwardAlign/src/MillePede2.cxx similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/src/MillePede2.cxx rename to Detectors/ForwardAlign/src/MillePede2.cxx index af445c14f09c1..5ad475d893271 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MillePede2.cxx +++ b/Detectors/ForwardAlign/src/MillePede2.cxx @@ -10,8 +10,8 @@ // or submit itself to any jurisdiction. /// @file MillePede2.cxx - -#include "MFTAlignment/MillePede2.h" +#include +#include "ForwardAlign/MillePede2.h" #include "Framework/Logger.h" #include #include @@ -27,14 +27,14 @@ #include #include -//#define _DUMP_EQ_BEFORE_ -//#define _DUMP_EQ_AFTER_ +// #define _DUMP_EQ_BEFORE_ +// #define _DUMP_EQ_AFTER_ -//#define _DUMPEQ_BEFORE_ -//#define _DUMPEQ_AFTER_ +// #define _DUMPEQ_BEFORE_ +// #define _DUMPEQ_AFTER_ using std::ifstream; -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(MillePede2); @@ -147,7 +147,8 @@ MillePede2::MillePede2(const MillePede2& src) fRecordWriter(nullptr), fConstraintsRecWriter(nullptr), fRecordReader(nullptr), - fConstraintsRecReader(nullptr) + fConstraintsRecReader(nullptr), + fDisableRecordWriter(false) { fWghScl[0] = src.fWghScl[0]; fWghScl[1] = src.fWghScl[1]; @@ -314,16 +315,17 @@ void MillePede2::EndChi2Storage() void MillePede2::SetLocalEquation(std::vector& dergb, std::vector& derlc, const double lMeas, const double lSigma) { - if (!fRecordWriter) { - LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: null pointer to record writer"; - return; - } - if (!fRecordWriter->isInitOk()) { - LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: unintialised record writer"; - return; + if (!fDisableRecordWriter) { + if (!fRecordWriter) { + LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: null pointer to record writer"; + return; + } + if (!fRecordWriter->isInitOk()) { + LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: unintialised record writer"; + return; + } + SetRecord(fRecordWriter->getRecord()); } - SetRecord(fRecordWriter->getRecord()); - // write data of single measurement if (lSigma <= 0.0) { // If parameter is fixed, then no equation for (int i = fNLocPar; i--;) { @@ -336,7 +338,6 @@ void MillePede2::SetLocalEquation(std::vector& dergb, std::vectorAddResidual(lMeas); - // Retrieve local param interesting indices for (int i = 0; i < fNLocPar; i++) { if (!IsZero(derlc[i])) { @@ -364,16 +365,17 @@ void MillePede2::SetLocalEquation(std::vector& indgb, std::vector& std::vector& derlc, const int nlc, const double lMeas, const double lSigma) { - if (!fRecordWriter) { - LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: null pointer to record writer"; - return; - } - if (!fRecordWriter->isInitOk()) { - LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: unintialised record writer"; - return; + if (!fDisableRecordWriter) { + if (!fRecordWriter) { + LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: null pointer to record writer"; + return; + } + if (!fRecordWriter->isInitOk()) { + LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: unintialised record writer"; + return; + } + SetRecord(fRecordWriter->getRecord()); } - SetRecord(fRecordWriter->getRecord()); - if (lSigma <= 0.0) { // If parameter is fixed, then no equation for (int i = nlc; i--;) { derlc[i] = 0.0; @@ -1384,7 +1386,7 @@ int MillePede2::SolveGlobalMatEq() if (fgIterSol == MinResSolve::kSolFGMRes) { res = slv->SolveFGMRES(sol, fgMinResCondType, fgMinResMaxIter, fgMinResTol, fgNKrylovV); } else { - LOGF(warning, "MillePede2 - Undefined Iteritive Solver ID=%d, only %d are defined", fgIterSol, MinResSolve::kNSolvers); + LOGF(warning, "MillePede2 - Undefined Iteritive Solver ID=%d, only %d are defined", fgIterSol, (int)MinResSolve::kNSolvers); } } diff --git a/Detectors/ITSMFT/MFT/alignment/src/MillePedeRecord.cxx b/Detectors/ForwardAlign/src/MillePedeRecord.cxx similarity index 99% rename from Detectors/ITSMFT/MFT/alignment/src/MillePedeRecord.cxx rename to Detectors/ForwardAlign/src/MillePedeRecord.cxx index d87a7ee9a77d6..447b01bc60035 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MillePedeRecord.cxx +++ b/Detectors/ForwardAlign/src/MillePedeRecord.cxx @@ -11,11 +11,11 @@ /// @file MillePedeRecord.cxx -#include "MFTAlignment/MillePedeRecord.h" +#include "ForwardAlign/MillePedeRecord.h" #include #include "Framework/Logger.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(MillePedeRecord); diff --git a/Detectors/ITSMFT/MFT/alignment/src/MilleRecordReader.cxx b/Detectors/ForwardAlign/src/MilleRecordReader.cxx similarity index 96% rename from Detectors/ITSMFT/MFT/alignment/src/MilleRecordReader.cxx rename to Detectors/ForwardAlign/src/MilleRecordReader.cxx index 0cab0726c7e24..927e7972c222b 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MilleRecordReader.cxx +++ b/Detectors/ForwardAlign/src/MilleRecordReader.cxx @@ -13,11 +13,11 @@ #include "Framework/Logger.h" -#include "MFTAlignment/MilleRecordReader.h" +#include "ForwardAlign/MilleRecordReader.h" -using namespace o2::mft; +using namespace o2::fwdalign; -ClassImp(o2::mft::MilleRecordReader); +ClassImp(o2::fwdalign::MilleRecordReader); //__________________________________________________________________________ MilleRecordReader::MilleRecordReader() @@ -25,7 +25,7 @@ MilleRecordReader::MilleRecordReader() mIsSuccessfulInit(false), mIsConstraintsRec(false), mIsReadEntryOk(false), - mDataTreeName("milleRecords"), + mDataTreeName("o2sim"), mDataBranchName("data"), mRecord(nullptr), mCurrentDataID(-1), diff --git a/Detectors/ITSMFT/MFT/alignment/src/MilleRecordWriter.cxx b/Detectors/ForwardAlign/src/MilleRecordWriter.cxx similarity index 95% rename from Detectors/ITSMFT/MFT/alignment/src/MilleRecordWriter.cxx rename to Detectors/ForwardAlign/src/MilleRecordWriter.cxx index 91b6438d968fd..e36c47562c81e 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MilleRecordWriter.cxx +++ b/Detectors/ForwardAlign/src/MilleRecordWriter.cxx @@ -16,11 +16,11 @@ #include "Framework/Logger.h" -#include "MFTAlignment/MilleRecordWriter.h" +#include "ForwardAlign/MilleRecordWriter.h" -using namespace o2::mft; +using namespace o2::fwdalign; -ClassImp(o2::mft::MilleRecordWriter); +ClassImp(o2::fwdalign::MilleRecordWriter); //__________________________________________________________________________ MilleRecordWriter::MilleRecordWriter() @@ -29,8 +29,8 @@ MilleRecordWriter::MilleRecordWriter() mIsSuccessfulInit(false), mIsConstraintsRec(false), mNEntriesAutoSave(10000), - mDataFileName("mft_mille_records.root"), - mDataTreeName("milleRecords"), + mDataFileName("millerecords.root"), + mDataTreeName("o2sim"), mDataBranchName("data"), mRecord(nullptr), mCurrentDataID(-1) @@ -135,6 +135,7 @@ void MilleRecordWriter::terminate() mDataTree->Write(); LOG(info) << "MilleRecordWriter::terminate() - wrote tree " << mDataTreeName.Data(); + mDataFile->Close(); } } diff --git a/Detectors/ForwardAlign/src/MilleRecordWriterSpec.cxx b/Detectors/ForwardAlign/src/MilleRecordWriterSpec.cxx new file mode 100644 index 0000000000000..e3fe8c12a9ad0 --- /dev/null +++ b/Detectors/ForwardAlign/src/MilleRecordWriterSpec.cxx @@ -0,0 +1,42 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MilleRecordWriterSpec.cxx +/// \brief Implementation of a data processor to write MillePede record in a root file +/// +/// \author Chi Zhang, CEA-Saclay, chi.zhang@cern.ch + +#include "ForwardAlign/MilleRecordWriterSpec.h" + +#include +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "ForwardAlign/MillePedeRecord.h" + +namespace o2 +{ +namespace fwdalign +{ + +using namespace o2::framework; + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; + +DataProcessorSpec getMilleRecordWriterSpec(bool useMC, const char* specName, const char* fileName) +{ + return MakeRootTreeWriterSpec(specName, + fileName, + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree MillePede records for MCH-MID tracks"}, + BranchDefinition{InputSpec{"data", "MUON", "RECORD_MCHMID", Lifetime::Sporadic}, "data"})(); +} + +} // namespace fwdalign +} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/MFT/alignment/src/MinResSolve.cxx b/Detectors/ForwardAlign/src/MinResSolve.cxx similarity index 99% rename from Detectors/ITSMFT/MFT/alignment/src/MinResSolve.cxx rename to Detectors/ForwardAlign/src/MinResSolve.cxx index 14eae8262c45d..6d1982307fc1e 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MinResSolve.cxx +++ b/Detectors/ForwardAlign/src/MinResSolve.cxx @@ -16,12 +16,12 @@ #include #include "Framework/Logger.h" -#include "MFTAlignment/MinResSolve.h" -#include "MFTAlignment/MatrixSq.h" -#include "MFTAlignment/MatrixSparse.h" -#include "MFTAlignment/SymBDMatrix.h" +#include "ForwardAlign/MinResSolve.h" +#include "ForwardAlign/MatrixSq.h" +#include "ForwardAlign/MatrixSparse.h" +#include "ForwardAlign/SymBDMatrix.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(MinResSolve); diff --git a/Detectors/ITSMFT/MFT/alignment/src/RectMatrix.cxx b/Detectors/ForwardAlign/src/RectMatrix.cxx similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/src/RectMatrix.cxx rename to Detectors/ForwardAlign/src/RectMatrix.cxx index a9b958b1fe308..94eb9fdf70359 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/RectMatrix.cxx +++ b/Detectors/ForwardAlign/src/RectMatrix.cxx @@ -12,9 +12,9 @@ /// @file RectMatrix.cxx #include -#include "MFTAlignment/RectMatrix.h" +#include "ForwardAlign/RectMatrix.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(RectMatrix); diff --git a/Detectors/ITSMFT/MFT/alignment/src/SymBDMatrix.cxx b/Detectors/ForwardAlign/src/SymBDMatrix.cxx similarity index 99% rename from Detectors/ITSMFT/MFT/alignment/src/SymBDMatrix.cxx rename to Detectors/ForwardAlign/src/SymBDMatrix.cxx index 6dd4129e1d7c7..84d002d0933ea 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/SymBDMatrix.cxx +++ b/Detectors/ForwardAlign/src/SymBDMatrix.cxx @@ -15,9 +15,9 @@ #include "TClass.h" #include "TMath.h" -#include "MFTAlignment/SymBDMatrix.h" +#include "ForwardAlign/SymBDMatrix.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(SymBDMatrix); diff --git a/Detectors/ITSMFT/MFT/alignment/src/SymMatrix.cxx b/Detectors/ForwardAlign/src/SymMatrix.cxx similarity index 99% rename from Detectors/ITSMFT/MFT/alignment/src/SymMatrix.cxx rename to Detectors/ForwardAlign/src/SymMatrix.cxx index 498cad5d080fc..c5e5267645148 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/SymMatrix.cxx +++ b/Detectors/ForwardAlign/src/SymMatrix.cxx @@ -15,10 +15,10 @@ #include #include -#include "MFTAlignment/SymMatrix.h" +#include "ForwardAlign/SymMatrix.h" #include "Framework/Logger.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(SymMatrix); diff --git a/Detectors/ITSMFT/MFT/alignment/src/VectorSparse.cxx b/Detectors/ForwardAlign/src/VectorSparse.cxx similarity index 98% rename from Detectors/ITSMFT/MFT/alignment/src/VectorSparse.cxx rename to Detectors/ForwardAlign/src/VectorSparse.cxx index 94463b8650297..b2e7f346b91d2 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/VectorSparse.cxx +++ b/Detectors/ForwardAlign/src/VectorSparse.cxx @@ -13,9 +13,9 @@ #include -#include "MFTAlignment/VectorSparse.h" +#include "ForwardAlign/VectorSparse.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(VectorSparse); diff --git a/Detectors/ForwardAlign/src/millerecord-writer-workflow.cxx b/Detectors/ForwardAlign/src/millerecord-writer-workflow.cxx new file mode 100644 index 0000000000000..9144ef1cb1025 --- /dev/null +++ b/Detectors/ForwardAlign/src/millerecord-writer-workflow.cxx @@ -0,0 +1,41 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file millerecord-writer-workflow.cxx +/// \brief Implementation of a DPL device to run the MillePede record writer +/// +/// \author Chi Zhang, CEA-Saclay, chi.zhang@cern.ch + +#include "ForwardAlign/MilleRecordWriterSpec.h" +#include "Framework/CompletionPolicyHelpers.h" + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + // ordered policies for the writers + policies.push_back(CompletionPolicyHelpers::consumeWhenAllOrdered(".*(?:FWDALIGN|fwdalign).*[W,w]riter.*")); +} + +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + workflowOptions.emplace_back("disable-mc", VariantType::Bool, false, + ConfigParamSpec::HelpString{"disable MC propagation even if available"}); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& configcontext) +{ + auto useMC = !configcontext.options().get("disable-mc"); + return WorkflowSpec{o2::fwdalign::getMilleRecordWriterSpec(useMC)}; +} \ No newline at end of file diff --git a/Detectors/GLOQC/CMakeLists.txt b/Detectors/GLOQC/CMakeLists.txt new file mode 100644 index 0000000000000..9d2db85460b69 --- /dev/null +++ b/Detectors/GLOQC/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. +# add_compile_options(-O0 -g -fPIC) + +o2_add_library(GLOQC + TARGETVARNAME targetName + SOURCES src/MatchITSTPCQC.cxx + src/ITSTPCMatchingQCParams.cxx + + PUBLIC_LINK_LIBRARIES O2::DetectorsVertexing + PRIVATE_LINK_LIBRARIES O2::GPUO2Interface + O2::GPUTracking) + +o2_target_root_dictionary(GLOQC + HEADERS include/GLOQC/MatchITSTPCQC.h + include/GLOQC/ITSTPCMatchingQCParams.h + +) + + diff --git a/Detectors/GLOQC/include/GLOQC/ITSTPCMatchingQCParams.h b/Detectors/GLOQC/include/GLOQC/ITSTPCMatchingQCParams.h new file mode 100644 index 0000000000000..024497b1b918e --- /dev/null +++ b/Detectors/GLOQC/include/GLOQC/ITSTPCMatchingQCParams.h @@ -0,0 +1,58 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author Chiara.Zampolli@cern.ch + +#ifndef ALICEO2_ITSTPCMATCHINGQC_PARAMS_H +#define ALICEO2_ITSTPCMATCHINGQC_PARAMS_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::gloqc +{ + +// There are configurable params for TPC-ITS matching +struct ITSTPCMatchingQCParams : public o2::conf::ConfigurableParamHelper { + + int nBinsPt = 100; + float minPtITSCut = 0.1; + float etaITSCut = 1.4; + int32_t minNITSClustersCut = 0; + float maxChi2PerClusterITS = 1e10; + float minPtTPCCut = 0.1; + float etaTPCCut = 1.4; + int32_t minNTPCClustersCut = 60; + float minDCACut = 100.; + float minDCACutY = 10.; + float minPtCut = 0.1; + float maxPtCut = 20; + float etaCut = 1.4; + float etaNo0Cut = 0.05; + float cutK0Mass = 0.05f; + float maxEtaK0 = 0.8f; + float K0Scaling = 1.f; + float minTPCOccpp = 0.f; + float maxTPCOccpp = 1.e6; + int nBinsTPCOccpp = 6; + float minTPCOccPbPb = 0.f; + float maxTPCOccPbPb = 8.e6; + int nBinsTPCOccPbPb = 8; + float maxK0DCA = 0.01; + float minK0CosPA = 0.995; + + O2ParamDef(ITSTPCMatchingQCParams, "ITSTPCMatchingQC"); +}; + +} // namespace o2::gloqc + // end namespace o2 + +#endif diff --git a/Detectors/GLOQC/include/GLOQC/MatchITSTPCQC.h b/Detectors/GLOQC/include/GLOQC/MatchITSTPCQC.h new file mode 100644 index 0000000000000..356d3e80d210e --- /dev/null +++ b/Detectors/GLOQC/include/GLOQC/MatchITSTPCQC.h @@ -0,0 +1,465 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MatchITSTPCQC.h +/// \brief Class to perform QC for ITSTPC matching +/// \author chiara.zampolli@cern.ch + +#ifndef ALICEO2_GLOQC_MATCHITSTPC_QC_ +#define ALICEO2_GLOQC_MATCHITSTPC_QC_ + +#include +#include +#include +#include +#include +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "Framework/ProcessingContext.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "Steer/MCKinematicsReader.h" +#include "ReconstructionDataFormats/PID.h" +#include "DCAFitter/DCAFitterN.h" +#include "GPUO2InterfaceConfiguration.h" +#include "GPUParam.h" +#include "GPUParam.inc" + +#include +#include +#include +#include + +namespace o2 +{ + +namespace gloqc +{ + +using GID = o2::dataformats::GlobalTrackID; + +struct LblInfo { + + int mIdx = -1; + bool mIsPhysicalPrimary = false; +}; + +class MatchITSTPCQC +{ + public: + enum matchType : uint8_t { TPC = 0, + ITS, + SIZE }; + + MatchITSTPCQC() = default; + MatchITSTPCQC(const MatchITSTPCQC&) = delete; + MatchITSTPCQC(MatchITSTPCQC&&) = delete; + MatchITSTPCQC& operator=(const MatchITSTPCQC&) = delete; + MatchITSTPCQC& operator=(MatchITSTPCQC&&) = delete; + ~MatchITSTPCQC(); + + bool init(); + void initDataRequest(); + void run(o2::framework::ProcessingContext& ctx); + void setDataRequest(const std::shared_ptr& dr) { mDataRequest = dr; } + void finalize(); + void reset(); + bool processV0(int iv, o2::globaltracking::RecoContainer& recoData, std::vector& mTBinClOcc, float pvTime); + bool refitV0(const o2::dataformats::V0Index& id, o2::dataformats::V0& v0, o2::globaltracking::RecoContainer& recoData); + + TH1D* getHistoPtNum(matchType m) const { return mPtNum[m]; } + TH1D* getHistoPtDen(matchType m) const { return mPtDen[m]; } + TEfficiency* getFractionITSTPCmatch(matchType m) const { return mFractionITSTPCmatch[m]; } + + TH1D* getHistoPtNumNoEta0(matchType m) const { return mPtNum_noEta0[m]; } + TH1D* getHistoPtDenNoEta0(matchType m) const { return mPtDen_noEta0[m]; } + TEfficiency* getFractionITSTPCmatchNoEta0(matchType m) const { return mFractionITSTPCmatch_noEta0[m]; } + + TH1F* getHistoPhiNum(matchType m) const { return mPhiNum[m]; } + TH1F* getHistoPhiDen(matchType m) const { return mPhiDen[m]; } + TEfficiency* getFractionITSTPCmatchPhi(matchType m) const { return mFractionITSTPCmatchPhi[m]; } + + TH2F* getHistoPhiVsPtNum(matchType m) const { return mPhiVsPtNum[m]; } + TH2F* getHistoPhiVsPtDen(matchType m) const { return mPhiVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchPhiVsPt(matchType m) const { return mFractionITSTPCmatchPhiVsPt[m]; } + + TH1F* getHistoEtaNum(matchType m) const { return mEtaNum[m]; } + TH1F* getHistoEtaDen(matchType m) const { return mEtaDen[m]; } + TEfficiency* getFractionITSTPCmatchEta(matchType m) const { return mFractionITSTPCmatchEta[m]; } + + TH2F* getHistoEtaVsPtNum(matchType m) const { return mEtaVsPtNum[m]; } + TH2F* getHistoEtaVsPtDen(matchType m) const { return mEtaVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchEtaVsPt(matchType m) const { return mFractionITSTPCmatchEtaVsPt[m]; } + + TH2F* getHistoClsVsPtNum(matchType m) const { return mClsVsPtNum[m]; } + TH2F* getHistoClsVsPtDen(matchType m) const { return mClsVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchClsVsPt(matchType m) const { return mFractionITSTPCmatchClsVsPt[m]; } + + TH2F* getHistoChi2VsPtNum(matchType m) const { return mChi2VsPtNum[m]; } + TH2F* getHistoChi2VsPtDen(matchType m) const { return mChi2VsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchChi2VsPt(matchType m) const { return mFractionITSTPCmatchChi2VsPt[m]; } + + TH1F* getHistoPtPhysPrimNum(matchType m) const { return mPtPhysPrimNum[m]; } + TH1F* getHistoPtPhysPrimDen(matchType m) const { return mPtPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhysPrim(matchType m) const { return mFractionITSTPCmatchPhysPrim[m]; } + + TH1F* getHistoPhiPhysPrimNum(matchType m) const { return mPhiPhysPrimNum[m]; } + TH1F* getHistoPhiPhysPrimDen(matchType m) const { return mPhiPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhiPhysPrim(matchType m) const { return mFractionITSTPCmatchPhiPhysPrim[m]; } + + TH1F* getHistoEtaPhysPrimNum(matchType m) const { return mEtaPhysPrimNum[m]; } + TH1F* getHistoEtaPhysPrimDen(matchType m) const { return mEtaPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchEtaPhysPrim(matchType m) const { return mFractionITSTPCmatchEtaPhysPrim[m]; } + + TH2F* getHistoResidualPt() const { return mResidualPt; } + TH2F* getHistoResidualPhi() const { return mResidualPhi; } + TH2F* getHistoResidualEta() const { return mResidualEta; } + + TH1F* getHistoChi2Matching() const { return mChi2Matching; } + TH1F* getHistoChi2Refit() const { return mChi2Refit; } + TH2F* getHistoTimeResVsPt() const { return mTimeResVsPt; } + TH1F* getHistoDCAr() const { return mDCAr; } + TH2F* getHistoDCArVsPtNum() const { return mDCArVsPtNum; } + TH2F* getHistoDCArVsPtDen() const { return mDCArVsPtDen; } + TEfficiency* getFractionITSTPCmatchDCArVsPt() const { return mFractionITSTPCmatchDCArVsPt; } + + TH1D* getHisto1OverPtNum(matchType m) const { return m1OverPtNum[m]; } + TH1D* getHisto1OverPtDen(matchType m) const { return m1OverPtDen[m]; } + TEfficiency* getFractionITSTPCmatch1OverPt(matchType m) const { return mFractionITSTPCmatch1OverPt[m]; } + + TH1D* getHisto1OverPtPhysPrimNum(matchType m) const { return m1OverPtPhysPrimNum[m]; } + TH1D* getHisto1OverPtPhysPrimDen(matchType m) const { return m1OverPtPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhysPrim1OverPt(matchType m) const { return mFractionITSTPCmatchPhysPrim1OverPt[m]; } + + TH3F* getHistoEtaPhiPtNum(matchType m) const { return mEtaPhiPtNum[m]; } + TH3F* getHistoEtaPhiPtDen(matchType m) const { return mEtaPhiPtDen[m]; } + + TH3F* getHistoK0MassVsPtVsOccpp() const { return mK0MassVsPtVsOccpp; } + TH3F* getHistoK0MassVsPtVsOccPbPb() const { return mK0MassVsPtVsOccPbPb; } + + void getHistos(TObjArray& objar); + + /// \brief Publishes the histograms to the publisher e.g. the one provided by the QC task + /// \tparam T type of the publisher + /// \param publisher the publisher e.g. getObjectsManager() + template + void publishHistograms(const std::shared_ptr& publisher) + { + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + publisher->startPublishing(mPtNum[i]); + publisher->startPublishing(mPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatch[i]); + + publisher->startPublishing(mPtNum_noEta0[i]); + publisher->startPublishing(mPtDen_noEta0[i]); + publisher->startPublishing(mFractionITSTPCmatch_noEta0[i]); + + // Phi + publisher->startPublishing(mPhiNum[i]); + publisher->startPublishing(mPhiDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhi[i]); + + publisher->startPublishing(mPhiVsPtNum[i]); + publisher->startPublishing(mPhiVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhiVsPt[i]); + + // Eta + publisher->startPublishing(mEtaNum[i]); + publisher->startPublishing(mEtaDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEta[i]); + + publisher->startPublishing(mEtaVsPtNum[i]); + publisher->startPublishing(mEtaVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEtaVsPt[i]); + + // Clusters + publisher->startPublishing(mClsVsPtNum[i]); + publisher->startPublishing(mClsVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchClsVsPt[i]); + + // Chi2 + publisher->startPublishing(mChi2VsPtNum[i]); + publisher->startPublishing(mChi2VsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchChi2VsPt[i]); + + // 1/pt + publisher->startPublishing(m1OverPtNum[i]); + publisher->startPublishing(m1OverPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatch1OverPt[i]); + + // 3D eta/phi/pt + publisher->startPublishing(mEtaPhiPtNum[i]); + publisher->startPublishing(mEtaPhiPtDen[i]); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + publisher->startPublishing(mPtNumVsTrkPID[i][j]); + publisher->startPublishing(mPtDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchPtVsTrkPID[i][j]); + + // Phi + publisher->startPublishing(mPhiNumVsTrkPID[i][j]); + publisher->startPublishing(mPhiDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchPhiVsTrkPID[i][j]); + + // Eta + publisher->startPublishing(mEtaNumVsTrkPID[i][j]); + publisher->startPublishing(mEtaDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchEtaVsTrkPID[i][j]); + } + } + + if (mUseMC) { + publisher->startPublishing(mPhiPhysPrimNum[i]); + publisher->startPublishing(mPhiPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhiPhysPrim[i]); + publisher->startPublishing(mPtPhysPrimNum[i]); + publisher->startPublishing(mPtPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhysPrim[i]); + publisher->startPublishing(mEtaPhysPrimNum[i]); + publisher->startPublishing(mEtaPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEtaPhysPrim[i]); + publisher->startPublishing(m1OverPtPhysPrimNum[i]); + publisher->startPublishing(m1OverPtPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhysPrim1OverPt[i]); + } + } + publisher->startPublishing(mChi2Matching); + publisher->startPublishing(mChi2Refit); + publisher->startPublishing(mTimeResVsPt); + publisher->startPublishing(mResidualPt); + publisher->startPublishing(mResidualPhi); + publisher->startPublishing(mResidualEta); + publisher->startPublishing(mDCAr); + publisher->startPublishing(mDCArVsPtNum); + publisher->startPublishing(mDCArVsPtDen); + publisher->startPublishing(mFractionITSTPCmatchDCArVsPt); + if (mDoK0QC) { + publisher->startPublishing(mK0MassVsPtVsOccpp); + publisher->startPublishing(mK0MassVsPtVsOccPbPb); + } + } + + void setTrkSources(GID::mask_t src) { mSrc = src; } + void setUseTrkPID(bool b) { mUseTrkPID = b; } + bool getUseTrkPID() const { return mUseTrkPID; } + void setUseMC(bool b) { mUseMC = b; } + bool getUseMC() const { return mUseMC; } + void deleteHistograms(); + void setBz(float bz) { mBz = bz; } + void setDoK0QC(bool v) { mDoK0QC = v; } + bool getDoK0QC() const { return mDoK0QC; } + + // ITS track + void setMinPtITSCut(float v) { mPtITSCut = v; }; + void setEtaITSCut(float v) { mEtaITSCut = v; }; // TODO: define 2 different values for min and max (**) + void setMinNClustersITS(int v) { mMinNClustersITS = v; } + void setMaxChi2PerClusterITS(float v) { mMaxChi2PerClusterITS = v; } + // TO DO: define an agreed way to implement the setter for ITS matching (min. # layers, which layers) + // [...] --> exploit the method TrackCuts::setRequireHitsInITSLayers(...) + // TPC track + void setMinPtTPCCut(float v) { mPtTPCCut = v; }; + void setEtaTPCCut(float v) { mEtaTPCCut = v; }; // TODO: define 2 different values for min and max (***) + void setMinNTPCClustersCut(int v) { mNTPCClustersCut = v; } + void setMinDCAtoBeamPipeCut(std::array v) + { + setMinDCAtoBeamPipeDistanceCut(v[0]); + setMinDCAtoBeamPipeYCut(v[1]); + } + void setMinDCAtoBeamPipeDistanceCut(float v) { mDCATPCCut = v; } + void setMinDCAtoBeamPipeYCut(float v) { mDCATPCCutY = v; } + // ITS-TPC kinematics + void setNBinsPt(int v) { mPtBins = v; } + void setPtCut(float v) { mPtCut = v; } + void setMaxPtCut(float v) { mPtMaxCut = v; } + void setEtaCut(float v) { mEtaCut = v; } + void setEtaNo0Cut(float v) { mEtaNo0Cut = v; } + + // K0 + void setMaxK0Eta(float v) { mMaxEtaK0 = v; } + void setRefitK0(bool v) { mRefit = v; } + void setCutK0Mass(float v) { mCutK0Mass = v; } + void setMinTPCOccpp(float v) { mMinTPCOccpp = v; } + void setMaxTPCOccpp(float v) { mMaxTPCOccpp = v; } + void setNBinsTPCOccpp(int v) { mNBinsTPCOccpp = v; } + void setMinTPCOccPbPb(float v) { mMinTPCOccPbPb = v; } + void setMaxTPCOccPbPb(float v) { mMaxTPCOccPbPb = v; } + void setNBinsTPCOccPbPb(int v) { mNBinsTPCOccPbPb = v; } + void setK0Scaling(float v) { mK0Scaling = v; } + float getK0Scaling() const { return mK0Scaling; } + void setK0MaxDCA(float v) { mK0MaxDCA = v; } + float getK0MaxDCA() const { return mK0MaxDCA; } + void setK0MinCosPA(float v) { mK0MinCosPA = v; } + float getK0MinCosPA() const { return mK0MinCosPA; } + + void printParams() const; + + private: + std::shared_ptr mDataRequest; + o2::globaltracking::RecoContainer mRecoCont; + std::string mRequestedSources = "ITS,TPC,ITS-TPC"; + GID::mask_t mSrc = GID::getSourcesMask("ITS,TPC,ITS-TPC"); + GID::mask_t mAllowedSources = GID::getSourcesMask("all"); + // TPC + gsl::span mTPCTracks; + // ITS + gsl::span mITSTracks; + // ITS-TPC + gsl::span mITSTPCTracks; + bool mUseMC = false; // Usage of the MC information + bool mUseTrkPID = false; // Usage of the PID hypothesis in tracking + float mBz = 0; ///< nominal Bz + std::array, matchType::SIZE> mMapLabels; // map with labels that have been found for the matched ITSTPC tracks; key is the label, + // value is the LbLinfo with the id of the track with the highest pT found with that label so far, + // and the flag to say if it is a physical primary or not + std::array, matchType::SIZE> mMapRefLabels; // map with labels that have been found for the unmatched TPC tracks; key is the label, + // value is the LblInfo with the id of the track with the highest number of TPC clusters found + // with that label so far, and the flag to say if it is a physical primary or not + o2::steer::MCKinematicsReader mcReader; // reader of MC information + + // Pt + TH1D* mPtNum[matchType::SIZE] = {}; + TH1D* mPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch[matchType::SIZE] = {}; + TH1D* mPtNum_noEta0[matchType::SIZE] = {}; + TH1D* mPtDen_noEta0[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch_noEta0[matchType::SIZE] = {}; + TH1F* mPtPhysPrimNum[matchType::SIZE] = {}; + TH1F* mPtPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhysPrim[matchType::SIZE] = {}; + // Pt split per PID hypothesis in tracking + TH1D* mPtNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mPtDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchPtVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + // Phi + TH1F* mPhiNum[matchType::SIZE] = {}; + TH1F* mPhiDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhi[matchType::SIZE] = {}; + TH1F* mPhiPhysPrimNum[matchType::SIZE] = {}; + TH1F* mPhiPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhiPhysPrim[matchType::SIZE] = {}; + TH2F* mPhiVsPtNum[matchType::SIZE] = {}; + TH2F* mPhiVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhiVsPt[matchType::SIZE] = {}; + // Phi split per PID hypothesis in tracking + TH1D* mPhiNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mPhiDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchPhiVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + // Eta + TH1F* mEtaNum[matchType::SIZE] = {}; + TH1F* mEtaDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEta[matchType::SIZE] = {}; + TH1F* mEtaPhysPrimNum[matchType::SIZE] = {}; + TH1F* mEtaPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEtaPhysPrim[matchType::SIZE] = {}; + TH2F* mEtaVsPtNum[matchType::SIZE] = {}; + TH2F* mEtaVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEtaVsPt[matchType::SIZE] = {}; + // Clusters + TH2F* mClsVsPtNum[matchType::SIZE] = {}; + TH2F* mClsVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchClsVsPt[matchType::SIZE] = {}; + // Chi2 + TH2F* mChi2VsPtNum[matchType::SIZE] = {}; + TH2F* mChi2VsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchChi2VsPt[matchType::SIZE] = {}; + // Eta split per PID hypothesis in tracking + TH1D* mEtaNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mEtaDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchEtaVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + // Residuals + TH2F* mResidualPt = nullptr; + TH2F* mResidualPhi = nullptr; + TH2F* mResidualEta = nullptr; + // Others + TH1F* mChi2Matching = nullptr; + TH1F* mChi2Refit = nullptr; + TH2F* mTimeResVsPt = nullptr; + TH1F* mDCAr = nullptr; + TH2F* mDCArVsPtNum = nullptr; + TH2F* mDCArVsPtDen = nullptr; + TEfficiency* mFractionITSTPCmatchDCArVsPt = nullptr; + // 1/Pt + TH1D* m1OverPtNum[matchType::SIZE] = {}; + TH1D* m1OverPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch1OverPt[matchType::SIZE] = {}; + TH1D* m1OverPtPhysPrimNum[matchType::SIZE] = {}; + TH1D* m1OverPtPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhysPrim1OverPt[matchType::SIZE] = {}; + // 3D Efficiency in eta/phi/pt + TH3F* mEtaPhiPtNum[matchType::SIZE] = {}; + TH3F* mEtaPhiPtDen[matchType::SIZE] = {}; + + template + void setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden); + + int mNTPCSelectedTracks = 0; + int mNITSSelectedTracks = 0; + int mNITSTPCSelectedTracks[matchType::SIZE] = {0, 0}; + + // cut values + // ITS track + float mPtITSCut = 0.1f; // min pT for ITS track + float mEtaITSCut = 1.4f; // eta window for ITS track --> TODO: define 2 different values for min and max (**) + int mMinNClustersITS = 0; // min number of ITS clusters + float mMaxChi2PerClusterITS{1e10f}; // max its fit chi2 per ITS cluster + std::vector>> mRequiredITSHits; // vector of ITS requirements (minNRequiredHits in specific requiredLayers) + // TPC track + float mPtTPCCut = 0.1f; // min pT for TPC track + float mEtaTPCCut = 1.4f; // eta window for TPC track --> TODO: define 2 different values for min and max (***) + int32_t mNTPCClustersCut = 60; // minimum number of TPC clusters for TPC track + float mDCATPCCut = 100.f; // max DCA 3D to PV for TPC track + float mDCATPCCutY = 10.f; // max DCA xy to PV for TPC track + // ITS-TPC kinematics + int mPtBins = 100; + float mPtCut = 0.1f; + float mPtMaxCut = 20; + float mEtaCut = 1.4f; + float mEtaNo0Cut = 0.05f; + // TODO: define 2 different values for min and max (*) + + // for V0s + o2::vertexing::DCAFitterN<2> mFitterV0; + TH3F* mK0MassVsPtVsOccpp = nullptr; + TH3F* mK0MassVsPtVsOccPbPb = nullptr; + bool mDoK0QC = false; // whether to fill the K0 QC plot(s) + float mCutK0Mass = 0.05; // cut on the difference between the K0 mass and the PDG mass + bool mRefit = false; // whether to refit or not + float mMaxEtaK0 = 0.8; // cut on the K0 eta + long int mTimestamp = -1; // timestamp used to load the SVertexParam object: if differnt from -1, we don't load (it means we already did it) + std::unique_ptr mConfig; + std::unique_ptr mConfParam; + // std::unique_ptr mParam; + std::shared_ptr mParam = nullptr; + int mNHBPerTF = 0; + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + float mNTPCOccBinLengthInv{}; + std::vector mTBinClOcc; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting from the TB = i*mNTPCOccBinLength + gsl::span mTPCRefitterOccMap; ///< externally set TPC clusters occupancy map + bool mIsHI = false; + float mK0Scaling = 1.f; // permill that we want to keep of K0S + uint64_t mNK0 = 0; // number of found V0s + float mMinTPCOccpp = 0.f; // min TPC occupancy for K0s plot for pp collisions + float mMaxTPCOccpp = 1.e6; // max TPC occupancy for K0s plot for pp collisions + int mNBinsTPCOccpp = 6; // number of bins in TPC occupancy for K0s plot for pp collisions + float mMinTPCOccPbPb = 0.f; // min TPC occupancy for K0s plot for PbPb collisions + float mMaxTPCOccPbPb = 8.e6; // max TPC occupancy for K0s plot for PbPb collisions + int mNBinsTPCOccPbPb = 8; // number of bins in TPC occupancy for K0s plot for PbPb collisions + float mK0MaxDCA = 0.01; // max DCA to select the K0 + float mK0MinCosPA = 0.995; // min cosPA to select the K0 + + ClassDefNV(MatchITSTPCQC, 4); +}; +} // namespace gloqc +} // namespace o2 + +#endif diff --git a/Detectors/GLOQC/src/GLOQCLinkDef.h b/Detectors/GLOQC/src/GLOQCLinkDef.h new file mode 100644 index 0000000000000..f7092a4394d08 --- /dev/null +++ b/Detectors/GLOQC/src/GLOQCLinkDef.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::gloqc::ITSTPCMatchingQCParams> + ; +#pragma link C++ class o2::gloqc::ITSTPCMatchingQCParams + ; + +#endif diff --git a/Detectors/GLOQC/src/ITSTPCMatchingQCParams.cxx b/Detectors/GLOQC/src/ITSTPCMatchingQCParams.cxx new file mode 100644 index 0000000000000..b5c9f6842dfc3 --- /dev/null +++ b/Detectors/GLOQC/src/ITSTPCMatchingQCParams.cxx @@ -0,0 +1,16 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ITSTPCMatchingQCParams.h +/// \brief Configurable params for ITSTPC matching QC + +#include "GLOQC/ITSTPCMatchingQCParams.h" +O2ParamImpl(o2::gloqc::ITSTPCMatchingQCParams); diff --git a/Detectors/GLOQC/src/MatchITSTPCQC.cxx b/Detectors/GLOQC/src/MatchITSTPCQC.cxx new file mode 100644 index 0000000000000..e1832056f072c --- /dev/null +++ b/Detectors/GLOQC/src/MatchITSTPCQC.cxx @@ -0,0 +1,1449 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include + +#include "GLOQC/MatchITSTPCQC.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DetectorsBase/Propagator.h" +#include "SimulationDataFormat/MCUtils.h" +#include "GlobalTracking/TrackCuts.h" +#include +#include +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/V0.h" +#include "DetectorsVertexing/SVertexerParams.h" +#include "Framework/InputRecord.h" +#include "Framework/TimingInfo.h" +#include "GPUO2InterfaceUtils.h" +#include "CommonConstants/LHCConstants.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "GPUO2InterfaceRefit.h" + +using namespace o2::gloqc; +using namespace o2::mcutils; +using MCTrack = o2::MCTrackT; +using DetID = o2::detectors::DetID; + +MatchITSTPCQC::~MatchITSTPCQC() +{ + deleteHistograms(); +} + +//_______________________________________________________ + +void MatchITSTPCQC::deleteHistograms() +{ + + LOG(debug) << "Deleting histos..."; + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + delete mPtNum[i]; + delete mPtDen[i]; + delete mFractionITSTPCmatch[i]; + delete mPtNum_noEta0[i]; + delete mPtDen_noEta0[i]; + delete mFractionITSTPCmatch_noEta0[i]; + delete mPtPhysPrimNum[i]; + delete mPtPhysPrimDen[i]; + delete mFractionITSTPCmatchPhysPrim[i]; + + // Phi + delete mPhiNum[i]; + delete mPhiDen[i]; + delete mFractionITSTPCmatchPhi[i]; + delete mPhiPhysPrimNum[i]; + delete mPhiPhysPrimDen[i]; + delete mFractionITSTPCmatchPhiPhysPrim[i]; + delete mPhiVsPtNum[i]; + delete mPhiVsPtDen[i]; + delete mFractionITSTPCmatchPhiVsPt[i]; + + // Eta + delete mEtaNum[i]; + delete mEtaDen[i]; + delete mFractionITSTPCmatchEta[i]; + delete mEtaPhysPrimNum[i]; + delete mEtaPhysPrimDen[i]; + delete mFractionITSTPCmatchEtaPhysPrim[i]; + delete mEtaVsPtNum[i]; + delete mEtaVsPtDen[i]; + delete mFractionITSTPCmatchEtaVsPt[i]; + + // Clusters + delete mClsVsPtNum[i]; + delete mClsVsPtDen[i]; + delete mFractionITSTPCmatchClsVsPt[i]; + + // Chi2 + delete mChi2VsPtNum[i]; + delete mChi2VsPtDen[i]; + delete mFractionITSTPCmatchChi2VsPt[i]; + + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + delete mPtNumVsTrkPID[i][j]; + delete mPtDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchPtVsTrkPID[i][j]; + // Phi + delete mPhiNumVsTrkPID[i][j]; + delete mPhiDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchPhiVsTrkPID[i][j]; + // Eta + delete mEtaNumVsTrkPID[i][j]; + delete mEtaDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchEtaVsTrkPID[i][j]; + } + + // 1/Pt + delete m1OverPtNum[i]; + delete m1OverPtDen[i]; + delete mFractionITSTPCmatch1OverPt[i]; + delete m1OverPtPhysPrimNum[i]; + delete m1OverPtPhysPrimDen[i]; + delete mFractionITSTPCmatchPhysPrim1OverPt[i]; + + // 3D eta/phi/pt + delete mEtaPhiPtNum[i]; + delete mEtaPhiPtDen[i]; + } + + // Residuals + delete mResidualPt; + delete mResidualPhi; + delete mResidualEta; + // Others + delete mChi2Matching; + delete mChi2Refit; + delete mTimeResVsPt; + delete mDCAr; + delete mDCArVsPtNum; + delete mDCArVsPtDen; + delete mFractionITSTPCmatchDCArVsPt; + + // K0 + delete mK0MassVsPtVsOccpp; + delete mK0MassVsPtVsOccPbPb; +} + +//__________________________________________________________ + +void MatchITSTPCQC::reset() +{ + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + mPtNum[i]->Reset(); + mPtDen[i]->Reset(); + mPtNum_noEta0[i]->Reset(); + mPtDen_noEta0[i]->Reset(); + + // Phi + mPhiNum[i]->Reset(); + mPhiDen[i]->Reset(); + mPhiVsPtNum[i]->Reset(); + mPhiVsPtDen[i]->Reset(); + + // Eta + mEtaNum[i]->Reset(); + mEtaDen[i]->Reset(); + mEtaVsPtNum[i]->Reset(); + mEtaVsPtDen[i]->Reset(); + + // Clusters + mClsVsPtNum[i]->Reset(); + mClsVsPtDen[i]->Reset(); + + // Chi2 + mChi2VsPtNum[i]->Reset(); + mChi2VsPtDen[i]->Reset(); + + // 1/Pt + m1OverPtNum[i]->Reset(); + m1OverPtDen[i]->Reset(); + + // 3D eta/phi/pt + mEtaPhiPtNum[i]->Reset(); + mEtaPhiPtDen[i]->Reset(); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + mPtNumVsTrkPID[i][j]->Reset(); + mPtDenVsTrkPID[i][j]->Reset(); + // Phi + mPhiNumVsTrkPID[i][j]->Reset(); + mPhiDenVsTrkPID[i][j]->Reset(); + // Eta + mEtaNumVsTrkPID[i][j]->Reset(); + mEtaDenVsTrkPID[i][j]->Reset(); + } + } + + if (mUseMC) { + mPtPhysPrimNum[i]->Reset(); + mPtPhysPrimDen[i]->Reset(); + + mPhiPhysPrimNum[i]->Reset(); + mPhiPhysPrimDen[i]->Reset(); + + mEtaPhysPrimNum[i]->Reset(); + mEtaPhysPrimDen[i]->Reset(); + + m1OverPtPhysPrimNum[i]->Reset(); + m1OverPtPhysPrimDen[i]->Reset(); + } + } + + // Residuals + mResidualPt->Reset(); + mResidualPhi->Reset(); + mResidualEta->Reset(); + // Others + mChi2Matching->Reset(); + mChi2Refit->Reset(); + mTimeResVsPt->Reset(); + mDCAr->Reset(); + mDCArVsPtNum->Reset(); + mDCArVsPtDen->Reset(); + + // K0 + if (mDoK0QC) { + mK0MassVsPtVsOccpp->Reset(); + mK0MassVsPtVsOccPbPb->Reset(); + } +} + +//__________________________________________________________ +bool MatchITSTPCQC::init() +{ + LOGP(debug, "Creating Variable Binning"); + std::array title{"TPC", "ITS"}; + std::array etaSel{Form(", |#eta| < %.1f", mEtaTPCCut), Form(", |#eta| < %.1f", mEtaCut)}; + std::array maxNCls{156, 7}; + // log binning for pT + const Int_t nbinsPt = mPtBins; + const Double_t xminPt = (mPtCut > 0) ? mPtCut : 0.01; + const Double_t xmaxPt = mPtMaxCut; + Double_t* xbinsPt = new Double_t[nbinsPt + 1]; + Double_t xlogminPt = TMath::Log10(xminPt); + Double_t xlogmaxPt = TMath::Log10(xmaxPt); + Double_t dlogxPt = (xlogmaxPt - xlogminPt) / nbinsPt; + for (int i = 0; i <= nbinsPt; i++) { + Double_t xlogPt = xlogminPt + i * dlogxPt; + xbinsPt[i] = TMath::Exp(TMath::Log(10) * xlogPt); + } + + LOGP(debug, "Creating Histograms"); + // Data and MC + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + mPtNum[i] = new TH1D(Form("mPtNum_%s", title[i].c_str()), Form("Pt distribution of ITSTPC matched tracks, wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + mPtNum[i]->Sumw2(); + mPtNum[i]->SetOption("logy"); + mPtNum[i]->GetYaxis()->SetTitleOffset(1.4); + mPtDen[i] = new TH1D(Form("mPtDen_%s", title[i].c_str()), Form("Pt distribution of %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + mPtDen[i]->Sumw2(); + mPtDen[i]->SetOption("logy"); + mPtDen[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatch[i] = new TEfficiency(Form("mFractionITSTPCmatch_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + mPtNum_noEta0[i] = new TH1D(Form("mPtNum_noEta0_%s", title[i].c_str()), Form("Pt distribution of ITSTPC matched tracks without |eta| < %.2f, wrt %s tracks %s; Pt [GeV/c]; dNdPt", mEtaNo0Cut, title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + mPtNum_noEta0[i]->Sumw2(); + mPtNum_noEta0[i]->SetOption("logy"); + mPtNum_noEta0[i]->GetYaxis()->SetTitleOffset(1.4); + mPtDen_noEta0[i] = new TH1D(Form("mPtDen_noEta0_%s", title[i].c_str()), Form("Pt distribution of %s tracks without |eta| < %.2f %s; Pt [GeV/c]; dNdPt", title[i].c_str(), mEtaNo0Cut, etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + mPtDen_noEta0[i]->Sumw2(); + mPtDen_noEta0[i]->SetOption("logy"); + mPtDen_noEta0[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatch_noEta0[i] = new TEfficiency(Form("mFractionITSTPCmatch_noEta0_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt without |eta| < %.2f %s; Pt [GeV/c]; Eff", title[i].c_str(), mEtaNo0Cut, etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + + // Phi + mPhiNum[i] = new TH1F(Form("mPhiNum_%s", title[i].c_str()), Form("Phi distribution of ITSTPC matched tracks, wrt %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiNum[i]->Sumw2(); + mPhiDen[i] = new TH1F(Form("mPhiDen_%s", title[i].c_str()), Form("Phi distribution of %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiDen[i]->Sumw2(); + mFractionITSTPCmatchPhi[i] = new TEfficiency(Form("mFractionITSTPCmatchPhi_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Phi wrt %s tracks %s; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtNum[i] = new TH2F(Form("mPhiVsPtNum_%s", title[i].c_str()), Form("Phi vs Pt distribution of ITSTPC matched tracks wrt %s %s; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtNum[i]->Sumw2(); + mPhiVsPtDen[i] = new TH2F(Form("mPhiVsPtDen_%s", title[i].c_str()), Form("Phi vs Pt distribution of %s tracks %s; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchPhiVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchPhiVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks %s, Phi vs Pt; #it{p}_{T} [GeV#it{c}]; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, 0.f, 2 * TMath::Pi()); + + // Eta + mEtaNum[i] = new TH1F(Form("mEtaNum_%s", title[i].c_str()), Form("Eta distribution of ITSTPC matched tracks, wrt %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaNum[i]->Sumw2(); + mEtaNum[i]->GetYaxis()->SetTitleOffset(1.4); + mEtaDen[i] = new TH1F(Form("mEtaDen_%s", title[i].c_str()), Form("Eta distribution of %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaDen[i]->Sumw2(); + mEtaDen[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatchEta[i] = new TEfficiency(Form("mFractionITSTPCmatchEta_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks , wrt %s tracks, vs Eta; Eta; Eff", title[i].c_str()), 100, -2.f, 2.f); + mEtaVsPtNum[i] = new TH2F(Form("mEtaVsPtNum_%s", title[i].c_str()), Form("Eta vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; Eta", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, -2.f, 2.f); + mEtaVsPtNum[i]->Sumw2(); + mEtaVsPtDen[i] = new TH2F(Form("mEtaVsPtDen_%s", title[i].c_str()), Form("Eta vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; Eta", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, -2.f, 2.f); + mEtaVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchEtaVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchEtaVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, Eta vs Pt; #it{p}_{T} [GeV#it{c}]; Eta; Eff", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, -2.f, 2.f); + + // Clusters + mClsVsPtNum[i] = new TH2F(Form("mClsVsPtNum_%s", title[i].c_str()), Form("#Clusters vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; #Clusters", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, maxNCls[i], 0, maxNCls[i]); + mClsVsPtNum[i]->Sumw2(); + mClsVsPtDen[i] = new TH2F(Form("mClsVsPtDen_%s", title[i].c_str()), Form("#Clusters vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; #Clusters", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, maxNCls[i], 0, maxNCls[i]); + mClsVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchClsVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchClsVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, #Clusters vs Pt; #it{p}_{T} [GeV#it{c}]; #Clusters; Eff", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, maxNCls[i], 0, maxNCls[i]); + + // Chi2 + mChi2VsPtNum[i] = new TH2F(Form("mChi2VsPtNum_%s", title[i].c_str()), Form("Chi2 vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; Chi2", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 200, 0, 300); + mChi2VsPtNum[i]->Sumw2(); + mChi2VsPtDen[i] = new TH2F(Form("mChi2VsPtDen_%s", title[i].c_str()), Form("Chi2 vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; Chi2", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 200, 0, 300); + mChi2VsPtDen[i]->Sumw2(); + mFractionITSTPCmatchChi2VsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchChi2VsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, Chi2 vs Pt; #it{p}_{T} [GeV#it{c}]; Chi2; Eff", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 200, 0, 300); + + // 1/pt + m1OverPtNum[i] = new TH1D(Form("m1OverPtNum_%s", title[i].c_str()), Form("1/Pt distribution of matched tracks, wrt %s tracks %s; 1/Pt [c/GeV]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtNum[i]->Sumw2(); + m1OverPtDen[i] = new TH1D(Form("m1OverPtDen_%s", title[i].c_str()), Form("1/Pt distribution of %s tracks %s; 1/Pt [c/GeV]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtDen[i]->Sumw2(); + mFractionITSTPCmatch1OverPt[i] = new TEfficiency(Form("mFractionITSTPCmatch1OverPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs 1/Pt, wrt %s tracks %s; 1/Pt [c/GeV]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + + // 3d eta/phi/pt + mEtaPhiPtNum[i] = new TH3F(Form("mEtaPhiPtNum_%s", title[i].c_str()), Form("Numerator #eta vs #varphi vs #it{p}_{T}, wrt %s;#eta %s;#varphi;#it{p}_{T} [GeV#it{c}];Entries", title[i].c_str(), etaSel[i].c_str()), 100, -2., 2., 100, 0., 2 * TMath::Pi(), 100, 0.01, 20.); + mEtaPhiPtNum[i]->Sumw2(); + mEtaPhiPtDen[i] = new TH3F(Form("mEtaPhiPtDen_%s", title[i].c_str()), Form("Denominator #eta vs #varphi vs #it{p}_{T}, wrt %s;#eta %s;#varphi;#it{p}_{T} [GeV#it{c}];Entries", title[i].c_str(), etaSel[i].c_str()), 100, -2., 2., 100, 0., 2 * TMath::Pi(), 100, 0.01, 20.); + mEtaPhiPtDen[i]->Sumw2(); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + mPtNumVsTrkPID[i][j] = new TH1D(Form("mPtNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Pt distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str(), j), mPtBins, mPtCut, mPtMaxCut); + mPtNumVsTrkPID[i][j]->Sumw2(); + mPtDenVsTrkPID[i][j] = new TH1D(Form("mPtDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Pt distribution of %s tracks %s, TrkPID %i; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str(), j), mPtBins, mPtCut, mPtMaxCut); + mPtDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchPtVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchPtVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt %s, TrkPID %i; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str(), j), mPtBins, mPtCut, mPtMaxCut); + + // Phi + mPhiNumVsTrkPID[i][j] = new TH1D(Form("mPhiNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Phi distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + mPhiNumVsTrkPID[i][j]->Sumw2(); + mPhiDenVsTrkPID[i][j] = new TH1D(Form("mPhiDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Phi distribution of %s tracks %s, TrkPID %i; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + mPhiDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchPhiVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchPhiVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Phi %s, TrkPID %i; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + + // Eta + mEtaNumVsTrkPID[i][j] = new TH1D(Form("mEtaNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Eta distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Eta; dNdEta", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + mEtaNumVsTrkPID[i][j]->Sumw2(); + mEtaDenVsTrkPID[i][j] = new TH1D(Form("mEtaDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Eta distribution of %s tracks %s, TrkPID %i; Eta; dNdEta", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + mEtaDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchEtaVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchEtaVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Eta %s, TrkPID %i; Eta; Eff", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + } + } + } + + mResidualPt = new TH2F("mResidualPt", "Residuals of ITS-TPC matching in #it{p}_{T}; #it{p}_{T}^{ITS-TPC} [GeV/c]; #it{p}_{T}^{ITS-TPC} - #it{p}_{T}^{TPC} [GeV/c]", mPtBins, mPtCut, mPtMaxCut, 100, -1.f, 1.f); + mResidualPhi = new TH2F("mResidualPhi", "Residuals of ITS-TPC matching in #it{#phi}; #it{#phi}^{ITS-TPC} [rad]; #it{#phi}^{ITS-TPC} - #it{#phi}^{TPC} [rad]", 100, 0.f, 2 * TMath::Pi(), 100, -1.f, 1.f); + mResidualEta = new TH2F("mResidualEta", "Residuals of ITS-TPC matching in #it{#eta}; #it{#eta}^{ITS-TPC}; #it{#eta}^{ITS-TPC} - #it{#eta}^{TPC}", 100, -2.f, 2.f, 100, -1.f, 1.f); + mChi2Matching = new TH1F("mChi2Matching", "Chi2 of matching; chi2", 200, 0, 300); + mChi2Matching->SetOption("logy"); + mChi2Matching->GetYaxis()->SetTitleOffset(1.4); + mChi2Refit = new TH1F("mChi2Refit", "Chi2 of refit; chi2", 200, 0, 300); + mChi2Refit->SetOption("logy"); + mChi2Refit->GetYaxis()->SetTitleOffset(1.4); + mDCAr = new TH1F("mDCAr", "DCA of TPC tracks; DCAr", 100, -mDCATPCCutY, mDCATPCCutY); + mDCArVsPtNum = new TH2F("mDCArVsPtNum", "DCA of TPC tracks Vs Pt Num; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 100, -mDCATPCCutY, mDCATPCCutY); + mDCArVsPtNum->Sumw2(); + mDCArVsPtDen = new TH2F("mDCArVsPtDen", "DCA of TPC tracks Vs Pt Den; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 100, -mDCATPCCutY, mDCATPCCutY); + mDCArVsPtDen->Sumw2(); + mFractionITSTPCmatchDCArVsPt = new TEfficiency("mFractionITSTPCmatchDCArVsPt", "Fraction of ITSTPC matched tracks wrt TPC vs DCAr; #it{p}_{T} [GeV#it{c}]; DCAr; Eff", 100, 0, 20., 200, -30, 30); + + mTimeResVsPt = new TH2F("mTimeResVsPt", "Time resolution vs Pt; Pt [GeV/c]; time res [us]", nbinsPt, xbinsPt, 100, 0.f, 2.f); + mTimeResVsPt->SetOption("colz logz logy logx"); + mTimeResVsPt->GetYaxis()->SetTitleOffset(1.4); + + if (mUseMC) { + mcReader.initFromDigitContext("collisioncontext.root"); + + for (int i = 0; i < matchType::SIZE; ++i) { + mPtPhysPrimNum[i] = new TH1F(Form("mPtPhysPrimNum_%s", title[i].c_str()), Form("Pt distribution of matched tracks (physical primary), wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + mPtPhysPrimNum[i]->Sumw2(); + mPtPhysPrimDen[i] = new TH1F(Form("mPtPhysPrimDen_%s", title[i].c_str()), Form("Pt distribution of %s tracks (physical primary) %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + mPtPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhiPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchPhiPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Phi (physical primary), wrt %s tracks %s; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + + mEtaPhysPrimNum[i] = new TH1F(Form("mEtaPhysPrimNum_%s", title[i].c_str()), Form("Eta distribution of matched tracks (physical primary), wrt %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaPhysPrimNum[i]->Sumw2(); + mEtaPhysPrimDen[i] = new TH1F(Form("mEtaPhysPrimDen_%s", title[i].c_str()), Form("Eta distribution of %s tracks (physical primary); Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchEtaPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchEtaPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Eta (physical primary), wrt %s tracks; Eta; Eff", title[i].c_str()), 100, -2.f, 2.f); + + mPhiPhysPrimNum[i] = new TH1F(Form("mPhiPhysPrimNum_%s", title[i].c_str()), Form("Phi distribution of matched tracks (physical primary), wrt %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiPhysPrimNum[i]->Sumw2(); + mPhiPhysPrimDen[i] = new TH1F(Form("mPhiPhysPrimDen_%s", title[i].c_str()), Form("Phi distribution of %s tracks (physical primary) %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Pt (physical primary), wrt %s tracks %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + + m1OverPtPhysPrimNum[i] = new TH1D(Form("m1OverPtPhysPrimNum_%s", title[i].c_str()), Form("1/Pt distribution of matched tracks (physical primary), wrt %s tracks %s; 1/Pt [c/GeV]; dNd1/Pt", title[i].c_str(), etaSel[i].c_str()), 2 * mPtBins, -20., 20.); + m1OverPtPhysPrimNum[i]->Sumw2(); + m1OverPtPhysPrimDen[i] = new TH1D(Form("m1OverPtPhysPrimDen_%s", title[i].c_str()), Form("1/PtPt distribution of %s tracks (physical primary) %s; 1/Pt [c/GeV]; dNd1/Pt", title[i].c_str(), etaSel[i].c_str()), 2 * mPtBins, -20., 20.); + m1OverPtPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhysPrim1OverPt[i] = new TEfficiency(Form("mFractionITSTPCmatchPhysPrim1OverPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs 1/Pt (physical primary), wrt %s tracks %s; 1/Pt [c/GeV]; Eff", title[i].c_str(), etaSel[i].c_str()), 2 * mPtBins, -20., 20.); + } + } + + // log binning for pT for K0s + const Int_t nbinsPtK0 = 10; + const Double_t xminPtK0 = 0.01; + const Double_t xmaxPtK0 = 20; + Double_t* xbinsPtK0 = new Double_t[nbinsPtK0 + 1]; + Double_t xlogminPtK0 = TMath::Log10(xminPtK0); + Double_t xlogmaxPtK0 = TMath::Log10(xmaxPtK0); + Double_t dlogxPtK0 = (xlogmaxPtK0 - xlogminPtK0) / nbinsPtK0; + for (int i = 0; i <= nbinsPtK0; i++) { + Double_t xlogPtK0 = xlogminPtK0 + i * dlogxPtK0; + xbinsPtK0[i] = TMath::Exp(TMath::Log(10) * xlogPtK0); + } + // the other bins + const Int_t nbinsMassK0 = 100; + Double_t* ybinsMassK0 = new Double_t[nbinsMassK0 + 1]; + Double_t yminMassK0 = 0.4; + Double_t ymaxMassK0 = 0.6; + Double_t dyMassK0 = (ymaxMassK0 - yminMassK0) / nbinsMassK0; + for (int i = 0; i <= nbinsMassK0; i++) { + ybinsMassK0[i] = yminMassK0 + i * dyMassK0; + } + const Int_t nbinsMultK0pp = mNBinsTPCOccpp; + Double_t* zbinsMultK0pp = new Double_t[nbinsMultK0pp + 1]; + Double_t zminMultK0pp = mMinTPCOccpp; + Double_t zmaxMultK0pp = mMaxTPCOccpp; + Double_t dzMultK0pp = (zmaxMultK0pp - zminMultK0pp) / nbinsMultK0pp; + for (int i = 0; i <= nbinsMultK0pp; i++) { + zbinsMultK0pp[i] = zminMultK0pp + i * dzMultK0pp; + } + + const Int_t nbinsMultK0PbPb = mNBinsTPCOccPbPb; + Double_t* zbinsMultK0PbPb = new Double_t[nbinsMultK0PbPb + 1]; + Double_t zminMultK0PbPb = mMinTPCOccPbPb; + Double_t zmaxMultK0PbPb = mMaxTPCOccPbPb; + Double_t dzMultK0PbPb = (zmaxMultK0PbPb - zminMultK0PbPb) / nbinsMultK0PbPb; + for (int i = 0; i <= nbinsMultK0PbPb; i++) { + zbinsMultK0PbPb[i] = zminMultK0PbPb + i * dzMultK0PbPb; + } + + if (mDoK0QC) { + // V0s + mK0MassVsPtVsOccpp = new TH3F("mK0MassVsPtVsOccpp", "K0 invariant mass vs Pt vs TPC occupancy; Pt [GeV/c]; K0s mass [GeV/c^2]; TPC occ.", nbinsPtK0, xbinsPtK0, nbinsMassK0, ybinsMassK0, nbinsMultK0pp, zbinsMultK0pp); + + mK0MassVsPtVsOccPbPb = new TH3F("mK0MassVsPtVsOccPbPb", "K0 invariant mass vs Pt vs TPC occupancy; Pt [GeV/c]; K0s mass [GeV/c^2]; TPC occ", nbinsPtK0, xbinsPtK0, nbinsMassK0, ybinsMassK0, nbinsMultK0PbPb, zbinsMultK0PbPb); + } + + LOG(info) << "Printing configuration cuts"; + printParams(); + + delete[] xbinsPt; + delete[] xbinsPtK0; + delete[] ybinsMassK0; + delete[] zbinsMultK0pp; + delete[] zbinsMultK0PbPb; + + return true; +} + +//__________________________________________________________ + +void MatchITSTPCQC::initDataRequest() +{ + + // initialize data request, if it was not already done + + mSrc &= mAllowedSources; + + if (!mSrc[GID::Source::ITSTPC] || !mSrc[GID::Source::TPC] || !mSrc[GID::Source::ITS]) { + LOG(fatal) << "We cannot do ITSTPC QC, some sources are missing, check sources in " << mSrc; + } + + mDataRequest = std::make_shared(); + mDataRequest->requestTracks(mSrc, mUseMC); + if (mDoK0QC) { + mDataRequest->requestPrimaryVertices(mUseMC); + mDataRequest->requestSecondaryVertices(mUseMC); + mDataRequest->requestTPCOccMap(); + } +} + +//__________________________________________________________ + +void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) +{ + // Getting the B field + mBz = o2::base::Propagator::Instance()->getNominalBz(); + + // Getting the SVertexer config params + if (mTimestamp == -1 && mDoK0QC) { + // we have not yet initialized the SVertexer params; let's do it + ctx.inputs().get("SVParam"); + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + mFitterV0.setUseAbsDCA(svparam.useAbsDCA); + mFitterV0.setMaxR(svparam.maxRIni); + mFitterV0.setMinParamChange(svparam.minParamChange); + mFitterV0.setMinRelChi2Change(svparam.minRelChi2Change); + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setMatCorrType(o2::base::Propagator::MatCorrType(svparam.matCorr)); + mFitterV0.setUsePropagator(svparam.usePropagator); + mFitterV0.setRefitWithMatCorr(svparam.refitWithMatCorr); + mFitterV0.setMaxStep(svparam.maxStep); + mFitterV0.setMaxSnp(svparam.maxSnp); + mFitterV0.setMinXSeed(svparam.minXSeed); + + mTimestamp = ctx.services().get().creation; + auto grplhcif = o2::base::GRPGeomHelper::instance().getGRPLHCIF(); + if (grplhcif->getBeamZ(0) != 1 || grplhcif->getBeamZ(1) != 1) { + LOG(info) << "We are in Heavy Ion: Z for beam 0 = " << grplhcif->getBeamZ(0) << " ; Z for beam 1 = " << grplhcif->getBeamZ(1); + mIsHI = true; + } else { + LOG(info) << "We are not in Heavy Ion: Z for beam 0 = " << grplhcif->getBeamZ(0) << " ; Z for beam 1 = " << grplhcif->getBeamZ(1); + } + } + + static int evCount = 0; + mRecoCont.collectData(ctx, *mDataRequest); + mTPCTracks = mRecoCont.getTPCTracks(); + mITSTracks = mRecoCont.getITSTracks(); + mITSTPCTracks = mRecoCont.getTPCITSTracks(); + + LOG(info) << "****** Number of found ITSTPC tracks = " << mITSTPCTracks.size(); + LOG(info) << "****** Number of found TPC tracks = " << mTPCTracks.size(); + LOG(info) << "****** Number of found ITS tracks = " << mITSTracks.size(); + + // cache selection for TPC and ITS tracks + std::vector isTPCTrackSelectedEntry(mTPCTracks.size(), false); + std::vector isITSTrackSelectedEntry(mITSTracks.size(), false); + TrackCuts cuts; + // ITS track + cuts.setMinPtITSCut(mPtITSCut); + cuts.setEtaITSCut(mEtaITSCut); + cuts.setMinNClustersITS(mMinNClustersITS); + cuts.setMaxChi2PerClusterITS(mMaxChi2PerClusterITS); + for (auto it = mRequiredITSHits.begin(); it != mRequiredITSHits.end(); it++) { + cuts.setRequireHitsInITSLayers((*it).first, (*it).second); + } + // TPC track + cuts.setMinPtTPCCut(mPtTPCCut); + cuts.setEtaTPCCut(mEtaTPCCut); + cuts.setMinNTPCClustersCut(mNTPCClustersCut); + cuts.setMaxDCATPCCut(mDCATPCCut); + cuts.setMaxDCATPCCutY(mDCATPCCutY); + // ITS-TPC track kinematics + cuts.setMinPtCut(mPtCut); + cuts.setMaxPtCut(mPtMaxCut); + cuts.setEtaCut(-mEtaCut, mEtaCut); + + for (size_t itrk = 0; itrk < mTPCTracks.size(); ++itrk) { + auto const& trkTpc = mTPCTracks[itrk]; + o2::dataformats::GlobalTrackID id(itrk, GID::TPC); + if (cuts.isSelected(id, mRecoCont)) { + // NB: same cuts for numerator and denominator tracks of ITS-TPC matching + // To change cuts only for numerator, something like o2::dataformats::GlobalTrackID id(itrk, GID::ITSTPC) is necessary + isTPCTrackSelectedEntry[itrk] = true; + } + } + + for (size_t itrk = 0; itrk < mITSTracks.size(); ++itrk) { + auto const& trkIts = mITSTracks[itrk]; + o2::dataformats::GlobalTrackID id(itrk, GID::ITS); + if (cuts.isSelected(id, mRecoCont)) { + // NB: same cuts for numerator and denominator tracks of ITS-TPC matching + // To change cuts only for numerator, something like o2::dataformats::GlobalTrackID id(itrk, GID::ITSTPC) is necessary + isITSTrackSelectedEntry[itrk] = true; + } + } + + // numerator + eta, chi2... + if (mUseMC) { + for (int i = 0; i < matchType::SIZE; ++i) { + mMapLabels[i].clear(); + } + for (int itrk = 0; itrk < static_cast(mITSTPCTracks.size()); ++itrk) { + auto const& trk = mITSTPCTracks[itrk]; + auto idxTrkTpc = trk.getRefTPC().getIndex(); + if (trk.getRefITS().getSource() != GID::ITS) { + continue; + } + if (isTPCTrackSelectedEntry[idxTrkTpc]) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::TPC].find(lbl) == mMapLabels[matchType::TPC].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapLabels[matchType::TPC].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = true}}); + } else { + mMapLabels[matchType::TPC].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = false}}); + } + } else { + // winner (if more tracks have the same label) has the highest pt + if (mITSTPCTracks[mMapLabels[matchType::TPC].at(lbl).mIdx].getPt() < trk.getPt()) { + mMapLabels[matchType::TPC].at(lbl).mIdx = itrk; + } + } + } + auto idxTrkIts = trk.getRefITS().getIndex(); + if (isITSTrackSelectedEntry[idxTrkIts]) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::ITS].find(lbl) == mMapLabels[matchType::ITS].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapLabels[matchType::ITS].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = true}}); + } else { + mMapLabels[matchType::ITS].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = false}}); + } + } else { + // winner (if more tracks have the same label) has the highest pt + if (mITSTPCTracks[mMapLabels[matchType::ITS].at(lbl).mIdx].getPt() < trk.getPt()) { + mMapLabels[matchType::ITS].at(lbl).mIdx = itrk; + } + } + } + } + LOG(debug) << "number of entries in map for nominator (without duplicates) = " << mMapLabels.size(); + // now we use only the tracks in the map to fill the histograms (--> tracks have passed the + // track selection and there are no duplicated tracks wrt the same MC label) + for (int i = 0; i < matchType::SIZE; ++i) { + for (auto const& el : mMapLabels[i]) { + auto const& trk = mITSTPCTracks[el.second.mIdx]; + o2::track::TrackParCov trkDen; + bool isEtaITSOk = true; + if (i == matchType::TPC) { + trkDen = mTPCTracks[trk.getRefTPC()]; + } else { + trkDen = mITSTracks[trk.getRefITS()]; + if (std::abs(trkDen.getEta()) > mEtaITSCut) { + // ITS track outside |eta | < 0.9, we don't fill pt, nor phi , nor phi vs pt histos + isEtaITSOk = false; + } + } + if (isEtaITSOk) { + mPtNum[i]->Fill(trkDen.getPt()); + if (std::abs(trkDen.getEta()) > mEtaNo0Cut) { + mPtNum_noEta0[i]->Fill(trkDen.getPt()); + } + mPhiNum[i]->Fill(trkDen.getPhi()); + mPhiVsPtNum[i]->Fill(trkDen.getPt(), trkDen.getPhi()); + m1OverPtNum[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + mEtaPhiPtNum[i]->Fill(trkDen.getEta(), trkDen.getPhi(), trkDen.getPt()); + // we fill also the denominator + mPtDen[i]->Fill(trkDen.getPt()); + if (std::abs(trkDen.getEta()) > mEtaNo0Cut) { + mPtDen_noEta0[i]->Fill(trkDen.getPt()); + } + mPhiDen[i]->Fill(trkDen.getPhi()); + mPhiVsPtDen[i]->Fill(trkDen.getPt(), trkDen.getPhi()); + m1OverPtDen[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + mEtaPhiPtDen[i]->Fill(trkDen.getEta(), trkDen.getPhi(), trkDen.getPt()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mPtNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPt()); + mPhiNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPhi()); + // we fill also the denominator + mPtDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPt()); + mPhiDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPhi()); + } + } + mEtaNum[i]->Fill(trkDen.getEta()); + mEtaVsPtNum[i]->Fill(trkDen.getPt(), trkDen.getEta()); + // we fill also the denominator + mEtaDen[i]->Fill(trkDen.getEta()); + mEtaVsPtDen[i]->Fill(trkDen.getPt(), trkDen.getEta()); + if (i == matchType::TPC) { + auto tpcTrk = mTPCTracks[trk.getRefTPC()]; + mClsVsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + mClsVsPtDen[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtDen[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (tpcTrk.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtNum->Fill(tpcTrk.getPt(), dca[0]); + mDCArVsPtDen->Fill(tpcTrk.getPt(), dca[0]); + } + } else { + const auto& itsTrk = mITSTracks[trk.getRefITS()]; + mClsVsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + mClsVsPtDen[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtDen[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + } + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mEtaNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getEta()); + // we fill also the denominator + mEtaDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getEta()); + } + if (el.second.mIsPhysicalPrimary) { + if (isEtaITSOk) { + mPtPhysPrimNum[i]->Fill(trkDen.getPt()); + mPhiPhysPrimNum[i]->Fill(trkDen.getPhi()); + m1OverPtPhysPrimNum[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + // we fill also the denominator + mPtPhysPrimDen[i]->Fill(trkDen.getPt()); + mPhiPhysPrimDen[i]->Fill(trkDen.getPhi()); + m1OverPtPhysPrimDen[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + } + mEtaPhysPrimNum[i]->Fill(trkDen.getEta()); + // we fill also the denominator + mEtaPhysPrimDen[i]->Fill(trkDen.getEta()); + } + ++mNITSTPCSelectedTracks[i]; + } + } + } + int iITSTPC = 0; + for (auto const& trk : mITSTPCTracks) { + if (trk.getRefTPC().getIndex() >= mTPCTracks.size()) { + LOG(fatal) << "******************** ATTENTION! for TPC track associated to matched track: idx = " << trk.getRefTPC().getIndex() << ", size of container = " << mTPCTracks.size() << " in TF " << evCount; + } + std::array title{"TPC", "ITS"}; + for (int i = 0; i < matchType::SIZE; ++i) { + o2::track::TrackParCov trkRef; + unsigned int idxTrkRef{0}; + bool fillHisto = false; + bool isEtaITSOk = true; + if (i == matchType::TPC) { + trkRef = mTPCTracks[trk.getRefTPC()]; + idxTrkRef = trk.getRefTPC().getIndex(); + if (isTPCTrackSelectedEntry[idxTrkRef]) { + fillHisto = true; + ++mNITSTPCSelectedTracks[i]; + } + } else { + idxTrkRef = trk.getRefITS().getIndex(); + if (trk.getRefITS().getSource() == GID::ITSAB) { + // do not use afterburner tracks + LOG(debug) << "Track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " is from afterburner"; + continue; + } + if (idxTrkRef >= mITSTracks.size()) { + LOG(fatal) << "******************** ATTENTION! for ITS track associated to matched track (NOT from AB): idx = " << trk.getRefITS().getIndex() << ", size of container = " << mITSTracks.size() << " in TF " << evCount; + } + trkRef = mITSTracks[trk.getRefITS()]; + LOG(debug) << "Checking track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " and pt = " << trkRef.getPt(); + if (isITSTrackSelectedEntry[idxTrkRef]) { + LOG(debug) << "Track was selected (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " , we keep it in the numerator, pt = " << trkRef.getPt(); + fillHisto = true; + ++mNITSTPCSelectedTracks[i]; + } else { + LOG(debug) << "Track was not selected (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " , we don't keep it in the numerator, pt = " << trkRef.getPt(); + } + if (std::abs(trkRef.getEta()) > mEtaITSCut) { + // ITS track outside |eta | < 0.9, we don't fill pt, nor phi , nor phi vs pt histos + isEtaITSOk = false; + LOG(debug) << "Track (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " will be discarded when filling pt of phi related histograms, since eta = " << trkRef.getEta() << " , we don't keep it in the numerator, pt = " << trkRef.getPt(); + } + } + if (fillHisto) { + if (!mUseMC) { + LOG(debug) << "Filling num (" << title[i] << ") with track with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " with pt = " << trkRef.getPt(); + if (isEtaITSOk) { + mPtNum[i]->Fill(trkRef.getPt()); + if (std::abs(trkRef.getEta()) > mEtaNo0Cut) { + mPtNum_noEta0[i]->Fill(trkRef.getPt()); + } + mPhiNum[i]->Fill(trkRef.getPhi()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mPtNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getPt()); + mPhiNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getPhi()); + } + mPhiVsPtNum[i]->Fill(trkRef.getPt(), trkRef.getPhi()); + m1OverPtNum[i]->Fill(trkRef.getSign() * trkRef.getPtInv()); + mEtaPhiPtNum[i]->Fill(trkRef.getEta(), trkRef.getPhi(), trkRef.getPt()); + } + mEtaNum[i]->Fill(trkRef.getEta()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mEtaNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getEta()); + } + mEtaVsPtNum[i]->Fill(trkRef.getPt(), trkRef.getEta()); + if (i == matchType::TPC) { + const auto& tpcTrk = mTPCTracks[trk.getRefTPC()]; + mClsVsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + } else { + const auto& itsTrk = mITSTracks[trk.getRefITS()]; + mClsVsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + } + } + if (i == matchType::TPC) { + mResidualPt->Fill(trk.getPt(), trk.getPt() - trkRef.getPt()); + mResidualPhi->Fill(trk.getPhi(), trk.getPhi() - trkRef.getPhi()); + mResidualEta->Fill(trk.getEta(), trk.getEta() - trkRef.getEta()); + mChi2Matching->Fill(trk.getChi2Match()); + mChi2Refit->Fill(trk.getChi2Refit()); + mTimeResVsPt->Fill(trkRef.getPt(), trk.getTimeMUS().getTimeStampError()); + math_utils::Point3D v{}; + std::array dca{-999, -999}; + if (trkRef.propagateParamToDCA(v, mBz, &dca)) { + mDCAr->Fill(dca[0]); + if (!mUseMC) { + mDCArVsPtNum->Fill(trkRef.getPt(), dca[0]); + } + } + LOG(debug) << "*** chi2Matching = " << trk.getChi2Match() << ", chi2refit = " << trk.getChi2Refit() << ", timeResolution = " << trk.getTimeMUS().getTimeStampError(); + } + } else { + LOG(debug) << "Not filling num (" << title[i] << ") for ITSTPC track " << iITSTPC << " for track with pt " << trkRef.getPt(); + } + } + ++iITSTPC; + } + + // now filling the denominator for the efficiency calculation + if (mUseMC) { + for (int i = 0; i < matchType::SIZE; ++i) { + mMapRefLabels[i].clear(); + } + // filling the map where we store for each MC label, the track id of the reconstructed + // track with the highest number of TPC clusters + for (int itrk = 0; itrk < static_cast(mTPCTracks.size()); ++itrk) { + auto const& trk = mTPCTracks[itrk]; + if (isTPCTrackSelectedEntry[itrk]) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::TPC}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::TPC].find(lbl) != mMapLabels[matchType::TPC].end()) { + // the track was already added to the denominator + continue; + } + if (mMapRefLabels[matchType::TPC].find(lbl) == mMapRefLabels[matchType::TPC].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapRefLabels[matchType::TPC].insert({lbl, {itrk, true}}); + } else { + mMapRefLabels[matchType::TPC].insert({lbl, {itrk, false}}); + } + } else { + // winner (if more tracks have the same label) has the highest number of TPC clusters + if (mTPCTracks[mMapRefLabels[matchType::TPC].at(lbl).mIdx].getNClusters() < trk.getNClusters()) { + mMapRefLabels[matchType::TPC].at(lbl).mIdx = itrk; + } + } + } + } + // same for ITS + // filling the map where we store for each MC label, the track id of the reconstructed + // track with the highest number of ITS clusters + for (int itrk = 0; itrk < static_cast(mITSTracks.size()); ++itrk) { + auto const& trk = mITSTracks[itrk]; + if (isITSTrackSelectedEntry[itrk]) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITS}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::ITS].find(lbl) != mMapLabels[matchType::ITS].end()) { + // the track was already added to the denominator + continue; + } + if (mMapRefLabels[matchType::ITS].find(lbl) == mMapRefLabels[matchType::ITS].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapRefLabels[matchType::ITS].insert({lbl, {itrk, true}}); + } else { + mMapRefLabels[matchType::ITS].insert({lbl, {itrk, false}}); + } + } else { + // winner (if more tracks have the same label) has the highest number of ITS clusters + if (mITSTracks[mMapRefLabels[matchType::ITS].at(lbl).mIdx].getNClusters() < trk.getNClusters()) { + mMapRefLabels[matchType::ITS].at(lbl).mIdx = itrk; + } + } + } + } + LOG(debug) << "number of entries in map for denominator of TPC tracks (without duplicates) = " << mMapRefLabels[matchType::TPC].size() + mMapLabels[matchType::TPC].size(); + LOG(debug) << "number of entries in map for denominator of ITS tracks (without duplicates) = " << mMapRefLabels[matchType::ITS].size() + mMapLabels[matchType::ITS].size(); + // now we use only the tracks in the map to fill the histograms (--> tracks have passed the + // track selection and there are no duplicated tracks wrt the same MC label) + for (auto const& el : mMapRefLabels[matchType::TPC]) { + auto const& trk = mTPCTracks[el.second.mIdx]; + mPtDen[matchType::TPC]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > mEtaNo0Cut) { + mPtDen_noEta0[matchType::TPC]->Fill(trk.getPt()); + } + mPhiDen[matchType::TPC]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getPhi()); + mEtaPhiPtDen[matchType::TPC]->Fill(trk.getEta(), trk.getPhi(), trk.getPt()); + mEtaDen[matchType::TPC]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getEta()); + m1OverPtDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); + mClsVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (auto trc = trk; trc.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtDen->Fill(trc.getPt(), dca[0]); + } + if (el.second.mIsPhysicalPrimary) { + mPtPhysPrimDen[matchType::TPC]->Fill(trk.getPt()); + mPhiPhysPrimDen[matchType::TPC]->Fill(trk.getPhi()); + mEtaPhysPrimDen[matchType::TPC]->Fill(trk.getEta()); + m1OverPtPhysPrimDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); + } + ++mNTPCSelectedTracks; + } + for (auto const& el : mMapRefLabels[matchType::ITS]) { + auto const& trk = mITSTracks[el.second.mIdx]; + if (std::abs(trk.getEta()) < mEtaITSCut) { + mPtDen[matchType::ITS]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > mEtaNo0Cut) { + mPtDen_noEta0[matchType::ITS]->Fill(trk.getPt()); + } + mPhiDen[matchType::ITS]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getPhi()); + mEtaPhiPtDen[matchType::ITS]->Fill(trk.getEta(), trk.getPhi(), trk.getPt()); + m1OverPtDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } + mEtaDen[matchType::ITS]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getEta()); + mClsVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getChi2()); + if (el.second.mIsPhysicalPrimary) { + if (std::abs(trk.getEta()) < mEtaITSCut) { + mPtPhysPrimDen[matchType::ITS]->Fill(trk.getPt()); + mPhiPhysPrimDen[matchType::ITS]->Fill(trk.getPhi()); + m1OverPtPhysPrimDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } + mEtaPhysPrimDen[matchType::ITS]->Fill(trk.getEta()); + } + ++mNITSSelectedTracks; + } + } else { + // if we are in data, we loop over all tracks (no check on the label) + for (size_t itrk = 0; itrk < mTPCTracks.size(); ++itrk) { + auto const& trk = mTPCTracks[itrk]; + if (isTPCTrackSelectedEntry[itrk]) { + LOG(debug) << "Filling den (TPC) with track with pt = " << trk.getPt(); + mPtDen[matchType::TPC]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > mEtaNo0Cut) { + mPtDen_noEta0[matchType::TPC]->Fill(trk.getPt()); + } else { + LOG(debug) << "Track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta() << " not used for den pt, phi, phi vs pt, 1.pt histos"; + } + mPhiDen[matchType::TPC]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getPhi()); + mEtaPhiPtDen[matchType::TPC]->Fill(trk.getEta(), trk.getPhi(), trk.getPt()); + mEtaDen[matchType::TPC]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getEta()); + m1OverPtDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); + mClsVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (auto trc = trk; trc.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtDen->Fill(trc.getPt(), dca[0]); + } + ++mNTPCSelectedTracks; + } + } + for (size_t itrk = 0; itrk < mITSTracks.size(); ++itrk) { + auto const& trk = mITSTracks[itrk]; + LOG(debug) << "Checking den for track (ITS) " << itrk << " with pt " << trk.getPt() << " and eta = " << trk.getEta(); + if (isITSTrackSelectedEntry[itrk]) { + if (std::abs(trk.getEta()) < mEtaITSCut) { + LOG(debug) << "Filling den for track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta(); + mPtDen[matchType::ITS]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > mEtaNo0Cut) { + mPtDen_noEta0[matchType::ITS]->Fill(trk.getPt()); + } + mPhiDen[matchType::ITS]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getPhi()); + mEtaPhiPtDen[matchType::ITS]->Fill(trk.getEta(), trk.getPhi(), trk.getPt()); + m1OverPtDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } else { + LOG(debug) << "Track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta() << " not used for num pt, phi, phi vs pt, 1.pt histos"; + } + mEtaDen[matchType::ITS]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getEta()); + mClsVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getChi2()); + ++mNITSSelectedTracks; + } else { + LOG(debug) << "Not filling for this track (ITS) " << itrk << " with pt = " << trk.getPt(); + } + } + } + + if (mDoK0QC && mRecoCont.getPrimaryVertices().size() > 0) { + // now doing K0S + mFitterV0.setBz(mBz); + const auto pvertices = mRecoCont.getPrimaryVertices(); + LOG(info) << "****** Number of PVs = " << pvertices.size(); + + // getting occupancy estimator + mNHBPerTF = o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + if (!mParam) { + // for occupancy estimator + mParam = o2::gpu::GPUO2InterfaceUtils::getFullParamShared(0.f, mNHBPerTF); + } + size_t occupancyMapSizeBytes = o2::gpu::GPUO2InterfaceRefit::fillOccupancyMapGetSize(mNHBPerTF, mParam.get()); + LOG(debug) << "occupancyMapSizeBytes = " << occupancyMapSizeBytes; + mTPCRefitterOccMap = mRecoCont.occupancyMapTPC; + o2::gpu::GPUO2InterfaceUtils::paramUseExternalOccupancyMap(mParam.get(), mNHBPerTF, mTPCRefitterOccMap.data(), occupancyMapSizeBytes); + + std::vector mTBinClOcc; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting from the TB = i * mNTPCOccBinLength + mTBinClOcc.clear(); + int mNTPCOccBinLength = mParam->rec.tpc.occupancyMapTimeBins; + LOG(debug) << "mNTPCOccBinLength = " << mNTPCOccBinLength; + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + if (mNTPCOccBinLength > 1 && mTPCRefitterOccMap.size()) { + int nTPCBinsInTF = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8; // number of TPC time bins in 1 TF, considering that 1 TPC time bin is 8 bunches + int ninteg = 0; + int nTPCOccBinsInTF = nTPCBinsInTF * mNTPCOccBinLengthInv; // how many occupancy bins in 1 TF; mNTPCOccBinLengthInv is the inverse of the length of an occupancy bin + int sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); // we will integrate occupancy at max for this number of bins: the max between 1 and the number of occupancy bins in 1 orbit + LOG(debug) << "number of TPC TB in 1 TF = nTPCBinsInTF = " << nTPCBinsInTF << " ; number of occupancy bins in 1 TF = nTPCOccBinsInTF = " << nTPCOccBinsInTF; + LOG(debug) << "bins to integrate = sumBins = " << sumBins; + mTBinClOcc.resize(nTPCOccBinsInTF); + std::vector mltHistTB(nTPCOccBinsInTF); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + bool foundNotZero = false; + for (int i = 0; i < nTPCOccBinsInTF; i++) { // for every occupancy bin in the TF + mltHistTB[i] = mParam->GetUnscaledMult(tb); + if (mParam->GetUnscaledMult(tb) != 0) { + LOG(debug) << "i = " << i << " tb = " << tb << " mltHistTB[" << i << "] = " << mltHistTB[i]; + foundNotZero = true; + } + tb += mNTPCOccBinLength; + } + if (!foundNotZero) { + LOG(debug) << "No mult bin was found different from 0!"; + } + foundNotZero = false; + // now we fill the occupancy map; we integrate the sumBins after the current one, but when we are at the last 27 bins of the TF, where we integrate what we have left till the end of the TF; for practical reasons, we start from the end, adding all the time, and then also removing the last bin, when we have enough, so that we always add together sumBins bins (except, as said, for the last part of the TF) + for (int i = nTPCOccBinsInTF; i--;) { + if (mltHistTB[i] != 0) { + foundNotZero = true; + } + LOG(debug) << "i = " << i << " sm before = " << sm; + sm += mltHistTB[i]; + LOG(debug) << "i = " << i << " sm after = " << sm; + if (i + sumBins < nTPCOccBinsInTF) { + LOG(debug) << "i = " << i << " sumBins = " << sumBins << " nTPCOccBinsInTF = " << nTPCOccBinsInTF << " we have to decrease sm by = " << mltHistTB[i + sumBins]; + sm -= mltHistTB[i + sumBins]; + LOG(debug) << "i = " << i << " sm after 2 = " << sm; + } + mTBinClOcc[i] = sm; + LOG(debug) << "i = " << i << " mTBinClOcc[" << i << "] = " << mTBinClOcc[i]; + } + if (!foundNotZero) { + LOG(debug) << "No mult bin was found different from 0! sm = " << sm; + } + } else { + mTBinClOcc.resize(1); + } + auto v0IDs = mRecoCont.getV0sIdx(); + auto nv0 = v0IDs.size(); + if (nv0 > mRecoCont.getV0s().size()) { + mRefit = true; + } + LOG(debug) << "Found " << mRecoCont.getV0s().size() << " V0s in reco container"; + LOG(debug) << "Found " << nv0 << " V0s ids"; + // associating sec vtxs to prim vtx + std::map> pv2sv; + static int tfID = 0; + for (int iv = 0; iv < nv0; iv++) { + const auto v0id = v0IDs[iv]; + o2::dataformats::GlobalTrackID id0 = v0id.getProngID(0), id1 = v0id.getProngID(1); + if (id0.getSourceDetectorsMask()[DetID::ITS] && id1.getSourceDetectorsMask()[DetID::ITS] && + mRecoCont.isTrackSourceLoaded(id0.getSource()) && mRecoCont.isTrackSourceLoaded(id1.getSource())) { // taking only tracks for which there is the ITS in the track sources + pv2sv[v0id.getVertexID()].push_back(iv); + } else { + LOG(debug) << "Skipping this V0: the track sources do not contain ITS"; + } + } + int nV0sOk = 0; + // processing every sec vtx for each prim vtx + int myCount = 0; + for (auto it : pv2sv) { + int pvID = it.first; + auto& vv = it.second; + if (pvID < 0 || vv.size() == 0) { + continue; + } + const auto& pv = mRecoCont.getPrimaryVertex(pvID); + float pvTime = pv.getTimeStamp().getTimeStamp(); // in \mus + for (int iv0 : vv) { + nV0sOk += processV0(iv0, mRecoCont, mTBinClOcc, pvTime) ? 1 : 0; + } + ++myCount; + } + + LOG(debug) << "Processed " << nV0sOk << " V0s"; + } + evCount++; +} + +//__________________________________________________________ +bool MatchITSTPCQC::processV0(int iv, o2::globaltracking::RecoContainer& recoData, std::vector& mTBinClOcc, float pvTime) +{ + o2::dataformats::V0 v0; + auto v0s = recoData.getV0s(); + auto v0IDs = recoData.getV0sIdx(); + static int tfID = 0; + + const auto& v0id = v0IDs[iv]; + ++mNK0; + if (mNK0 % int(1 / mK0Scaling) == 0) { + LOG(debug) << "Checking " << mNK0 << "th V0: refitting it, since we keep " << mK0Scaling * 100 << "% of all V0s"; + } else { + LOG(debug) << "Checking " << mNK0 << "th K0: NOT refitting it, but skipping it, since we keep " << mK0Scaling * 100 << "% of all V0s"; + return false; + } + if (mRefit && !refitV0(v0id, v0, recoData)) { + return false; + } + const auto& v0sel = mRefit ? v0 : v0s[iv]; + if (mMaxEtaK0 < std::abs(v0sel.getEta())) { + return false; + } + + if (mCutK0Mass > 0 && std::abs(std::sqrt(v0sel.calcMass2AsK0()) - 0.497) > mCutK0Mass || v0sel.getDCA() > mK0MaxDCA || v0sel.getCosPA() < mK0MinCosPA) { + if (v0sel.getDCA() > mK0MaxDCA && v0sel.getCosPA() < mK0MinCosPA) { + LOG(debug) << "v0sel.getDCA() = " << v0sel.getDCA() << " max is " << mK0MaxDCA << " returning ... "; + } + if (v0sel.getCosPA() > mK0MinCosPA) { + LOG(debug) << "v0sel.getCosPA() = " << v0sel.getCosPA() << " min is " << mK0MinCosPA << " returning ... "; + } + return false; + } + // get the corresponding PV + int tb = pvTime / (8 * o2::constants::lhc::LHCBunchSpacingMUS) * mNTPCOccBinLengthInv; // V0 time in TPC time bins + LOG(debug) << "pvTime = " << pvTime << " tb = " << tb; + float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]); + ++mNK0; + LOG(debug) << "Filling K0 histogram with pt = " << v0sel.getPt() << " mass = " << std::sqrt(v0sel.calcMass2AsK0()) << " mult TPC = " << mltTPC; + if (!mIsHI) { + mK0MassVsPtVsOccpp->Fill(v0sel.getPt(), std::sqrt(v0sel.calcMass2AsK0()), mltTPC); + } else { + mK0MassVsPtVsOccPbPb->Fill(v0sel.getPt(), std::sqrt(v0sel.calcMass2AsK0()), mltTPC); + } + return true; +} + +//__________________________________________________________ +bool MatchITSTPCQC::refitV0(const o2::dataformats::V0Index& id, o2::dataformats::V0& v0, o2::globaltracking::RecoContainer& recoData) +{ + LOG(debug) << "Refitting V0"; + if (!recoData.isTrackSourceLoaded(id.getProngID(0).getSource()) || !recoData.isTrackSourceLoaded(id.getProngID(1).getSource())) { + return false; + } + auto seedP = recoData.getTrackParam(id.getProngID(0)); + auto seedN = recoData.getTrackParam(id.getProngID(1)); + bool isTPConly = (id.getProngID(0).getSource() == o2::dataformats::GlobalTrackID::TPC) || (id.getProngID(1).getSource() == o2::dataformats::GlobalTrackID::TPC); + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + if (svparam.mTPCTrackPhotonTune && isTPConly) { + mFitterV0.setMaxDZIni(svparam.mTPCTrackMaxDZIni); + mFitterV0.setMaxDXYIni(svparam.mTPCTrackMaxDXYIni); + mFitterV0.setMaxChi2(svparam.mTPCTrackMaxChi2); + mFitterV0.setCollinear(true); + } + int nCand = mFitterV0.process(seedP, seedN); + if (svparam.mTPCTrackPhotonTune && isTPConly) { // restore + // Reset immediately to the defaults + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setCollinear(false); + } + if (nCand == 0) { // discard this pair + return false; + } + const int cand = 0; + if (!mFitterV0.isPropagateTracksToVertexDone(cand) && !mFitterV0.propagateTracksToVertex(cand)) { + return false; + } + const auto& trPProp = mFitterV0.getTrack(0, cand); + const auto& trNProp = mFitterV0.getTrack(1, cand); + std::array pP{}, pN{}; + trPProp.getPxPyPzGlo(pP); + trNProp.getPxPyPzGlo(pN); + std::array pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + auto p2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1] + pV0[2] * pV0[2]; + const auto& pv = recoData.getPrimaryVertex(id.getVertexID()); + const auto v0XYZ = mFitterV0.getPCACandidatePos(cand); + float dx = v0XYZ[0] - pv.getX(), dy = v0XYZ[1] - pv.getY(), dz = v0XYZ[2] - pv.getZ(), prodXYZv0 = dx * pV0[0] + dy * pV0[1] + dz * pV0[2]; + float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0); + new (&v0) o2::dataformats::V0(v0XYZ, pV0, mFitterV0.calcPCACovMatrixFlat(cand), trPProp, trNProp); + v0.setDCA(mFitterV0.getChi2AtPCACandidate(cand)); + v0.setCosPA(cosPA); + return true; +} + +//__________________________________________________________ +void MatchITSTPCQC::finalize() +{ + + std::array title{"TPC", "ITS"}; + + // first we use denominators and nominators to set the TEfficiency; later they are scaled + + // filling the efficiency + for (int ti = 0; ti < matchType::SIZE; ++ti) { + setEfficiency(mFractionITSTPCmatch[ti], mPtNum[ti], mPtDen[ti]); + setEfficiency(mFractionITSTPCmatch_noEta0[ti], mPtNum_noEta0[ti], mPtDen_noEta0[ti]); + setEfficiency(mFractionITSTPCmatchPhi[ti], mPhiNum[ti], mPhiDen[ti]); + setEfficiency(mFractionITSTPCmatchEta[ti], mEtaNum[ti], mEtaDen[ti]); + setEfficiency<2>(mFractionITSTPCmatchPhiVsPt[ti], mPhiVsPtNum[ti], mPhiVsPtDen[ti]); + setEfficiency<2>(mFractionITSTPCmatchEtaVsPt[ti], mEtaVsPtNum[ti], mEtaVsPtDen[ti]); + setEfficiency(mFractionITSTPCmatch1OverPt[ti], m1OverPtNum[ti], m1OverPtDen[ti]); + setEfficiency<2>(mFractionITSTPCmatchClsVsPt[ti], mClsVsPtNum[ti], mClsVsPtDen[ti]); + setEfficiency<2>(mFractionITSTPCmatchChi2VsPt[ti], mChi2VsPtNum[ti], mChi2VsPtDen[ti]); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + setEfficiency(mFractionITSTPCmatchPtVsTrkPID[ti][j], mPtNumVsTrkPID[ti][j], mPtDenVsTrkPID[ti][j]); + setEfficiency(mFractionITSTPCmatchPhiVsTrkPID[ti][j], mPhiNumVsTrkPID[ti][j], mPhiDenVsTrkPID[ti][j]); + setEfficiency(mFractionITSTPCmatchEtaVsTrkPID[ti][j], mEtaNumVsTrkPID[ti][j], mEtaDenVsTrkPID[ti][j]); + } + } + if (mUseMC) { + setEfficiency(mFractionITSTPCmatchPhysPrim[ti], mPtPhysPrimNum[ti], mPtPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchPhiPhysPrim[ti], mPhiPhysPrimNum[ti], mPhiPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchEtaPhysPrim[ti], mEtaPhysPrimNum[ti], mEtaPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchPhysPrim1OverPt[ti], m1OverPtPhysPrimNum[ti], m1OverPtPhysPrimDen[ti]); + } + } + setEfficiency<2>(mFractionITSTPCmatchDCArVsPt, mDCArVsPtNum, mDCArVsPtDen); + /* + mPtTPC->Scale(scaleFactTPC); + mPt->Scale(scaleFactITSTPC); + mPhiTPC->Scale(scaleFactTPC); + mPhi->Scale(scaleFactITSTPC); + if (mUseMC) { + mPtTPCPhysPrim->Scale(scaleFactTPC); + mPtPhysPrim->Scale(scaleFactITSTPC); + mPhiTPCPhysPrim->Scale(scaleFactTPC); + mPhiPhysPrim->Scale(scaleFactITSTPC); + } + mEta->Scale(scaleFactITSTPC); + mChi2Matching->Scale(scaleFactITSTPC); + mChi2Refit->Scale(scaleFactITSTPC); + //mTimeResVsPt->Scale(scaleFactITSTPC); // if to few entries, one sees nothing after normalization --> let's not normalize + */ +} + +//__________________________________________________________ +template +void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden) +{ + // Trivial check if we initalized + if (eff == nullptr) { + LOG(fatal) << "Cannot get TEfficiency object "; + } + if (hnum == nullptr) { + LOG(fatal) << "Cannot get numerator histogram for TEfficiency object " << eff->GetName(); + } + if (hden == nullptr) { + LOG(fatal) << "Cannot get denominator histogram for TEfficiency object " << eff->GetName(); + } + + // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents + if constexpr (DEBUG) { // checking + bool bad{false}; + LOG(info) << "Setting efficiency " << eff->GetName() << " from " << hnum->GetName() << " and " << hden->GetName(); + LOG(info) << "Num " << hnum->GetName() << " " << hnum->GetNbinsX() << " " << hnum->GetNbinsY() << " with " << hnum->GetEntries() << " entries"; + LOG(info) << "Den " << hden->GetName() << " " << hden->GetNbinsX() << " " << hden->GetNbinsY() << " with " << hden->GetEntries() << " entries"; + if (hnum->GetDimension() != hden->GetDimension()) { + LOGP(warning, "Histograms have different dimensions (num={} to den={})", hnum->GetDimension(), hden->GetDimension()); + bad = true; + } + if (!TEfficiency::CheckBinning(*hnum, *hden)) { + LOGP(warning, "Histograms do not have a compatible binning"); + bad = true; + } + if constexpr (DIM == 3) { + for (int i = 1; i <= hden->GetNbinsX(); i++) { + for (int j = 1; j <= hden->GetNbinsY(); j++) { + for (int k = 1; k <= hden->GetNbinsZ(); k++) { + if (hden->GetBinContent(i, j, k) < hnum->GetBinContent(i, j, k)) { + LOGP(warning, "bin {}/{}/{} -> den: {} < num: {}", i, j, k, hden->GetBinContent(i, j, k), hnum->GetBinContent(i, j, k)); + bad = true; + } + } + } + } + } else if constexpr (DIM == 2) { + for (int i = 1; i <= hden->GetNbinsX(); i++) { + for (int j = 1; j <= hden->GetNbinsY(); j++) { + if (hden->GetBinContent(i, j) < hnum->GetBinContent(i, j)) { + LOGP(warning, "bin {}/{} -> den: {} < num: {}", i, j, hden->GetBinContent(i, j), hnum->GetBinContent(i, j)); + bad = true; + } + } + } + } else { + for (int i = 1; i <= hden->GetNbinsX(); i++) { + if (hden->GetBinContent(i) < hnum->GetBinContent(i)) { + LOG(warning) << "bin " << i << " den: " << hden->GetBinContent(i) << " < num: " << hnum->GetBinContent(i) << " should be the opposite"; + bad = true; + } + } + } + if (bad) { + LOG(info) << " `--> Histogram is bad!"; + return; + } else { + LOG(info) << " `--> Histogram is good!"; + } + } + // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents + if (!eff->SetTotalHistogram(*hden, "f")) { + LOG(fatal) << "Something went wrong when defining the efficiency denominator " << eff->GetName() << " from " << hnum->GetName(); + } + if (!eff->SetPassedHistogram(*hnum, "")) { + LOG(fatal) << "Something went wrong when defining the efficiency numerator " << eff->GetName() << " from " << hnum->GetName(); + } + if constexpr (DIM == 3) { + eff->SetTitle(Form("%s;%s;%s;%s;%s", eff->GetTitle(), hnum->GetXaxis()->GetTitle(), hnum->GetYaxis()->GetTitle(), hnum->GetZaxis()->GetTitle(), "Efficiency")); + } else if constexpr (DIM == 2) { + eff->SetTitle(Form("%s;%s;%s;%s", eff->GetTitle(), hnum->GetXaxis()->GetTitle(), hnum->GetYaxis()->GetTitle(), "Efficiency")); + } else { + eff->SetTitle(Form("%s;%s;%s", eff->GetTitle(), hnum->GetXaxis()->GetTitle(), "Efficiency")); + } +} + +//__________________________________________________________ + +void MatchITSTPCQC::getHistos(TObjArray& objar) +{ + + for (int i = 0; i < matchType::SIZE; ++i) { + objar.Add(mPtNum[i]); + objar.Add(mPtDen[i]); + objar.Add(mFractionITSTPCmatch[i]); + + objar.Add(mPtNum_noEta0[i]); + objar.Add(mPtDen_noEta0[i]); + objar.Add(mFractionITSTPCmatch_noEta0[i]); + + objar.Add(mPtPhysPrimNum[i]); + objar.Add(mPtPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhysPrim[i]); + + objar.Add(mPhiNum[i]); + objar.Add(mPhiDen[i]); + objar.Add(mFractionITSTPCmatchPhi[i]); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + objar.Add(mPtNumVsTrkPID[i][j]); + objar.Add(mPtDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchPtVsTrkPID[i][j]); + // Phi + objar.Add(mPhiNumVsTrkPID[i][j]); + objar.Add(mPhiDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchPhiVsTrkPID[i][j]); + // Eta + objar.Add(mEtaNumVsTrkPID[i][j]); + objar.Add(mEtaDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchEtaVsTrkPID[i][j]); + } + } + + objar.Add(mPhiPhysPrimNum[i]); + objar.Add(mPhiPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhiPhysPrim[i]); + + objar.Add(mPhiVsPtNum[i]); + objar.Add(mPhiVsPtDen[i]); + objar.Add(mFractionITSTPCmatchPhiVsPt[i]); + + objar.Add(mEtaNum[i]); + objar.Add(mEtaDen[i]); + objar.Add(mFractionITSTPCmatchEta[i]); + + objar.Add(mEtaPhysPrimNum[i]); + objar.Add(mEtaPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchEtaPhysPrim[i]); + + objar.Add(mEtaVsPtNum[i]); + objar.Add(mEtaVsPtDen[i]); + objar.Add(mFractionITSTPCmatchEtaVsPt[i]); + + objar.Add(mClsVsPtNum[i]); + objar.Add(mClsVsPtDen[i]); + objar.Add(mFractionITSTPCmatchClsVsPt[i]); + + objar.Add(mChi2VsPtNum[i]); + objar.Add(mChi2VsPtDen[i]); + objar.Add(mFractionITSTPCmatchChi2VsPt[i]); + + objar.Add(m1OverPtNum[i]); + objar.Add(m1OverPtDen[i]); + objar.Add(mFractionITSTPCmatch1OverPt[i]); + + objar.Add(m1OverPtPhysPrimNum[i]); + objar.Add(m1OverPtPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhysPrim1OverPt[i]); + + objar.Add(mEtaPhiPtNum[i]); + objar.Add(mEtaPhiPtDen[i]); + } + objar.Add(mChi2Matching); + objar.Add(mChi2Refit); + objar.Add(mTimeResVsPt); + objar.Add(mResidualPt); + objar.Add(mResidualPhi); + objar.Add(mResidualEta); + objar.Add(mDCAr); + objar.Add(mDCArVsPtNum); + objar.Add(mDCArVsPtDen); + objar.Add(mFractionITSTPCmatchDCArVsPt); + + // V0 + objar.Add(mK0MassVsPtVsOccpp); + objar.Add(mK0MassVsPtVsOccPbPb); +} + +void MatchITSTPCQC::printParams() const +{ + LOG(info) << "MatchITSTPCQC parameters:"; + LOG(info) << " - minPtBins = " << mPtBins; + LOG(info) << " - minPtITSCut = " << mPtITSCut; + LOG(info) << " - etaITSCut = " << mEtaITSCut; + LOG(info) << " - minNITSClustersCut = " << mMinNClustersITS; + LOG(info) << " - maxChi2PerClusterITS = " << mMaxChi2PerClusterITS; + LOG(info) << " - minPtTPCCut = " << mPtTPCCut; + LOG(info) << " - etaTPCCut = " << mEtaTPCCut; + LOG(info) << " - minNTPCClustersCut = " << mNTPCClustersCut; + LOG(info) << " - mEtaNo0Cut = " << mEtaNo0Cut; + LOG(info) << " - minDCACut = " << mDCATPCCut; + LOG(info) << " - minDCACutY = " << mDCATPCCutY; + LOG(info) << " - minPtCut = " << mPtCut; + LOG(info) << " - maxPtCut = " << mPtMaxCut; + LOG(info) << " - etaCut = " << mEtaCut; + LOG(info) << " - cutK0Mass = " << mCutK0Mass; + LOG(info) << " - maxEtaK0 = " << mMaxEtaK0; + LOG(info) << " - minTPCOccpp = " << mMinTPCOccpp; + LOG(info) << " - maxTPCOccpp = " << mMaxTPCOccpp; + LOG(info) << " - nBinsTPCOccpp = " << mNBinsTPCOccpp; + LOG(info) << " - minTPCOccPbPb = " << mMinTPCOccPbPb; + LOG(info) << " - maxTPCOccPbPb = " << mMaxTPCOccPbPb; + LOG(info) << " - nBinsTPCOccPbPb = " << mNBinsTPCOccPbPb; +} diff --git a/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h b/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h index 0270136abbf77..5043f36ef1433 100644 --- a/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h +++ b/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h @@ -16,6 +16,7 @@ #include #include #include "Framework/Logger.h" +#include "Framework/O2LongInt.h" #include "DetectorsDCS/DataPointCompositeObject.h" #include "DetectorsDCS/DataPointIdentifier.h" #include "DetectorsDCS/DataPointValue.h" @@ -37,12 +38,19 @@ using DPID = o2::dcs::DataPointIdentifier; using DPVAL = o2::dcs::DataPointValue; using DPCOM = o2::dcs::DataPointCompositeObject; -inline unsigned long llu2lu(std::uint64_t v) { return (unsigned long)v; } +inline O2LongUInt llu2lu(std::uint64_t v) { return (O2LongUInt)v; } struct GRPEnvVariables { - std::unordered_map>> mEnvVars; - + std::unordered_map>> mEnvVars; + size_t totalEntries() const + { + size_t s = 0; + for (const auto& el : mEnvVars) { + s += el.second.size(); + } + return s; + } void print() { for (const auto& el : mEnvVars) { @@ -53,7 +61,7 @@ struct GRPEnvVariables { } } - ClassDefNV(GRPEnvVariables, 1); + ClassDefNV(GRPEnvVariables, 2); }; struct MagFieldHelper { @@ -64,10 +72,12 @@ struct MagFieldHelper { bool negDip = 0; bool updated = false; bool verbose = false; + static constexpr float ZeroCurrent = 70.f; // current below this threshold considered to be produce 0 field + static constexpr float DeltaThreshold = 0.2f; // update field on delta's exceeding this value void updateCurL3(float v) { - if (!(isSet & 0x1) || std::abs(v - curL3) > 0.1) { + if (!(isSet & 0x1) || (std::abs(v - curL3) > DeltaThreshold && (std::abs(v) > ZeroCurrent || std::abs(curL3) > ZeroCurrent))) { if (verbose) { LOG(info) << "L3 current will be updated from " << curL3 << " to " << v; } @@ -78,7 +88,7 @@ struct MagFieldHelper { } void updateCurDip(float v) { - if (!(isSet & 0x2) || std::abs(v - curDip) > 0.1) { + if (!(isSet & 0x2) || (std::abs(v - curDip) > DeltaThreshold && (std::abs(v) > ZeroCurrent || std::abs(curDip) > ZeroCurrent))) { if (verbose) { LOG(info) << "Dipole current will be updated from " << curDip << " to " << v; } @@ -113,11 +123,17 @@ struct MagFieldHelper { struct GRPCollimators { - std::unordered_map>> mCollimators; - + std::unordered_map>> mCollimators; + size_t totalEntries() const + { + size_t s = 0; + for (const auto& el : mCollimators) { + s += el.second.size(); + } + return s; + } void print() { - for (const auto& el : mCollimators) { std::printf("%-60s\n", el.first.c_str()); for (const auto& it : el.second) { @@ -126,7 +142,7 @@ struct GRPCollimators { } } - ClassDefNV(GRPCollimators, 1); + ClassDefNV(GRPCollimators, 2); }; struct GRPLHCInfo { @@ -174,21 +190,21 @@ struct GRPLHCInfo { static constexpr std::string_view bptxPhaseShiftAliases[NBPTXPhaseShiftAliases] = {"BPTX_Phase_Shift_B1", "BPTX_Phase_Shift_B2"}; static constexpr std::string_view lumiAliases[NLumiAliases] = {"ALI_Lumi_Total_Inst"}; static constexpr std::string_view lhcStringAliases[NLHCStringAliases] = {"ALI_Lumi_Source_Name", "BEAM_MODE", "MACHINE_MODE"}; - static constexpr int nAliasesLHC = NCollimatorAliases + NBeamAliases + NBkgAliases + NBPTXAliases + NBPTXPhaseAliases + NBPTXPhaseRMSAliases + NBPTXPhaseShiftAliases + NLumiAliases + NLHCStringAliases; - - std::array>, 2> mIntensityBeam; - std::array>, 3> mBackground; - std::vector> mInstLumi; - std::vector> mBPTXdeltaT; - std::vector> mBPTXdeltaTRMS; - std::array>, 2> mBPTXPhase; - std::array>, 2> mBPTXPhaseRMS; - std::array>, 2> mBPTXPhaseShift; - std::pair mLumiSource; // only one value per object: when there is a change, a new object is stored - std::pair mMachineMode; // only one value per object: when there is a change, a new object is stored - std::pair mBeamMode; // only one value per object: when there is a change, a new object is stored - - void resetAndKeepLastVector(std::vector>& vect) + static constexpr int nAliasesLHC = (int)NCollimatorAliases + (int)NBeamAliases + (int)NBkgAliases + (int)NBPTXAliases + (int)NBPTXPhaseAliases + (int)NBPTXPhaseRMSAliases + (int)NBPTXPhaseShiftAliases + (int)NLumiAliases + (int)NLHCStringAliases; + + std::array>, 2> mIntensityBeam; + std::array>, 3> mBackground; + std::vector> mInstLumi; + std::vector> mBPTXdeltaT; + std::vector> mBPTXdeltaTRMS; + std::array>, 2> mBPTXPhase; + std::array>, 2> mBPTXPhaseRMS; + std::array>, 2> mBPTXPhaseShift; + std::pair mLumiSource; // only one value per object: when there is a change, a new object is stored + std::pair mMachineMode; // only one value per object: when there is a change, a new object is stored + std::pair mBeamMode; // only one value per object: when there is a change, a new object is stored + + void resetAndKeepLastVector(std::vector>& vect) { // always check that the size is > 0 (--> begin != end) for all vectors if (vect.begin() != vect.end()) { @@ -276,20 +292,22 @@ class GRPDCSDPsProcessor void init(const std::vector& pids); int process(const gsl::span dps); int processDP(const DPCOM& dpcom); - uint64_t processFlags(uint64_t flag, const char* pid) { return 0; } // for now it is not really implemented + O2LongUInt processFlags(O2LongUInt flag, const char* pid) { return 0; } // for now it is not really implemented bool processCollimators(const DPCOM& dpcom); bool processEnvVar(const DPCOM& dpcom); - bool processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate); - bool processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag); - bool compareToLatest(std::pair& p, double val); + bool processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate); + bool processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag); + bool compareToLatest(std::pair& p, double val); bool processLHCIFDPs(const DPCOM& dpcom); void resetAndKeepLastLHCIFDPs() { mLHCInfo.resetAndKeepLast(); } - void resetAndKeepLast(std::unordered_map>>& mapToReset) + void resetAndKeepLast(std::unordered_map>>& mapToReset) { // keep only the latest measurement for (auto& el : mapToReset) { - el.second.erase(el.second.begin(), el.second.end() - 1); + if (el.second.begin() != el.second.end()) { + el.second.erase(el.second.begin(), el.second.end() - 1); + } } } @@ -349,8 +367,8 @@ class GRPDCSDPsProcessor void useVerboseMode() { mVerbose = true; } void clearVectors() { mClearVectors = true; } - void printVectorInfo(const std::vector>& vect, bool afterUpdate); - void updateVector(const DPID& dpid, std::vector>& vect, std::string alias, uint64_t timestamp, double val); + void printVectorInfo(const std::vector>& vect, bool afterUpdate); + void updateVector(const DPID& dpid, std::vector>& vect, std::string alias, O2LongUInt timestamp, double val); private: std::unordered_map mPids; // contains all PIDs for the processor, the bool diff --git a/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx b/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx index 2700deed92fe8..aec4241f4f8db 100644 --- a/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx +++ b/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx @@ -175,7 +175,6 @@ bool GRPDCSDPsProcessor::processCollimators(const DPCOM& dpcom) bool GRPDCSDPsProcessor::processEnvVar(const DPCOM& dpcom) { - // function to process Data Points that are related to env variables bool match = processPairD(dpcom, "CavernTemperature", mEnvVars.mEnvVars) || processPairD(dpcom, "CavernAtmosPressure", mEnvVars.mEnvVars) || @@ -186,7 +185,7 @@ bool GRPDCSDPsProcessor::processEnvVar(const DPCOM& dpcom) } //______________________________________________________________________ -bool GRPDCSDPsProcessor::processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate) +bool GRPDCSDPsProcessor::processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate) { // function to process Data Points that is stored in a pair @@ -208,7 +207,7 @@ bool GRPDCSDPsProcessor::processPairD(const DPCOM& dpcom, const std::string& ali } //______________________________________________________________________ -bool GRPDCSDPsProcessor::processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag) +bool GRPDCSDPsProcessor::processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag) { // function to process string Data Points that is stored in a pair @@ -238,7 +237,7 @@ bool GRPDCSDPsProcessor::processPairS(const DPCOM& dpcom, const std::string& ali //______________________________________________________________________ -bool GRPDCSDPsProcessor::compareToLatest(std::pair& p, double val) +bool GRPDCSDPsProcessor::compareToLatest(std::pair& p, double val) { // check if the content of the pair should be updated @@ -278,13 +277,13 @@ bool GRPDCSDPsProcessor::processLHCIFDPs(const DPCOM& dpcom) } for (int ibeam = 0; ibeam < GRPLHCInfo::BeamAliases::NBeamAliases; ++ibeam) { - if (aliasStr.find(static_cast(GRPLHCInfo::beamAliases[ibeam])) != string::npos) { + if (aliasStr.find(static_cast(GRPLHCInfo::beamAliases[ibeam])) != std::string::npos) { updateVector(dpid, mLHCInfo.mIntensityBeam[ibeam], aliasStr, dpcomdata.get_epoch_time(), val); return true; } } - if (aliasStr.find("BPTX") != string::npos) { + if (aliasStr.find("BPTX") != std::string::npos) { if (aliasStr == static_cast(GRPLHCInfo::bptxAliases[GRPLHCInfo::BPTXAliases::BPTX_deltaT_B1_B2])) { updateVector(dpid, mLHCInfo.mBPTXdeltaT, aliasStr, dpcomdata.get_epoch_time(), val); return true; @@ -319,7 +318,7 @@ bool GRPDCSDPsProcessor::processLHCIFDPs(const DPCOM& dpcom) } for (int ibkg = 0; ibkg < 3; ++ibkg) { - if (aliasStr.find(static_cast(GRPLHCInfo::bkgAliases[ibkg])) != string::npos) { + if (aliasStr.find(static_cast(GRPLHCInfo::bkgAliases[ibkg])) != std::string::npos) { updateVector(dpid, mLHCInfo.mBackground[ibkg], aliasStr, dpcomdata.get_epoch_time(), val); return true; } @@ -396,20 +395,20 @@ void GRPDCSDPsProcessor::updateEnvVarsCCDB() void GRPDCSDPsProcessor::updateCollimatorsCCDB() { - // we need to update a CCDB for the Env Variables DPs --> let's prepare the CCDBInfo + // we need to update a CCDB for the Collimators Variables DPs --> let's prepare the CCDBInfo if (mVerbose) { LOG(info) << "Entry related to Collimators needs to be updated with startTime " << mStartValidityColli; } std::map md; md["responsible"] = "Chiara Zampolli"; - o2::calibration::Utils::prepareCCDBobjectInfo(mEnvVars, mccdbCollimatorsInfo, "GLO/Config/Collimators", md, mStartValidityColli, mStartValidityColli + 3 * o2::ccdb::CcdbObjectInfo::DAY); // valid for 3 days + o2::calibration::Utils::prepareCCDBobjectInfo(mCollimators, mccdbCollimatorsInfo, "GLO/Config/Collimators", md, mStartValidityColli, mStartValidityColli + 3 * o2::ccdb::CcdbObjectInfo::DAY); // valid for 3 days return; } //______________________________________________________________________ -void GRPDCSDPsProcessor::printVectorInfo(const std::vector>& vect, bool afterUpdate) +void GRPDCSDPsProcessor::printVectorInfo(const std::vector>& vect, bool afterUpdate) { std::string stage = afterUpdate ? "after update" : "before update"; @@ -423,7 +422,7 @@ void GRPDCSDPsProcessor::printVectorInfo(const std::vector>& vect, std::string alias, uint64_t timestamp, double val) +void GRPDCSDPsProcessor::updateVector(const DPID& dpid, std::vector>& vect, std::string alias, O2LongUInt timestamp, double val) { printVectorInfo(vect, 0); bool updateFlag = false; diff --git a/Detectors/GRP/workflows/CMakeLists.txt b/Detectors/GRP/workflows/CMakeLists.txt index 87957000ab08b..1097855a5d579 100644 --- a/Detectors/GRP/workflows/CMakeLists.txt +++ b/Detectors/GRP/workflows/CMakeLists.txt @@ -31,11 +31,21 @@ o2_add_executable(grp-lhc-if-file-workflow O2::GRPCalibration O2::DetectorsCalibration) +o2_add_executable(workflow + COMPONENT_NAME rct-updater + SOURCES src/rct-updater-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::CCDB + O2::DetectorsBase + O2::DetectorsCalibration + O2::DataFormatsParameters) + o2_add_executable(grp-create COMPONENT_NAME ecs SOURCES src/create-grp-ecs.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsCommonDataFormats O2::DataFormatsParameters + O2::DataFormatsCTP O2::CommonUtils O2::CCDB Boost::program_options) @@ -47,6 +57,8 @@ o2_add_executable(workflow O2::CommonUtils O2::CCDB O2::DetectorsRaw + O2::ITSBase + O2::MFTBase Boost::program_options) o2_add_executable(grp-dcs-dps-sim-workflow diff --git a/Detectors/GRP/workflows/include/GRPWorkflows/GRPDCSDPsSpec.h b/Detectors/GRP/workflows/include/GRPWorkflows/GRPDCSDPsSpec.h index 52f6be37a226a..6a9b946251eb1 100644 --- a/Detectors/GRP/workflows/include/GRPWorkflows/GRPDCSDPsSpec.h +++ b/Detectors/GRP/workflows/include/GRPWorkflows/GRPDCSDPsSpec.h @@ -45,6 +45,9 @@ class GRPDCSDPsDataProcessor : public Task int64_t mDPsUpdateInterval; bool mReportTiming = false; bool mLHCIFupdated = false; + size_t mEmptyCyclesEnvVars = 0; + size_t mEmptyCyclesCollimators = 0; + size_t mWarnEmptyCycles = 1; }; } // namespace grp diff --git a/Detectors/GRP/workflows/src/GRPDCSDPsSpec.cxx b/Detectors/GRP/workflows/src/GRPDCSDPsSpec.cxx index 24ac29aed05ad..6f214cf7c0b42 100644 --- a/Detectors/GRP/workflows/src/GRPDCSDPsSpec.cxx +++ b/Detectors/GRP/workflows/src/GRPDCSDPsSpec.cxx @@ -46,6 +46,7 @@ void GRPDCSDPsDataProcessor::init(o2::framework::InitContext& ic) std::vector vect; mDPsUpdateInterval = ic.options().get("DPs-update-interval"); + mWarnEmptyCycles = ic.options().get("warn-empty-cycles"); if (mDPsUpdateInterval == 0) { LOG(error) << "GRP DPs update interval set to zero seconds --> changed to 60"; mDPsUpdateInterval = 60; @@ -248,6 +249,14 @@ void GRPDCSDPsDataProcessor::sendCollimatorsDPsoutput(DataAllocator& output) // filling CCDB with Collimators object const auto& payload = mProcessor->getCollimatorsObj(); + if (payload.totalEntries() == 0) { + if ((mEmptyCyclesCollimators % size_t(mWarnEmptyCycles)) == 0) { + LOGP(alarm, "No Collimator DPs were received after {} {}-s cycles", mEmptyCyclesCollimators, mDPsUpdateInterval); + } + mEmptyCyclesCollimators++; + } else { + mEmptyCyclesCollimators = 0; + } auto& info = mProcessor->getccdbCollimatorsInfo(); auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); LOG(info) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() @@ -264,6 +273,14 @@ void GRPDCSDPsDataProcessor::sendEnvVarsDPsoutput(DataAllocator& output) // filling CCDB with EnvVars object const auto& payload = mProcessor->getEnvVarsObj(); + if (payload.totalEntries() == 0) { + if ((mEmptyCyclesEnvVars % size_t(mWarnEmptyCycles)) == 0) { + LOGP(alarm, "No EnvVar DPs were received after {} {}-s cycles", mEmptyCyclesEnvVars, mDPsUpdateInterval); + } + mEmptyCyclesEnvVars++; + } else { + mEmptyCyclesEnvVars = 0; + } auto& info = mProcessor->getccdbEnvVarsInfo(); auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); LOG(info) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() @@ -305,6 +322,7 @@ DataProcessorSpec getGRPDCSDPsDataProcessorSpec() {"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}, {"report-timing", VariantType::Bool, false, {"Report timing for every slice"}}, {"DPs-update-interval", VariantType::Int64, 600ll, {"Interval (in s) after which to update the DPs CCDB entry"}}, + {"warn-empty-cycles", VariantType::Int, 1, {"Warn about empty object after this number of cycles"}}, {"clear-vectors", VariantType::Bool, false, {"Clear vectors when starting processing for a new CCDB entry (latest value will not be kept)"}}}}; } diff --git a/Detectors/GRP/workflows/src/GRPLHCIFfileSpec.cxx b/Detectors/GRP/workflows/src/GRPLHCIFfileSpec.cxx index 37608f1bd34b1..5e5eba87dafe2 100644 --- a/Detectors/GRP/workflows/src/GRPLHCIFfileSpec.cxx +++ b/Detectors/GRP/workflows/src/GRPLHCIFfileSpec.cxx @@ -245,8 +245,8 @@ DataProcessorSpec getGRPLHCIFfileSpec() outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "GRP_LHCIFData"}, Lifetime::Sporadic); return DataProcessorSpec{ "grp-lhc-if-file-processor", - Inputs{{"inputConfig", "GRP", "DCS_CONFIG_FILE", Lifetime::Timeframe}, - {"inputConfigFileName", "GRP", "DCS_CONFIG_NAME", Lifetime::Timeframe}}, + Inputs{{"inputConfig", "GRP", "DCS_CONFIG_FILE", Lifetime::Sporadic}, + {"inputConfigFileName", "GRP", "DCS_CONFIG_NAME", Lifetime::Sporadic}}, outputs, AlgorithmSpec{adaptFromTask()}, Options{{"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}}}; diff --git a/Detectors/GRP/workflows/src/create-aligned-geometry.cxx b/Detectors/GRP/workflows/src/create-aligned-geometry.cxx index 69c3b9a3561d3..d738976ff4ffd 100644 --- a/Detectors/GRP/workflows/src/create-aligned-geometry.cxx +++ b/Detectors/GRP/workflows/src/create-aligned-geometry.cxx @@ -18,6 +18,8 @@ #include #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" +#include "MFTBase/GeometryTGeo.h" +#include "ITSBase/GeometryTGeo.h" #include "Framework/Task.h" #include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" @@ -96,7 +98,33 @@ class AlignerTask : public Task gGeoManager->SetName(std::string(o2::base::NameConf::CCDBOBJECT).c_str()); auto fnm = o2::base::NameConf::getAlignedGeomFileName(); gGeoManager->Export(fnm.c_str()); - LOG(info) << "Stored to local file " << fnm; + LOGP(info, "Stored aligned geometry to local file {}", fnm); + + // create GeometryTGeo for detectors which support it + if (mDetsMask[DetID::ITS] +#ifdef ENABLE_UPGRADES + || mDetsMask[DetID::IT3] +#endif + ) { + auto itsTGeo = o2::its::GeometryTGeo::Instance(); + itsTGeo->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2GRot)); + TFile outF("its_GeometryTGeo.root", "recreate"); + outF.WriteObjectAny(itsTGeo, "o2::its::GeometryTGeo", "ccdb_object"); + LOGP(info, "Stored ITS geometry to {}", outF.GetName()); + outF.Close(); + itsTGeo->destroy(); + } + if (mDetsMask[DetID::MFT]) { + auto mftTGeo = o2::mft::GeometryTGeo::Instance(); + mftTGeo->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); + TFile outF("mft_GeometryTGeo.root", "recreate"); + outF.WriteObjectAny(mftTGeo, "o2::mft::GeometryTGeo", "ccdb_object"); + LOGP(info, "Stored MFT geometry to {}", outF.GetName()); + outF.Close(); + mftTGeo->destroy(); + } + // consider upgrades? + pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); } @@ -122,7 +150,10 @@ DataProcessorSpec getAlignerSpec(DetID::mask_t dets) false, // GRPMagField false, // askMatLUT o2::base::GRPGeomRequest::Alignments, // geometry - inputs); + inputs, // inputs + false, // ask-once + false, // dprop + DetID::getNames(dets)); // only selected detectors return DataProcessorSpec{ "geometry-aligned-producer", diff --git a/Detectors/GRP/workflows/src/create-grp-ecs.cxx b/Detectors/GRP/workflows/src/create-grp-ecs.cxx index 252ab3845e146..d9a73f0737799 100644 --- a/Detectors/GRP/workflows/src/create-grp-ecs.cxx +++ b/Detectors/GRP/workflows/src/create-grp-ecs.cxx @@ -15,8 +15,10 @@ #include #include #include "DataFormatsParameters/GRPECSObject.h" +#include "DataFormatsCTP/Configuration.h" #include "DetectorsCommonDataFormats/DetID.h" #include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" #include "CommonUtils/NameConf.h" #include "CommonUtils/StringUtils.h" @@ -29,24 +31,30 @@ enum CCDBRefreshMode { NONE, ASYNC, SYNC }; -void createGRPECSObject(const std::string& dataPeriod, - int run, - int runTypeI, - int nHBPerTF, - const std::string& _detsReadout, - const std::string& _detsContinuousRO, - const std::string& _detsTrigger, - const std::string& flpList, - long tstart, - long tend, - long tstartCTP, - long tendCTP, - long marginAtSOR, - long marginAtEOR, - const std::string& ccdbServer = "", - const std::string& metaDataStr = "", - CCDBRefreshMode refresh = CCDBRefreshMode::NONE) +int createGRPECSObject(const std::string& dataPeriod, + int run, + int runOrig, // in case of replay + int runTypeI, + int nHBPerTF, + const std::string& _detsReadout, + const std::string& _detsContinuousRO, + const std::string& _detsTrigger, + const std::string& flpList, + long tstart, + long tend, + long tstartCTP, + long tendCTP, + long marginAtSOR, + long marginAtEOR, + const std::string& ccdbServer = "", + std::string ccdbServerInp = "", + const std::string& metaDataStr = "", + CCDBRefreshMode refresh = CCDBRefreshMode::NONE) { + int retValGLO = 0; + int retValRCT = 0; + int retValGLOmd = 0; + int retValCTP = 0; // substitute TRG by CTP std::regex regCTP(R"((^\s*|,\s*)(TRG)(\s*,|\s*$))"); std::string detsReadout{std::regex_replace(_detsReadout, regCTP, "$1CTP$3")}; @@ -74,6 +82,8 @@ void createGRPECSObject(const std::string& dataPeriod, tendVal = tend + marginAtEOR; } GRPECSObject grpecs; + o2::ctp::CTPConfiguration* ctpConfig = nullptr; + o2::ctp::CTPConfiguration ctpConfigNew; grpecs.setTimeStart(tstart); grpecs.setTimeEnd(tend); grpecs.setTimeStartCTP(tstartCTP); @@ -115,17 +125,59 @@ void createGRPECSObject(const std::string& dataPeriod, } }; + if (ccdbServerInp.empty()) { + ccdbServerInp = ccdbServer; + } + if (runOrig > 0 && runOrig != run && tend <= tstart && !ccdbServerInp.empty()) { // create CTP config + try { + auto& bcm = o2::ccdb::BasicCCDBManager::instance(); + bcm.setURL(ccdbServerInp); + bcm.setFatalWhenNull(false); + ctpConfig = bcm.getForRun("CTP/Config/Config", runOrig); + if (!ctpConfig) { + throw std::runtime_error(fmt::format("Failed to access CTP/Config/Config for original run {}", runOrig)); + } + std::string cfstr = ctpConfig->getConfigString(), srun{fmt::format("run {}", run)}, srunOrig{fmt::format("run {}", runOrig)}; + o2::utils::Str::replaceAll(cfstr, srunOrig, srun); + ctpConfigNew.loadConfigurationRun3(cfstr); + ctpConfigNew.setRunNumber(run); + } catch (std::exception e) { + LOGP(error, "Failed to create CTP/Config/Config from the original run {}, reason: {}", runOrig, e.what()); + } + } + toKeyValPairs(metaDataStr); if (!ccdbServer.empty()) { CcdbApi api; + const std::string objPath{"GLO/Config/GRPECS"}; api.init(ccdbServer); metadata["responsible"] = "ECS"; metadata[o2::base::NameConf::CCDBRunTag.data()] = std::to_string(run); metadata["EOR"] = fmt::format("{}", tend); - api.storeAsTFileAny(&grpecs, objPath, metadata, tstart, tendVal); // making it 1-year valid to be sure we have something - LOGP(info, "Uploaded to {}/{} with validity {}:{} for SOR:{}/EOR:{}", ccdbServer, objPath, tstart, tendVal, tstart, tend); + retValGLO = api.storeAsTFileAny(&grpecs, objPath, metadata, tstart, tendVal); // making it 1-year valid to be sure we have something + if (retValGLO == 0) { + LOGP(info, "Uploaded to {}/{} with validity {}:{} for SOR:{}/EOR:{}", ccdbServer, objPath, tstart, tendVal, tstart, tend); + } else { + LOGP(alarm, "Upload to {}/{} with validity {}:{} for SOR:{}/EOR:{} FAILED, returned with code {}", ccdbServer, objPath, tstart, tendVal, tstart, tend, retValGLO); + } + if ((runType == GRPECSObject::RunType::PHYSICS || runType == GRPECSObject::RunType::COSMICS) && tstart >= tend) { // also create the RCT/Info/RunInformation entry in case the run type is PHYSICS, to be finalized at EOR + char tempChar{}; + std::map mdRCT; + mdRCT["SOR"] = std::to_string(tstart); + mdRCT["EOR"] = std::to_string(tend); + mdRCT["SOX"] = std::to_string(tstartCTP); + mdRCT["EOX"] = std::to_string(tendCTP); + long startValRCT = (long)run; + long endValRCT = (long)(run + 1); + retValRCT = api.storeAsBinaryFile(&tempChar, sizeof(tempChar), "tmp.dat", "char", "RCT/Info/RunInformation", mdRCT, startValRCT, endValRCT); + if (retValRCT == 0) { + LOGP(info, "Uploaded initial RCT object to {}/{} with validity {}:{}", ccdbServer, "RCT/Info/RunInformation", startValRCT, endValRCT); + } else { + LOGP(alarm, "Upload of initial RCT object to {}/{} with validity {}:{} FAILED, returned with code {}", ccdbServer, "RCT/Info/RunInformation", startValRCT, endValRCT, retValRCT); + } + } if (tend > tstart) { // override SOR version to the same limits metadata.erase("EOR"); @@ -135,27 +187,55 @@ void createGRPECSObject(const std::string& dataPeriod, std::string etag = itETag->second; etag.erase(remove(etag.begin(), etag.end(), '\"'), etag.end()); LOGP(info, "Overriding run {} SOR-only version {}{}{}/{} validity to match complete SOR/EOR version validity", run, ccdbServer, ccdbServer.back() == '/' ? "" : "/", prevHeader["Valid-From"], etag); - api.updateMetadata(objPath, {}, std::max(tstart, tendVal - 1), etag, tendVal); + retValGLOmd = api.updateMetadata(objPath, {}, std::max(tstart, tendVal - 1), etag, tendVal); + if (retValGLOmd != 0) { + LOGP(alarm, "Overriding run {} SOR-only version {}{}{}/{} validity to match complete SOR/EOR version validity FAILED", run, ccdbServer, ccdbServer.back() == '/' ? "" : "/", prevHeader["Valid-From"], etag); + } } - if (runType == GRPECSObject::RunType::PHYSICS) { // also storing the RCT/Info/RunInformation entry in case the run type is PHYSICS and if we are at the end of run + if (runType == GRPECSObject::RunType::PHYSICS || runType == GRPECSObject::RunType::COSMICS) { // also storing the RCT/Info/RunInformation entry in case the run type is PHYSICS and if we are at the end of run char tempChar{}; std::map mdRCT; mdRCT["SOR"] = std::to_string(tstart); mdRCT["EOR"] = std::to_string(tend); + mdRCT["SOX"] = std::to_string(tstartCTP); + mdRCT["EOX"] = std::to_string(tendCTP); long startValRCT = (long)run; long endValRCT = (long)(run + 1); - api.storeAsBinaryFile(&tempChar, sizeof(tempChar), "tmp.dat", "char", "RCT/Info/RunInformation", mdRCT, startValRCT, endValRCT); - LOGP(info, "Uploaded RCT object to {}/{} with validity {}:{}", ccdbServer, "RCT/Info/RunInformation", startValRCT, endValRCT); + retValRCT = api.updateMetadata("RCT/Info/RunInformation", mdRCT, startValRCT); + if (retValRCT == 0) { + LOGP(info, "Updated RCT object to SOR:{}/EOR:{} SOX:{}/EOX:{}", tstart, tend, tstartCTP, tendCTP); + } else { + LOGP(alarm, "Update of RCT object to SOR:{}/EOR:{} SOX:{}/EOX:{} FAILED, returned with code {}", tstart, tend, tstartCTP, tendCTP, retValRCT); + } } } + if (ctpConfig && ctpConfigNew.getRunNumber() == run) { // create CTP config + std::map metadataCTP; + metadataCTP["runNumber"] = fmt::format("{}", run); + metadataCTP["comment"] = fmt::format("cloned from run {}", runOrig); + retValCTP = api.storeAsTFileAny(&ctpConfigNew, "CTP/Config/Config", metadataCTP, tstart, tendVal); + if (retValCTP == 0) { + LOGP(info, "Uploaded to {}/{} with validity {}:{} for SOR:{}/EOR:{}, cloned from run {}", ccdbServer, "CTP/Config/Config", tstart, tendVal, tstart, tend, runOrig); + } else { + LOGP(alarm, "Upload to {}/{} with validity {}:{} for SOR:{}/EOR:{} (cloned from run {}) FAILED, returned with code {}", ccdbServer, "CTP/Config/Config", tstart, tendVal, tstart, tend, runOrig, retValCTP); + } + } } else { // write a local file auto fname = o2::base::NameConf::getGRPECSFileName(); TFile grpF(fname.c_str(), "recreate"); grpF.WriteObjectAny(&grpecs, grpecs.Class(), o2::base::NameConf::CCDBOBJECT.data()); - LOG(info) << "Stored to local file " << fname; + grpF.Close(); + LOGP(info, "Stored GRPECS to local file {}", fname); + if (ctpConfig && ctpConfigNew.getRunNumber() == run) { + std::string ctnpfname = fmt::format("CTPConfig_{}_from_{}.root", run, runOrig); + TFile ctpF(ctnpfname.c_str(), "recreate"); + ctpF.WriteObjectAny(&ctpConfigNew, ctpConfigNew.Class(), o2::base::NameConf::CCDBOBJECT.data()); + ctpF.Close(); + LOGP(info, "Stored CTPConfig to local file {}", ctnpfname); + } } - // + if (refresh != CCDBRefreshMode::NONE && !ccdbServer.empty()) { auto cmd = fmt::format("curl -I -i -s \"{}{}latest/%5Cw%7B3%7D/.*/`date +%s000`/?prepare={}\"", ccdbServer, ccdbServer.back() == '/' ? "" : "/", refresh == CCDBRefreshMode::SYNC ? "sync" : "true"); auto t0 = std::chrono::high_resolution_clock::now(); @@ -163,6 +243,10 @@ void createGRPECSObject(const std::string& dataPeriod, auto t1 = std::chrono::high_resolution_clock::now(); LOGP(info, "Executed [{}] -> {} in {:.3f} s", cmd, res, std::chrono::duration_cast(t1 - t0).count() / 1000.f); } + if (retValGLO != 0 || retValRCT != 0 || retValGLOmd != 0 || retValCTP != 0) { + return 4; + } + return 0; } int main(int argc, char** argv) @@ -184,19 +268,21 @@ int main(int argc, char** argv) add_option("run,r", bpo::value(), "run number"); add_option("run-type,t", bpo::value()->default_value(int(GRPECSObject::RunType::NONE)), "run type"); add_option("hbf-per-tf,n", bpo::value()->default_value(128), "number of HBFs per TF"); - add_option("detectors,d", bpo::value()->default_value("all"), "comma separated list of detectors"); - add_option("continuous,c", bpo::value()->default_value("ITS,TPC,TOF,MFT,MCH,MID,ZDC,FT0,FV0,FDD,CTP"), "comma separated list of detectors in continuous readout mode"); - add_option("triggering,g", bpo::value()->default_value("FT0,FV0"), "comma separated list of detectors providing a trigger"); - add_option("flps,f", bpo::value()->default_value(""), "comma separated list of FLPs in the data taking"); + add_option("detectors,d", bpo::value()->default_value("all"), "comma separated list of detectors"); + add_option("continuous,c", bpo::value()->default_value("ITS,TPC,TOF,MFT,MCH,MID,ZDC,FT0,FV0,FDD,CTP"), "comma separated list of detectors in continuous readout mode"); + add_option("triggering,g", bpo::value()->default_value("FT0,FV0"), "comma separated list of detectors providing a trigger"); + add_option("flps,f", bpo::value()->default_value(""), "comma separated list of FLPs in the data taking"); add_option("start-time,s", bpo::value()->default_value(0), "ECS run start time in ms, now() if 0"); add_option("end-time,e", bpo::value()->default_value(0), "ECS run end time in ms, start-time+3days is used if 0"); add_option("start-time-ctp", bpo::value()->default_value(0), "run start CTP time in ms, same as ECS if not set or 0"); add_option("end-time-ctp", bpo::value()->default_value(0), "run end CTP time in ms, same as ECS if not set or 0"); add_option("ccdb-server", bpo::value()->default_value("http://alice-ccdb.cern.ch"), "CCDB server for upload, local file if empty"); + add_option("ccdb-server-input", bpo::value()->default_value(""), "CCDB server for inputs (if needed, e.g. CTPConfig), dy default ccdb-server is used"); add_option("meta-data,m", bpo::value()->default_value("")->implicit_value(""), "metadata as key1=value1;key2=value2;.."); - add_option("refresh", bpo::value()->default_value("")->implicit_value("async"), R"(refresh server cache after upload: "none" (or ""), "async" (non-blocking) and "sync" (blocking))"); + add_option("refresh", bpo::value()->default_value("")->implicit_value("async"), R"(refresh server cache after upload: "none" (or ""), "async" (non-blocking) and "sync" (blocking))"); add_option("marginSOR", bpo::value()->default_value(4 * o2::ccdb::CcdbObjectInfo::DAY), "validity at SOR"); add_option("marginEOR", bpo::value()->default_value(10 * o2::ccdb::CcdbObjectInfo::MINUTE), "validity margin to add after EOR"); + add_option("original-run,o", bpo::value()->default_value(0), "if >0, use as the source run to create CTP/Config/Config object"); opt_all.add(opt_general).add(opt_hidden); bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); @@ -217,17 +303,17 @@ int main(int argc, char** argv) } if (vm.count("run") == 0) { std::cerr << "ERROR: " - << "obligator run number is missing" << std::endl; + << "obligatory run number is missing" << std::endl; std::cerr << opt_general << std::endl; exit(3); } if (vm.count("period") == 0) { std::cerr << "ERROR: " - << "obligator data taking period name is missing" << std::endl; + << "obligatory data taking period name is missing" << std::endl; std::cerr << opt_general << std::endl; exit(3); } - std::string refreshStr = vm["refresh"].as(); + std::string refreshStr = vm["refresh"].as(); CCDBRefreshMode refresh = CCDBRefreshMode::NONE; if (!refreshStr.empty() && refreshStr != "none") { if (refreshStr == "async") { @@ -239,9 +325,10 @@ int main(int argc, char** argv) } } - createGRPECSObject( + int retVal = createGRPECSObject( vm["period"].as(), vm["run"].as(), + vm["original-run"].as(), vm["run-type"].as(), vm["hbf-per-tf"].as(), vm["detectors"].as(), @@ -255,6 +342,14 @@ int main(int argc, char** argv) vm["marginSOR"].as(), vm["marginEOR"].as(), vm["ccdb-server"].as(), + vm["ccdb-server-input"].as(), vm["meta-data"].as(), refresh); + + if (retVal != 0) { + std::cerr << "ERROR: " + << "Either GLO/Config/GRPECS or RCT/Info/RunInformation could not be stored correctly to CCDB" << std::endl; + std::cerr << opt_general << std::endl; + exit(retVal); + } } diff --git a/Detectors/GRP/workflows/src/rct-updater-workflow.cxx b/Detectors/GRP/workflows/src/rct-updater-workflow.cxx new file mode 100644 index 0000000000000..9634dbe2bcdea --- /dev/null +++ b/Detectors/GRP/workflows/src/rct-updater-workflow.cxx @@ -0,0 +1,204 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/Logger.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/InputSpec.h" +#include "Framework/Task.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsCalibration/Utils.h" + +using namespace o2::framework; + +void customize(std::vector& workflowOptions) +{ + std::vector options{{"configKeyValues", o2::framework::VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + std::swap(workflowOptions, options); +} + +#include "DetectorsBase/GRPGeomHelper.h" +#include "CCDB/CcdbApi.h" +#include "DataFormatsParameters/GRPECSObject.h" + +namespace o2::rct +{ +class RCTUpdaterSpec final : public o2::framework::Task +{ + public: + RCTUpdaterSpec(std::shared_ptr gr) : mGGCCDBRequest(gr) {} + ~RCTUpdaterSpec() final = default; + + void init(InitContext& ic) final + { + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mUpdateInterval = std::max(0.1f, ic.options().get("update-interval")); + auto ccdb = ic.options().get("ccdb-server"); + if (!ccdb.empty() && ccdb != "none") { + mCCDBApi = std::make_unique(); + mCCDBApi->init(ic.options().get("ccdb-server")); + } else { + LOGP(warn, "No ccdb server provided, no RCT update will be done"); + } + mTimeToleranceMS = ic.options().get("max-diff-orbit-creationtime"); + mMaxWarnings = ic.options().get("max-warn-tf-discard"); + } + + void run(ProcessingContext& pc) final + { + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + auto tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged) { // do we have RCT object? + const auto* grp = o2::base::GRPGeomHelper::instance().getGRPECS(); + mNHBFPerTF = grp->getNHBFPerTF(); + if (mNHBFPerTF < 1) { + mNHBFPerTF = 32; + } + mRunNumber = tinfo.runNumber; + mUpdateIntervalTF = uint32_t(mUpdateInterval / (mNHBFPerTF * o2::constants::lhc::LHCOrbitMUS * 1e-6)); // convert update interval in seconds to interval in TFs + LOGP(info, "Will update RCT after {} TFs of {} HBFs ({}s was requested)", mUpdateIntervalTF, mNHBFPerTF, mUpdateInterval); + mOrbitReset = o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(); + mMinOrbit = 0xffffffff; + mMaxOrbit = 0; + if (grp->getRunType() == o2::parameters::GRPECS::PHYSICS || grp->getRunType() == o2::parameters::GRPECS::COSMICS) { + mEnabled = true; + } else { + LOGP(warning, "Run {} type is {}, disabling RCT update", mRunNumber, o2::parameters::GRPECS::RunTypeNames[grp->getRunType()]); + mEnabled = false; + } + if (mEnabled) { + if (mCCDBApi) { + auto md = mCCDBApi->retrieveHeaders("RCT/Info/RunInformation", {}, grp->getRun()); + if (md.empty()) { + mEnabled = false; + LOGP(alarm, "RCT object is missing for {} run {}, disabling RCT updater", o2::parameters::GRPECS::RunTypeNames[grp->getRunType()], grp->getRun()); + } + } + } + } + if (mEnabled) { + // make sure that the orbit makes sense, since sometimes at the EOR bogus TFs are sent. + long ts = mOrbitReset + long(tinfo.firstTForbit * o2::constants::lhc::LHCOrbitMUS * 1e-3); + if (mTimeToleranceMS > 0 && std::abs(int64_t(tinfo.creation) - ts) < mTimeToleranceMS) { + if (tinfo.firstTForbit < mMinOrbit) { + mMinOrbit = tinfo.firstTForbit; + } + if (tinfo.firstTForbit > mMaxOrbit) { + mMaxOrbit = tinfo.firstTForbit; + } + if (tinfo.tfCounter > mLastTFUpdate + mUpdateIntervalTF) { // need to update + mLastTFUpdate = tinfo.tfCounter; + updateRCT(); + } + } else { + static int nWarn = 0; + if (nWarn < mMaxWarnings) { + nWarn++; + LOGP(warn, "timestamp {} for orbit {} and orbit reset time {} differs by >{} from the TF creation time {}, ignore TF {}", ts, tinfo.firstTForbit, mOrbitReset / 1000, mTimeToleranceMS, tinfo.creation, tinfo.tfCounter); + } + } + } + } + + void endOfStream(framework::EndOfStreamContext& ec) final + { + if (mEnabled) { + updateRCT(); + mEnabled = false; + } + } + + void stop() final + { + if (mEnabled) { + updateRCT(); + mEnabled = false; + } + } + + void finaliseCCDB(framework::ConcreteDataMatcher& matcher, void* obj) final + { + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + } + + void updateRCT() + { + std::map mdRCT; + if (mMinOrbit > mMaxOrbit) { + return; + } + mdRCT["STF"] = std::to_string(long(mMinOrbit * o2::constants::lhc::LHCOrbitMUS * 1e-3) + mOrbitReset); + mdRCT["ETF"] = std::to_string(long((mMaxOrbit + mNHBFPerTF - 1) * o2::constants::lhc::LHCOrbitMUS * 1e-3) + mOrbitReset); + long startValRCT = (long)mRunNumber; + long endValRCT = (long)(mRunNumber + 1); + if (mCCDBApi) { + int retValRCT = mCCDBApi->updateMetadata("RCT/Info/RunInformation", mdRCT, startValRCT); + if (retValRCT == 0) { + LOGP(info, "Updated {}/RCT/Info/RunInformation object for run {} with TF start:{} end:{}", mCCDBApi->getURL(), mRunNumber, mdRCT["STF"], mdRCT["ETF"]); + } else { + LOGP(alarm, "Update of RCT object for run {} with TF start:{} end:{} FAILED, returned with code {}", mRunNumber, mdRCT["STF"], mdRCT["ETF"], retValRCT); + } + } else { + LOGP(info, "CCDB update disabled, TF timestamp range is {}:{}", mdRCT["STF"], mdRCT["ETF"]); + } + } + + private: + bool mEnabled = true; + float mUpdateInterval = 1.; + int mUpdateIntervalTF = 1; + uint32_t mMinOrbit = 0xffffffff; + uint32_t mMaxOrbit = 0; + uint32_t mLastTFUpdate = 0; + long mTimeToleranceMS = 5000; + long mOrbitReset = 0; + int mRunNumber = 0; + int mNHBFPerTF = 32; + int mMaxWarnings = 0; + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mCCDBApi; +}; +} // namespace o2::rct + +// ------------------------------------------------------------------ +#include "Framework/runDataProcessing.h" +#include "Framework/DataProcessorSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + std::vector inputs{{"ctfdone", "CTF", "DONE", 0, Lifetime::Timeframe}}; + std::vector outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "RCTUPD_DUMMY"}, Lifetime::Sporadic); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "RCTUPD_DUMMY"}, Lifetime::Sporadic); + auto ggRequest = std::make_shared(true, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs, + true); // query only once all objects except mag.field + specs.push_back(DataProcessorSpec{ + "rct-updater", + inputs, + outputs, + AlgorithmSpec{adaptFromTask(ggRequest)}, + Options{ + {"update-interval", VariantType::Float, 1.f, {"update every ... seconds"}}, + {"max-diff-orbit-creationtime", VariantType::Int, -1, {"max difference between TF creation-time and orbit-time to discard TF, do not check if negative"}}, + {"max-warn-tf-discard", VariantType::Int, 10, {"max N warnings about discarding bad TFs"}}, + {"ccdb-server", VariantType::String, "http://ccdb-test.cern.ch:8080", {"CCDB to update"}}}}); + return specs; +} diff --git a/Detectors/GlobalTracking/CMakeLists.txt b/Detectors/GlobalTracking/CMakeLists.txt index 816af91470d52..ecf77ea741e21 100644 --- a/Detectors/GlobalTracking/CMakeLists.txt +++ b/Detectors/GlobalTracking/CMakeLists.txt @@ -8,46 +8,52 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +#add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) o2_add_library(GlobalTracking TARGETVARNAME targetName SOURCES src/MatchTPCITS.cxx src/MatchTOF.cxx + src/MatchHMP.cxx src/MatchTPCITSParams.cxx src/MatchCosmics.cxx src/MatchCosmicsParams.cxx src/MatchGlobalFwd.cxx src/MatchGlobalFwdAssessment.cxx - src/MatchITSTPCQC.cxx - src/ITSTPCMatchingQCParams.cxx src/MatchGlobalFwdParam.cxx src/MatchTOFParams.cxx - PUBLIC_LINK_LIBRARIES O2::Framework - O2::DataFormatsTPC - O2::DataFormatsITSMFT - O2::DataFormatsITS - O2::DataFormatsFT0 - O2::DataFormatsTOF - O2::DataFormatsTRD - O2::ITSReconstruction - O2::FT0Reconstruction - O2::TPCFastTransformation - O2::GPUO2Interface - O2::TPCBase - O2::TPCReconstruction - O2::TPCCalibration - O2::TOFBase - O2::TOFCalibration - O2::TOFWorkflowUtils - O2::SimConfig - O2::DataFormatsFT0 - O2::DataFormatsGlobalTracking - O2::ITStracking - O2::MFTTracking - O2::MCHTracking - O2::MathUtils - O2::ReconstructionDataFormats - O2::Steer) + src/MatchITSTPCQC.cxx + src/ITSTPCMatchingQCParams.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsTPC + O2::DataFormatsITSMFT + O2::DataFormatsITS + O2::DataFormatsFT0 + O2::DataFormatsTOF + O2::DataFormatsHMP + O2::DataFormatsTRD + O2::ITSReconstruction + O2::FT0Reconstruction + O2::TPCFastTransformation + O2::GPUO2Interface + O2::GPUTracking + O2::TPCBase + O2::TPCReconstruction + O2::TPCCalibration + O2::TOFBase + O2::HMPIDReconstruction + O2::TOFCalibration + O2::TOFWorkflowUtils + O2::SimConfig + O2::DataFormatsFT0 + O2::DataFormatsGlobalTracking + O2::ITStracking + O2::MFTTracking + O2::MCHTracking + O2::MathUtils + O2::ReconstructionDataFormats + O2::Steer + $<$:O2::ITS3Reconstruction>) o2_target_root_dictionary(GlobalTracking HEADERS include/GlobalTracking/MatchTPCITSParams.h @@ -55,12 +61,13 @@ o2_target_root_dictionary(GlobalTracking include/GlobalTracking/MatchGlobalFwdParam.h include/GlobalTracking/MatchGlobalFwdAssessment.h include/GlobalTracking/MatchTOF.h + include/GlobalTracking/MatchHMP.h include/GlobalTracking/MatchCosmics.h include/GlobalTracking/MatchCosmicsParams.h - include/GlobalTracking/MatchITSTPCQC.h - include/GlobalTracking/ITSTPCMatchingQCParams.h include/GlobalTracking/TrackMethods.h - include/GlobalTracking/TrackCuts.h) + include/GlobalTracking/TrackCuts.h + include/GlobalTracking/MatchITSTPCQC.h + include/GlobalTracking/ITSTPCMatchingQCParams.h) if (OpenMP_CXX_FOUND) diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmics.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmics.h index 7720d75590475..896351f7baa06 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmics.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmics.h @@ -26,7 +26,6 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "GlobalTracking/MatchCosmicsParams.h" #include "CommonUtils/TreeStreamRedirector.h" -#include "TPCFastTransform.h" #define _ALLOW_DEBUG_TREES_COSM // to allow debug and control tree output @@ -38,7 +37,7 @@ class VDriftCorrFact; } namespace gpu { -class CorrectionMapsHelper; +class TPCFastTransformPOD; } namespace globaltracking { @@ -84,13 +83,16 @@ class MatchCosmics TBracket tBracket; ///< bracketing time-bins GTrackID origID; ///< track origin id int matchID = MinusOne; ///< entry (none if MinusOne) of its match in the vector of matches + short vtIDMin = -1; ///< id of the 1st compatible vertex + short vtIDMax = -1; ///< id of the last compatible vertex }; - void setTPCCorrMaps(o2::gpu::CorrectionMapsHelper* maph); + void setTPCCorrMaps(const o2::gpu::TPCFastTransformPOD* maph); void setTPCVDrift(const o2::tpc::VDriftCorrFact& v); void setITSROFrameLengthMUS(float fums) { mITSROFrameLengthMUS = fums; } void setITSDict(const o2::itsmft::TopologyDictionary* dict) { mITSDict = dict; } void process(const o2::globaltracking::RecoContainer& data); void setUseMC(bool mc) { mUseMC = mc; } + void setUsePVInfo(bool v) { mUsePVInfo = v; } void init(); void end(); @@ -138,15 +140,16 @@ class MatchCosmics std::vector mRecords; std::vector mWinners; const o2::itsmft::TopologyDictionary* mITSDict = nullptr; // cluster patterns dictionary - o2::gpu::CorrectionMapsHelper* mTPCCorrMapsHelper = nullptr; + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps = nullptr; int mTFCount = 0; - float mTPCVDriftRef = -1.; ///< TPC nominal drift speed in cm/microseconds - float mTPCVDriftCorrFact = 1.; ///< TPC nominal correction factort (wrt ref) - float mTPCVDrift = -1.; ///< TPC drift speed in cm/microseconds + float mTPCVDriftRef = -1.; ///< TPC nominal drift speed in cm/microseconds + float mTPCVDriftCorrFact = 1.; ///< TPC nominal correction factort (wrt ref) + float mTPCVDrift = -1.; ///< TPC drift speed in cm/microseconds float mTPCDriftTimeOffset = 0.; ///< drift time offset in mus - float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds - float mBz = 0; ///< nominal Bz + float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds + float mBz = 0; ///< nominal Bz bool mFieldON = true; + bool mUsePVInfo = false; bool mUseMC = true; float mITSROFrameLengthMUS = 0.; float mQ2PtCutoff = 1e9; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmicsParams.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmicsParams.h index 402b3488193fe..4b34135d83693 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmicsParams.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmicsParams.h @@ -17,6 +17,7 @@ #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" #include "DetectorsBase/Propagator.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" namespace o2 { @@ -24,14 +25,20 @@ namespace globaltracking { struct MatchCosmicsParams : public o2::conf::ConfigurableParamHelper { + float dcaCutChi2[o2::dataformats::GlobalTrackID::NSources] = {}; // optional (>0) chi2 cut on track DCA to any of its compatible vertices float systSigma2[o2::track::kNParams] = {0.01f, 0.01f, 1e-4f, 1e-4f, 0.f}; // extra error to be added at legs comparison float crudeNSigma2Cut[o2::track::kNParams] = {49.f, 49.f, 49.f, 49.f, 49.f}; - float crudeChi2Cut = 999.; - float timeToleranceMUS = 0.; - float maxStep = 10.; - float maxSnp = 0.99; - float minSeedPt = 0.10; // use only tracks above this pT (scaled with field) - float nSigmaTError = 4.; // number of sigmas on track time error for matching (except for TPC which provides an interval) + float crudeChi2Cut = 999.f; + float timeToleranceMUS = 0.f; + float maxStep = 10.f; + float maxSnp = 0.99f; + float minSeedPt = 0.10f; // use only tracks above this pT (scaled with field) + float nSigmaTError = 4.f; // number of sigmas on track time error for matching (except for TPC which provides an interval) + float tpcExtraZError2 = 1.f; // extra error^2 on the TPC-only track Z coordinate + float fiducialRIP = 1.0f; // consider track having |Y@x=0|< this as passing DCA cut (if requested) + float fiducialZIP = 20.f; // consider track having |Z@x=0|< this as passing DCA cut (if requested) + bool allowTPCOnly = true; + bool discardPVContributors = true; // used only if the global option --use-pv-info is requested o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; O2ParamDef(MatchCosmicsParams, "cosmicsMatch"); diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h index ef045adc5bd6f..3445123385cfa 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h @@ -134,12 +134,20 @@ class MatchGlobalFwd void setMFTROFrameLengthMUS(float fums); ///< set MFT ROFrame duration in BC (continuous mode only) void setMFTROFrameLengthInBC(int nbc); + ///< set MFT ROFrame bias in BC (continuous mode only) or time shift applied already as MFTAlpideParam.roFrameBiasInBC + void setMFTROFrameBiasInBC(int nbc); + const std::vector& getMatchedFwdTracks() const { return mMatchedTracks; } const std::vector& getMFTMatchingPlaneParams() const { return mMFTMatchPlaneParams; } const std::vector& getMCHMatchingPlaneParams() const { return mMCHMatchPlaneParams; } const std::vector& getMFTMCHMatchInfo() const { return mMatchingInfo; } const std::vector& getMatchLabels() const { return mMatchLabels; } + /// Converts mchTrack parameters to Forward coordinate system + o2::dataformats::GlobalFwdTrack MCHtoFwd(const o2::mch::TrackParam& mchTrack); + /// Converts FwdTrack parameters to MCH coordinate system + o2::mch::TrackParam FwdtoMCH(const o2::dataformats::GlobalFwdTrack& fwdtrack); + private: void updateTimeDependentParams(); void fillBuiltinFunctions(); @@ -277,9 +285,6 @@ class MatchGlobalFwd return true; } - /// Converts mchTrack parameters to Forward coordinate system - o2::dataformats::GlobalFwdTrack MCHtoFwd(const o2::mch::TrackParam& mchTrack); - float mBz = -5.f; ///< nominal Bz in kGauss float mMatchingPlaneZ = sLastMFTPlaneZ; ///< MCH-MFT matching plane Z position Float_t mMFTDiskThicknessInX0 = 0.042 / 5; ///< MFT disk thickness in radiation length @@ -288,6 +293,9 @@ class MatchGlobalFwd int mMFTROFrameLengthInBC = 0; ///< MFT RO frame in BC (for MFT cont. mode only) float mMFTROFrameLengthMUS = -1.; ///< MFT RO frame in \mus float mMFTROFrameLengthMUSInv = -1.; ///< MFT RO frame in \mus inverse + int mMFTROFrameBiasInBC = 0; ///< MFT ROF bias in BC wrt to orbit start + float mMFTROFrameBiasMUS = -1.; ///< MFT ROF bias in \mus + float mMFTROFrameBiasMUSInv = -1.; ///< MFT ROF bias in \mus inverse std::map mMatchingFunctionMap; ///< MFT-MCH Matching function std::map mCutFunctionMap; ///< MFT-MCH Candidate cut function @@ -315,6 +323,7 @@ class MatchGlobalFwd std::vector mMCHROFTimes; ///< min/max times of MCH ROFs in \mus std::vector mMCHWork; ///< MCH track params prepared for matching + std::vector mMCHID2Work; ///< MCH track id to ensure correct indexing for matching std::vector mMFTROFTimes; ///< min/max times of MFT ROFs in \mus std::vector mMFTWork; ///< MFT track params prepared for matching std::vector mMFTClusters; ///< input MFT clusters @@ -324,11 +333,15 @@ class MatchGlobalFwd std::vector mMFTMatchPlaneParams; ///< MFT track parameters at matching plane std::vector mMCHMatchPlaneParams; ///< MCH track parameters at matching plane + std::map>> mCandidates; ///< map each MCH track id to vector of best match candidates + const o2::itsmft::TopologyDictionary* mMFTDict{nullptr}; // cluster patterns dictionary o2::itsmft::ChipMappingMFT mMFTMapping; bool mMCTruthON = false; ///< Flag availability of MC truth bool mUseMIDMCHMatch = false; ///< Flag for using MCHMID matches (TrackMCHMID) - int mSaveMode = 0; ///< Output mode [0 = SaveBestMatch; 1 = SaveAllMatches; 2 = SaveTrainingData] + bool mUseTrackTime = false; ///< Flag for using the MCH or MCHMID track time information to select the MFT ROF(s) + int mSaveMode = 0; ///< Output mode [0 = SaveBestMatch; 1 = SaveAllMatches; 2 = SaveTrainingData; 3 = SaveNCandidates] + int mNCandidates = 5; ///< Numbers of matching candidates to save in savemode=3 MatchingType mMatchingType = MATCHINGUNDEFINED; TGeoManager* mGeoManager; }; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdAssessment.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdAssessment.h index b64fc7cd0cf16..94afdfcf8bcff 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdAssessment.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdAssessment.h @@ -133,7 +133,7 @@ class GloFwdAssessment std::array, 7> mTrackPhiNCls = {nullptr}; std::array, 7> mTrackXYNCls = {nullptr}; std::array, 7> mTrackEtaPhiNCls = {nullptr}; - std::unique_ptr mTrackTanl = nullptr; + std::unique_ptr mTrackCotl = nullptr; // Histos and data for MC analysis std::vector mNameOfTrackTypes = {"Rec", @@ -176,7 +176,7 @@ class GloFwdAssessment kTH3GMTrackXPullPtEta, kTH3GMTrackYPullPtEta, kTH3GMTrackPhiPullPtEta, - kTH3GMTrackTanlPullPtEta, + kTH3GMTrackCotlPullPtEta, kTH3GMTrackInvQPtPullPtEta, kTH3GMTrackReducedChi2PtEta, kTH3GMTrackPtEtaChi2, @@ -200,7 +200,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, "TH3GMTrackXPullPtEta"}, {kTH3GMTrackYPullPtEta, "TH3GMTrackYPullPtEta"}, {kTH3GMTrackPhiPullPtEta, "TH3GMTrackPhiPullPtEta"}, - {kTH3GMTrackTanlPullPtEta, "TH3GMTrackTanlPullPtEta"}, + {kTH3GMTrackCotlPullPtEta, "TH3GMTrackCotlPullPtEta"}, {kTH3GMTrackInvQPtPullPtEta, "TH3GMTrackInvQPtPullPtEta"}, {kTH3GMTrackReducedChi2PtEta, "TH3GMTrackReducedChi2PtEta"}, {kTH3GMCloseMatchPtEtaChi2, "TH3GMCloseMatchPtEtaChi2"}, @@ -222,7 +222,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, "TH3GMTrackXPullPtEta"}, {kTH3GMTrackYPullPtEta, "TH3GMTrackYPullPtEta"}, {kTH3GMTrackPhiPullPtEta, "TH3GMTrackPhiPullPtEta"}, - {kTH3GMTrackTanlPullPtEta, "TH3GMTrackTanlPullPtEta"}, + {kTH3GMTrackCotlPullPtEta, "TH3GMTrackCotlPullPtEta"}, {kTH3GMTrackInvQPtPullPtEta, "TH3GMTrackInvQPtPullPtEta"}, {kTH3GMTrackReducedChi2PtEta, "TH3GMTrackReducedChi2PtEta"}, {kTH3GMCloseMatchPtEtaChi2, "TH3GMCloseMatchPtEtaChi2"}, @@ -244,7 +244,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -10, 10}}, {kTH3GMTrackYPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -10, 10}}, {kTH3GMTrackPhiPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -10, 10}}, - {kTH3GMTrackTanlPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -10, 10}}, + {kTH3GMTrackCotlPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -10, 10}}, {kTH3GMTrackInvQPtPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -50, 50}}, {kTH3GMTrackReducedChi2PtEta, {40, 0, 20, 16, 2.2, 3.8, 1000, 0, 100}}, {kTH3GMCloseMatchPtEtaChi2, {40, 0, 20, 16, 2.2, 3.8, 1000, 0, 100}}, @@ -266,7 +266,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, R"(p_{t}_{MC})"}, {kTH3GMTrackYPullPtEta, R"(p_{t}_{MC})"}, {kTH3GMTrackPhiPullPtEta, R"(p_{t}_{MC})"}, - {kTH3GMTrackTanlPullPtEta, R"(p_{t}_{MC})"}, + {kTH3GMTrackCotlPullPtEta, R"(p_{t}_{MC})"}, {kTH3GMTrackInvQPtPullPtEta, R"(p_{t}_{MC})"}, {kTH3GMTrackReducedChi2PtEta, R"(p_{t}_{MC})"}, {kTH3GMCloseMatchPtEtaChi2, R"(p_{t}_{Fit})"}, @@ -288,7 +288,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, R"(\eta_{MC})"}, {kTH3GMTrackYPullPtEta, R"(\eta_{MC})"}, {kTH3GMTrackPhiPullPtEta, R"(\eta_{MC})"}, - {kTH3GMTrackTanlPullPtEta, R"(\eta_{MC})"}, + {kTH3GMTrackCotlPullPtEta, R"(\eta_{MC})"}, {kTH3GMTrackInvQPtPullPtEta, R"(\eta_{MC})"}, {kTH3GMTrackReducedChi2PtEta, R"(\eta_{MC})"}, {kTH3GMCloseMatchPtEtaChi2, R"(\eta_{Fit})"}, @@ -310,7 +310,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, R"(\Delta X/\sigma_{X})"}, {kTH3GMTrackYPullPtEta, R"(\Delta Y/\sigma_{Y})"}, {kTH3GMTrackPhiPullPtEta, R"(\Delta \phi/\sigma_{\phi})"}, - {kTH3GMTrackTanlPullPtEta, R"(\Delta \tan(\lambda)/\sigma_{tan(\lambda)})"}, + {kTH3GMTrackCotlPullPtEta, R"(\Delta \cot(\lambda)/\sigma_{cot(\lambda)})"}, {kTH3GMTrackInvQPtPullPtEta, R"((\Delta q/p_t)/\sigma_{q/p_{t}})"}, {kTH3GMTrackReducedChi2PtEta, R"(\chi^2/d.f.)"}, {kTH3GMCloseMatchPtEtaChi2, R"(Match \chi^2)"}, @@ -337,8 +337,8 @@ class GloFwdAssessment kInvQPtResMCHVsPt, kPhiPullVsEta, kPhiPullVsPt, - kTanlPullVsEta, - kTanlPullVsPt, + kCotlPullVsEta, + kCotlPullVsPt, kInvQPtPullVsEta, kInvQPtPullVsPt, kNSlicedTH3 @@ -359,8 +359,8 @@ class GloFwdAssessment {kInvQPtResMCHVsPt, "InvQPtResMCHVsPt"}, {kPhiPullVsEta, "PhiPullVsEta"}, {kPhiPullVsPt, "PhiPullVsPt"}, - {kTanlPullVsEta, "TanlPullVsEta"}, - {kTanlPullVsPt, "TanlPullVsPt"}, + {kCotlPullVsEta, "CotlPullVsEta"}, + {kCotlPullVsPt, "CotlPullVsPt"}, {kInvQPtPullVsEta, "InvQPtPullVsEta"}, {kInvQPtPullVsPt, "InvQPtPullVsPt"}}; @@ -379,8 +379,8 @@ class GloFwdAssessment {kInvQPtResMCHVsPt, kTH3GMTrackInvQPtResMCHPtEta}, {kPhiPullVsEta, kTH3GMTrackPhiPullPtEta}, {kPhiPullVsPt, kTH3GMTrackPhiPullPtEta}, - {kTanlPullVsEta, kTH3GMTrackTanlPullPtEta}, - {kTanlPullVsPt, kTH3GMTrackTanlPullPtEta}, + {kCotlPullVsEta, kTH3GMTrackCotlPullPtEta}, + {kCotlPullVsPt, kTH3GMTrackCotlPullPtEta}, {kInvQPtPullVsEta, kTH3GMTrackInvQPtPullPtEta}, {kInvQPtPullVsPt, kTH3GMTrackInvQPtPullPtEta}}; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdParam.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdParam.h index 9ba6563cec334..757d74ff40780 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdParam.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdParam.h @@ -26,7 +26,8 @@ namespace globaltracking enum SaveMode { kBestMatch = 0, kSaveAll, - kSaveTrainingData }; + kSaveTrainingData, + kSaveNCandidates }; struct GlobalFwdMatchingParam : public o2::conf::ConfigurableParamHelper { @@ -38,9 +39,11 @@ struct GlobalFwdMatchingParam : public o2::conf::ConfigurableParamHelper +#include +#include +#include +#include +#include +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/TrackTPCTOF.h" +#include "ReconstructionDataFormats/MatchInfoTOFReco.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "CommonDataFormat/EvIndex.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "CommonUtils/TreeStreamRedirector.h" + +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DetectorsBase/Propagator.h" +#include "MathUtils/Cartesian.h" +#include "MathUtils/Utils.h" +#include "CommonConstants/MathConstants.h" +#include "CommonConstants/PhysicsConstants.h" +#include "CommonConstants/GeomConstants.h" +#include "DetectorsBase/GeometryManager.h" + +#include "DataFormatsHMP/Cluster.h" +#include "GlobalTracking/MatchTPCITS.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsTRD/TrackTRD.h" +#include "ReconstructionDataFormats/PID.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "ReconstructionDataFormats/MatchInfoHMP.h" +#include "ReconstructionDataFormats/TrackHMP.h" + +#include "HMPIDBase/Geo.h" +#include "DataFormatsHMP/Cluster.h" +#include "DataFormatsHMP/Trigger.h" + +namespace o2 +{ + +namespace globaltracking +{ +class RecoContainer; +} + +namespace dataformats +{ +template +class MCTruthContainer; +} + +namespace globaltracking +{ + +class MatchHMP +{ + + using Geo = o2::hmpid::Geo; + using Cluster = o2::hmpid::Cluster; + using Trigger = o2::hmpid::Trigger; + using evGIdx = o2::dataformats::EvIndex; + using evIdx = o2::dataformats::EvIndex; + using timeEst = o2::dataformats::TimeStampWithError; + using matchTrack = std::pair; + + public: + ///< perform matching for provided input + void run(const o2::globaltracking::RecoContainer& inp); + + ///< print settings + void print() const; + void printCandidatesHMP() const; + + ///< set extra time tolerance + void setExtraTimeToleranceTRD(float val) { mExtraTimeToleranceTRD = val; } + ///< get extra tolerance + float getExtraTimeToleranceTRD() const { return mExtraTimeToleranceTRD; } + + ///< set extra time tolerance + void setExtraTimeToleranceTOF(float val) { mExtraTimeToleranceTOF = val; } + ///< get extra tolerance + float getExtraTimeToleranceTOF() const { return mExtraTimeToleranceTOF; } + + /* enum DebugFlagTypes : UInt_t { + MatchTreeAll = 0x1 << 1, // ///< produce matching candidates tree for all candidates + }; + */ + enum trackType : int8_t { UNCONS = 0, + CONSTR, + SIZE, + TPC = 0, + ITSTPC, + TPCTRD, + ITSTPCTRD, + SIZEALL }; + + std::vector& getMatchedTrackVector(o2::globaltracking::MatchHMP::trackType index) { return mMatchedTracks[index]; } + + std::vector& getMatchedHMPLabelsVector(o2::globaltracking::MatchHMP::trackType index) { return mOutHMPLabels[index]; } ///< get vector of HMP label of matched tracks + + void setTS(unsigned long creationTime) + { + mTimestamp = creationTime; + } + unsigned long getTS() const { return mTimestamp; } + + private: + // bool prepareFITData(); + int prepareInteractionTimes(); + bool prepareTracks(); + bool prepareHMPClusters(); + void doFastMatching(); + void doMatching(); + + static int intTrkCha(o2::track::TrackParCov* pTrk, double& xPc, double& yPc, double& xRa, double& yRa, double& theta, double& phi, double bz); // find track-PC intersection, retuns chamber ID + static int intTrkCha(int ch, o2::dataformats::TrackHMP* pHmpTrk, double& xPc, double& yPc, double& xRa, double& yRa, double& theta, double& phi, double bz); // find track-PC intersection, retuns chamber ID + + bool intersect(Double_t pnt[3], Double_t norm[3]) const; + + void addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); + void addITSTPCSeed(const o2::dataformats::TrackTPCITS& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); + void addTRDSeed(const o2::trd::TrackTRD& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); + void addTPCTOFSeed(const o2::dataformats::TrackTPCTOF& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); + void addConstrainedSeed(o2::track::TrackParCov& trc, o2::dataformats::GlobalTrackID srcGID, timeEst timeMUS); + + // Data members + const o2::globaltracking::RecoContainer* mRecoCont = nullptr; + + o2::InteractionRecord mStartIR{0, 0}; ///< IR corresponding to the start of the TF + + // for derived class + int mCurrTracksTreeEntry = 0; ///< current tracks tree entry loaded to memory + + bool mMCTruthON = false; ///< flag availability of MC truth + + ///========== Parameters to be set externally, e.g. from CCDB ==================== + float mBz = 0; ///< nominal Bz + float mMaxInvPt = 999.; ///< derived from nominal Bz + + // to be done later + float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds + float mTPCTBinMUSInv = 0.; ///< inverse TPC time bin duration in microseconds + float mTPCBin2Z = 0.; ///< conversion coeff from TPC time-bin to Z + // float mTimeTolerance = 1e3; ///< tolerance in ns for track-TOF time bracket matching + float mExtraTimeToleranceTRD = 0.; ///< extra tolerance in ns for track-TOF time bracket matching + float mExtraTimeToleranceTOF = 0.; ///< extra tolerance in ns for track-TOF time bracket matching + float mSigmaTimeCut = 3.; ///< number of sigmas to cut on time when matching the track to the TOF cluster + + static constexpr Double_t BC_TIME = o2::constants::lhc::LHCBunchSpacingNS; // bunch crossing in ns + static constexpr Double_t BC_TIME_INV = 1. / BC_TIME; // inv bunch crossing in ns + static constexpr Double_t BC_TIME_INPS = BC_TIME * 1000; // bunch crossing in ps + static constexpr Double_t BC_TIME_INPS_INV = 1. / BC_TIME_INPS; // inv bunch crossing in ps + + bool mIsFIT = false; + bool mIsTPCused = false; + bool mIsITSTPCused = false; + bool mIsTPCTOFused = false; + bool mIsTPCTRDused = false; + bool mIsITSTPCTOFused = false; + bool mIsTPCTRDTOFused = false; + bool mIsITSTPCTRDused = false; + bool mIsITSTPCTRDTOFused = false; + bool mSetHighPurity = false; + + float mTPCVDrift = -1.; ///< TPC drift speed in cm/microseconds + + unsigned long mTimestamp = 0; ///< in ms + + ///>>>------ these are input arrays which should not be modified by the matching code + // since this info is provided by external device + gsl::span mHMPClustersArray; ///< input HMPID clusters + gsl::span mHMPTriggersArray; ///< input HMPID triggers + + const o2::dataformats::MCTruthContainer* mHMPClusLabels; ///< input HMP clusters MC labels (pointer to read from tree) + + ///< working copy of the input tracks + std::vector mTracksWork[o2::globaltracking::MatchHMP::trackType::SIZE]; ///< track params prepared for matching + time value + std::vector mHMPTriggersWork; + std::vector mTracksLblWork[o2::globaltracking::MatchHMP::trackType::SIZE]; ///< track labels + + std::vector mTracksIndexCache[o2::globaltracking::MatchHMP::trackType::SIZE]; ///< indices of track entry in mTracksWork + std::vector mHMPTriggersIndexCache; ///< indices of track entry in mHMPTriggersWork + + ///< array of matched HMPCluster with matching information + std::vector mMatchedTracks[o2::globaltracking::MatchHMP::trackType::SIZE]; // this is the output of the matching -> UNCONS, CONSTR + std::vector mOutHMPLabels[o2::globaltracking::MatchHMP::trackType::SIZE]; ///< HMP label of matched tracks + + std::vector mTrackGid[o2::globaltracking::MatchHMP::trackType::SIZE]; ///< expected times and others + std::vector mMatchedTracksIndex[o2::globaltracking::MatchHMP::trackType::SIZE]; // vector of indexes of the tracks to be matched + + int mNumOfTriggers; // number of HMP triggers + + ///----------- aux stuff --------------/// + static constexpr float MAXSNP = 0.85; // max snp of ITS or TPC track at xRef to be matched + + TStopwatch mTimerTot; + TStopwatch mTimerMatchITSTPC; + TStopwatch mTimerMatchTPC; + TStopwatch mTimerDBG; + + ClassDef(MatchHMP, 1); +}; +} // namespace globaltracking +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h index b46ce7aff05dd..983fadcb0092e 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h @@ -26,8 +26,11 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTrack.h" #include "Steer/MCKinematicsReader.h" +#include "ReconstructionDataFormats/PID.h" #include #include +#include +#include namespace o2 { @@ -46,39 +49,63 @@ struct LblInfo { class MatchITSTPCQC { public: + enum matchType : int { TPC = 0, + ITS, + SIZE }; + MatchITSTPCQC() = default; ~MatchITSTPCQC(); bool init(); void initDataRequest(); void run(o2::framework::ProcessingContext& ctx); - void setDataRequest(std::shared_ptr dr) { mDataRequest = dr; } + void setDataRequest(const std::shared_ptr& dr) { mDataRequest = dr; } void finalize(); void reset(); - TH1D* getHistoPt() const { return mPt; } - TH1D* getHistoPtTPC() const { return mPtTPC; } - TEfficiency* getFractionITSTPCmatch() const { return mFractionITSTPCmatch; } + TH1D* getHistoPtNum(matchType m) const { return mPtNum[m]; } + TH1D* getHistoPtDen(matchType m) const { return mPtDen[m]; } + TEfficiency* getFractionITSTPCmatch(matchType m) const { return mFractionITSTPCmatch[m]; } + + TH1D* getHistoPtNumNoEta0(matchType m) const { return mPtNum_noEta0[m]; } + TH1D* getHistoPtDenNoEta0(matchType m) const { return mPtDen_noEta0[m]; } + TEfficiency* getFractionITSTPCmatchNoEta0(matchType m) const { return mFractionITSTPCmatch_noEta0[m]; } + + TH1F* getHistoPhiNum(matchType m) const { return mPhiNum[m]; } + TH1F* getHistoPhiDen(matchType m) const { return mPhiDen[m]; } + TEfficiency* getFractionITSTPCmatchPhi(matchType m) const { return mFractionITSTPCmatchPhi[m]; } + + TH2F* getHistoPhiVsPtNum(matchType m) const { return mPhiVsPtNum[m]; } + TH2F* getHistoPhiVsPtDen(matchType m) const { return mPhiVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchPhiVsPt(matchType m) const { return mFractionITSTPCmatchPhiVsPt[m]; } - TH1F* getHistoPhi() const { return mPhi; } - TH1F* getHistoPhiTPC() const { return mPhiTPC; } - TEfficiency* getFractionITSTPCmatchPhi() const { return mFractionITSTPCmatchPhi; } + TH1F* getHistoEtaNum(matchType m) const { return mEtaNum[m]; } + TH1F* getHistoEtaDen(matchType m) const { return mEtaDen[m]; } + TEfficiency* getFractionITSTPCmatchEta(matchType m) const { return mFractionITSTPCmatchEta[m]; } - TH1F* getHistoEta() const { return mEta; } - TH1F* getHistoEtaTPC() const { return mEtaTPC; } - TEfficiency* getFractionITSTPCmatchEta() const { return mFractionITSTPCmatchEta; } + TH2F* getHistoEtaVsPtNum(matchType m) const { return mEtaVsPtNum[m]; } + TH2F* getHistoEtaVsPtDen(matchType m) const { return mEtaVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchEtaVsPt(matchType m) const { return mFractionITSTPCmatchEtaVsPt[m]; } - TH1F* getHistoPtPhysPrim() const { return mPtPhysPrim; } - TH1F* getHistoPtTPCPhysPrim() const { return mPtTPCPhysPrim; } - TEfficiency* getFractionITSTPCmatchPhysPrim() const { return mFractionITSTPCmatchPhysPrim; } + TH2F* getHistoClsVsPtNum(matchType m) const { return mClsVsPtNum[m]; } + TH2F* getHistoClsVsPtDen(matchType m) const { return mClsVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchClsVsPt(matchType m) const { return mFractionITSTPCmatchClsVsPt[m]; } - TH1F* getHistoPhiPhysPrim() const { return mPhiPhysPrim; } - TH1F* getHistoPhiTPCPhysPrim() const { return mPhiTPCPhysPrim; } - TEfficiency* getFractionITSTPCmatchPhiPhysPrim() const { return mFractionITSTPCmatchPhiPhysPrim; } + TH2F* getHistoChi2VsPtNum(matchType m) const { return mChi2VsPtNum[m]; } + TH2F* getHistoChi2VsPtDen(matchType m) const { return mChi2VsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchChi2VsPt(matchType m) const { return mFractionITSTPCmatchChi2VsPt[m]; } - TH1F* getHistoEtaPhysPrim() const { return mEtaPhysPrim; } - TH1F* getHistoEtaTPCPhysPrim() const { return mEtaTPCPhysPrim; } - TEfficiency* getFractionITSTPCmatchEtaPhysPrim() const { return mFractionITSTPCmatchEtaPhysPrim; } + TH1F* getHistoPtPhysPrimNum(matchType m) const { return mPtPhysPrimNum[m]; } + TH1F* getHistoPtPhysPrimDen(matchType m) const { return mPtPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhysPrim(matchType m) const { return mFractionITSTPCmatchPhysPrim[m]; } + + TH1F* getHistoPhiPhysPrimNum(matchType m) const { return mPhiPhysPrimNum[m]; } + TH1F* getHistoPhiPhysPrimDen(matchType m) const { return mPhiPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhiPhysPrim(matchType m) const { return mFractionITSTPCmatchPhiPhysPrim[m]; } + + TH1F* getHistoEtaPhysPrimNum(matchType m) const { return mEtaPhysPrimNum[m]; } + TH1F* getHistoEtaPhysPrimDen(matchType m) const { return mEtaPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchEtaPhysPrim(matchType m) const { return mFractionITSTPCmatchEtaPhysPrim[m]; } TH2F* getHistoResidualPt() const { return mResidualPt; } TH2F* getHistoResidualPhi() const { return mResidualPhi; } @@ -87,70 +114,219 @@ class MatchITSTPCQC TH1F* getHistoChi2Matching() const { return mChi2Matching; } TH1F* getHistoChi2Refit() const { return mChi2Refit; } TH2F* getHistoTimeResVsPt() const { return mTimeResVsPt; } + TH1F* getHistoDCAr() const { return mDCAr; } + TH2F* getHistoDCArVsPtNum() const { return mDCArVsPtNum; } + TH2F* getHistoDCArVsPtDen() const { return mDCArVsPtDen; } + TEfficiency* getFractionITSTPCmatchDCArVsPt() const { return mFractionITSTPCmatchDCArVsPt; } + + TH1D* getHisto1OverPtNum(matchType m) const { return m1OverPtNum[m]; } + TH1D* getHisto1OverPtDen(matchType m) const { return m1OverPtDen[m]; } + TEfficiency* getFractionITSTPCmatch1OverPt(matchType m) const { return mFractionITSTPCmatch1OverPt[m]; } + + TH1D* getHisto1OverPtPhysPrimNum(matchType m) const { return m1OverPtPhysPrimNum[m]; } + TH1D* getHisto1OverPtPhysPrimDen(matchType m) const { return m1OverPtPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhysPrim1OverPt(matchType m) const { return mFractionITSTPCmatchPhysPrim1OverPt[m]; } + void getHistos(TObjArray& objar); + + /// \brief Publishes the histograms to the publisher e.g. the one provided by the QC task + /// \tparam T type of the publisher + /// \param publisher the publisher e.g. getObjectsManager() + template + void publishHistograms(const std::shared_ptr& publisher) + { + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + publisher->startPublishing(mPtNum[i]); + publisher->startPublishing(mPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatch[i]); + + publisher->startPublishing(mPtNum_noEta0[i]); + publisher->startPublishing(mPtDen_noEta0[i]); + publisher->startPublishing(mFractionITSTPCmatch_noEta0[i]); + + // Phi + publisher->startPublishing(mPhiNum[i]); + publisher->startPublishing(mPhiDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhi[i]); + + publisher->startPublishing(mPhiVsPtNum[i]); + publisher->startPublishing(mPhiVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhiVsPt[i]); + + // Eta + publisher->startPublishing(mEtaNum[i]); + publisher->startPublishing(mEtaDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEta[i]); + + publisher->startPublishing(mEtaVsPtNum[i]); + publisher->startPublishing(mEtaVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEtaVsPt[i]); + + // Clusters + publisher->startPublishing(mClsVsPtNum[i]); + publisher->startPublishing(mClsVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchClsVsPt[i]); + + // Chi2 + publisher->startPublishing(mChi2VsPtNum[i]); + publisher->startPublishing(mChi2VsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchChi2VsPt[i]); + + // 1/pt + publisher->startPublishing(m1OverPtNum[i]); + publisher->startPublishing(m1OverPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatch1OverPt[i]); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + publisher->startPublishing(mPtNumVsTrkPID[i][j]); + publisher->startPublishing(mPtDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchPtVsTrkPID[i][j]); + + // Phi + publisher->startPublishing(mPhiNumVsTrkPID[i][j]); + publisher->startPublishing(mPhiDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchPhiVsTrkPID[i][j]); + + // Eta + publisher->startPublishing(mEtaNumVsTrkPID[i][j]); + publisher->startPublishing(mEtaDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchEtaVsTrkPID[i][j]); + } + } + + if (mUseMC) { + publisher->startPublishing(mPhiPhysPrimNum[i]); + publisher->startPublishing(mPhiPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhiPhysPrim[i]); + publisher->startPublishing(mPtPhysPrimNum[i]); + publisher->startPublishing(mPtPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhysPrim[i]); + publisher->startPublishing(mEtaPhysPrimNum[i]); + publisher->startPublishing(mEtaPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEtaPhysPrim[i]); + publisher->startPublishing(m1OverPtPhysPrimNum[i]); + publisher->startPublishing(m1OverPtPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhysPrim1OverPt[i]); + } + } + publisher->startPublishing(mChi2Matching); + publisher->startPublishing(mChi2Refit); + publisher->startPublishing(mTimeResVsPt); + publisher->startPublishing(mResidualPt); + publisher->startPublishing(mResidualPhi); + publisher->startPublishing(mResidualEta); + publisher->startPublishing(mDCAr); + publisher->startPublishing(mDCArVsPtNum); + publisher->startPublishing(mDCArVsPtDen); + publisher->startPublishing(mFractionITSTPCmatchDCArVsPt); + } + void setSources(GID::mask_t src) { mSrc = src; } + void setUseTrkPID(bool b) { mUseTrkPID = b; } + bool getUseTrkPID() const { return mUseTrkPID; } void setUseMC(bool b) { mUseMC = b; } bool getUseMC() const { return mUseMC; } void deleteHistograms(); - void setGRPFileName(std::string fn) { mGRPFileName = fn; } - void setGeomFileName(std::string fn) { mGeomFileName = fn; } void setBz(float bz) { mBz = bz; } - // track selection - bool selectTrack(o2::tpc::TrackTPC const& track); - void setPtCut(float v) { mPtCut = v; } - void setEtaCut(float v) { mEtaCut = v; } + // ITS track + void setMinPtITSCut(float v) { mPtITSCut = v; }; + void setEtaITSCut(float v) { mEtaITSCut = v; }; // TODO: define 2 different values for min and max (**) + void setMinNClustersITS(float v) { mMinNClustersITS = v; } + void setMaxChi2PerClusterITS(float v) { mMaxChi2PerClusterITS = v; } + // TO DO: define an agreed way to implement the setter for ITS matching (min. # layers, which layers) + // [...] --> exploit the method TrackCuts::setRequireHitsInITSLayers(...) + // TPC track + void setMinPtTPCCut(float v) { mPtTPCCut = v; }; + void setEtaTPCCut(float v) { mEtaTPCCut = v; }; // TODO: define 2 different values for min and max (***) void setMinNTPCClustersCut(float v) { mNTPCClustersCut = v; } void setMinDCAtoBeamPipeCut(std::array v) { setMinDCAtoBeamPipeDistanceCut(v[0]); setMinDCAtoBeamPipeYCut(v[1]); } - void setMinDCAtoBeamPipeDistanceCut(float v) { mDCACut = v; } - void setMinDCAtoBeamPipeYCut(float v) { mDCACutY = v; } + void setMinDCAtoBeamPipeDistanceCut(float v) { mDCATPCCut = v; } + void setMinDCAtoBeamPipeYCut(float v) { mDCATPCCutY = v; } + // ITS-TPC kinematics + void setPtCut(float v) { mPtCut = v; } + void setMaxPtCut(float v) { mPtMaxCut = v; } + void setEtaCut(float v) { mEtaCut = v; } // TODO: define 2 different values for min and max (*) private: std::shared_ptr mDataRequest; o2::globaltracking::RecoContainer mRecoCont; - GID::mask_t mSrc = GID::getSourcesMask("TPC,ITS-TPC"); - GID::mask_t mAllowedSources = GID::getSourcesMask("TPC,ITS-TPC"); + GID::mask_t mSrc = GID::getSourcesMask("ITS,TPC,ITS-TPC"); + GID::mask_t mAllowedSources = GID::getSourcesMask("ITS,TPC,ITS-TPC"); // TPC gsl::span mTPCTracks; + // ITS + gsl::span mITSTracks; // ITS-TPC gsl::span mITSTPCTracks; - bool mUseMC = false; - std::string mGRPFileName = "o2sim_grp.root"; - std::string mGeomFileName = "o2sim_geometry-aligned.root"; - float mBz = 0; ///< nominal Bz - std::unordered_map mMapLabels; // map with labels that have been found for the matched ITSTPC tracks; key is the label, - // value is the LbLinfo with the id of the track with the highest pT found with that label so far, - // and the flag to say if it is a physical primary or not - std::unordered_map mMapTPCLabels; // map with labels that have been found for the unmatched TPC tracks; key is the label, - // value is the LblInfo with the id of the track with the highest number of TPC clusters found - // with that label so far, and the flag to say if it is a physical primary or not - o2::steer::MCKinematicsReader mcReader; // reader of MC information - - // Pt. - TH1D* mPt = nullptr; - TH1D* mPtTPC = nullptr; - TEfficiency* mFractionITSTPCmatch = nullptr; - TH1F* mPtPhysPrim = nullptr; - TH1F* mPtTPCPhysPrim = nullptr; - TEfficiency* mFractionITSTPCmatchPhysPrim = nullptr; + bool mUseMC = false; // Usage of the MC information + bool mUseTrkPID = false; // Usage of the PID hypothesis in tracking + float mBz = 0; ///< nominal Bz + std::array, matchType::SIZE> mMapLabels; // map with labels that have been found for the matched ITSTPC tracks; key is the label, + // value is the LbLinfo with the id of the track with the highest pT found with that label so far, + // and the flag to say if it is a physical primary or not + std::array, matchType::SIZE> mMapRefLabels; // map with labels that have been found for the unmatched TPC tracks; key is the label, + // value is the LblInfo with the id of the track with the highest number of TPC clusters found + // with that label so far, and the flag to say if it is a physical primary or not + o2::steer::MCKinematicsReader mcReader; // reader of MC information + + // Pt + TH1D* mPtNum[matchType::SIZE] = {}; + TH1D* mPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch[matchType::SIZE] = {}; + TH1D* mPtNum_noEta0[matchType::SIZE] = {}; + TH1D* mPtDen_noEta0[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch_noEta0[matchType::SIZE] = {}; + TH1F* mPtPhysPrimNum[matchType::SIZE] = {}; + TH1F* mPtPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhysPrim[matchType::SIZE] = {}; + // Pt split per PID hypothesis in tracking + TH1D* mPtNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mPtDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchPtVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; // Phi - TH1F* mPhi = nullptr; - TH1F* mPhiTPC = nullptr; - TEfficiency* mFractionITSTPCmatchPhi = nullptr; - TH1F* mPhiPhysPrim = nullptr; - TH1F* mPhiTPCPhysPrim = nullptr; - TEfficiency* mFractionITSTPCmatchPhiPhysPrim = nullptr; + TH1F* mPhiNum[matchType::SIZE] = {}; + TH1F* mPhiDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhi[matchType::SIZE] = {}; + TH1F* mPhiPhysPrimNum[matchType::SIZE] = {}; + TH1F* mPhiPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhiPhysPrim[matchType::SIZE] = {}; + TH2F* mPhiVsPtNum[matchType::SIZE] = {}; + TH2F* mPhiVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhiVsPt[matchType::SIZE] = {}; + // Phi split per PID hypothesis in tracking + TH1D* mPhiNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mPhiDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchPhiVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; // Eta - TH1F* mEta = nullptr; - TH1F* mEtaTPC = nullptr; - TEfficiency* mFractionITSTPCmatchEta = nullptr; - TH1F* mEtaPhysPrim = nullptr; - TH1F* mEtaTPCPhysPrim = nullptr; - TEfficiency* mFractionITSTPCmatchEtaPhysPrim = nullptr; + TH1F* mEtaNum[matchType::SIZE] = {}; + TH1F* mEtaDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEta[matchType::SIZE] = {}; + TH1F* mEtaPhysPrimNum[matchType::SIZE] = {}; + TH1F* mEtaPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEtaPhysPrim[matchType::SIZE] = {}; + TH2F* mEtaVsPtNum[matchType::SIZE] = {}; + TH2F* mEtaVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEtaVsPt[matchType::SIZE] = {}; + // Clusters + TH2F* mClsVsPtNum[matchType::SIZE] = {}; + TH2F* mClsVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchClsVsPt[matchType::SIZE] = {}; + // Chi2 + TH2F* mChi2VsPtNum[matchType::SIZE] = {}; + TH2F* mChi2VsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchChi2VsPt[matchType::SIZE] = {}; + // Eta split per PID hypothesis in tracking + TH1D* mEtaNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mEtaDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchEtaVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; // Residuals TH2F* mResidualPt = nullptr; TH2F* mResidualPhi = nullptr; @@ -159,18 +335,44 @@ class MatchITSTPCQC TH1F* mChi2Matching = nullptr; TH1F* mChi2Refit = nullptr; TH2F* mTimeResVsPt = nullptr; + TH1F* mDCAr = nullptr; + TH2F* mDCArVsPtNum = nullptr; + TH2F* mDCArVsPtDen = nullptr; + TEfficiency* mFractionITSTPCmatchDCArVsPt = nullptr; + // 1/Pt + TH1D* m1OverPtNum[matchType::SIZE] = {}; + TH1D* m1OverPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch1OverPt[matchType::SIZE] = {}; + TH1D* m1OverPtPhysPrimNum[matchType::SIZE] = {}; + TH1D* m1OverPtPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhysPrim1OverPt[matchType::SIZE] = {}; + + void setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool is2D = false); int mNTPCSelectedTracks = 0; - int mNITSTPCSelectedTracks = 0; + int mNITSSelectedTracks = 0; + int mNITSTPCSelectedTracks[matchType::SIZE] = {0, 0}; // cut values + // ITS track + float mPtITSCut = 0.f; // min pT for ITS track + float mEtaITSCut = 1e10f; // eta window for ITS track --> TODO: define 2 different values for min and max (**) + int mMinNClustersITS = 0; // min number of ITS clusters + float mMaxChi2PerClusterITS{1e10f}; // max its fit chi2 per ITS cluster + std::vector>> mRequiredITSHits{}; // vector of ITS requirements (minNRequiredHits in specific requiredLayers) + // TPC track + float mPtTPCCut = 0.1f; // min pT for TPC track + float mEtaTPCCut = 1.4f; // eta window for TPC track --> TODO: define 2 different values for min and max (***) + int32_t mNTPCClustersCut = 60; // minimum number of TPC clusters for TPC track + float mDCATPCCut = 100.f; // max DCA 3D to PV for TPC track + float mDCATPCCutY = 10.f; // max DCA xy to PV for TPC track + // ITS-TPC kinematics float mPtCut = 0.1f; - float mEtaCut = 1.4f; - int32_t mNTPCClustersCut = 60; - float mDCACut = 100.f; - float mDCACutY = 10.f; + float mPtMaxCut = 1e10f; + float mEtaCut = 1e10f; // 1e10f as defaults of Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h + // TODO: define 2 different values for min and max (*) - ClassDefNV(MatchITSTPCQC, 1); + ClassDefNV(MatchITSTPCQC, 2); }; } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h index db0abf1233e00..8447fbc42cdce 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h @@ -37,9 +37,7 @@ #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTRD/TrackTRD.h" #include "ReconstructionDataFormats/PID.h" -#include "TPCFastTransform.h" #include "CommonDataFormat/InteractionRecord.h" -#include "CorrectionMapsHelper.h" #include "GlobalTracking/MatchTOFParams.h" // from FIT @@ -90,7 +88,7 @@ class MatchTOF public: ///< perform matching for provided input - void run(const o2::globaltracking::RecoContainer& inp); + void run(const o2::globaltracking::RecoContainer& inp, unsigned long firstTForbit = 0); void setCosmics() { @@ -152,45 +150,41 @@ class MatchTOF std::vector& getMatchedTOFLabelsVector(trkType index) { return mOutTOFLabels[index]; } ///< get vector of TOF labels of matched tracks void setTPCVDrift(const o2::tpc::VDriftCorrFact& v); - void setTPCCorrMaps(o2::gpu::CorrectionMapsHelper* maph); - - ///< set input TPC tracks cluster indices - void setTPCTrackClusIdxInp(const gsl::span inp) - { - mTPCTrackClusIdx = inp; - } - - ///< set input TPC cluster sharing map - void setTPCClustersSharingMap(const gsl::span inp) - { - mTPCRefitterShMap = inp; - } - - ///< set input TPC clusters - void setTPCClustersInp(const o2::tpc::ClusterNativeAccess* inp) - { - mTPCClusterIdxStruct = inp; - } + void setTPCCorrMaps(const o2::gpu::TPCFastTransformPOD* maph, float lumi); void setFIT(bool value = true) { mIsFIT = value; } - static int findFITIndex(int bc, const gsl::span& FITRecPoints); + static int findFITIndex(int bc, const gsl::span& FITRecPoints, unsigned long firstOrbit); void checkRefitter(); bool makeConstrainedTPCTrack(int matchedID, o2::dataformats::TrackTPCTOF& trConstr); ///< populate externally provided container by TOF-time-constrained TPC tracks - template - void makeConstrainedTPCTracks(V& container) + template + void makeConstrainedTPCTracks(MtcInfo& mtcCont, MCInfo& MCCont, CTrack& trcCont) { + auto nmatch = getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC).size(); // preliminary matches + mtcCont.reserve(nmatch); + trcCont.reserve(nmatch); + if (mMCTruthON) { + MCCont.reserve(nmatch); + } checkRefitter(); - int nmatched = mMatchedTracks[trkType::TPC].size(), nconstrained = 0; - container.resize(nmatched); - for (unsigned i = 0; i < nmatched; i++) { - if (makeConstrainedTPCTrack(i, container[nconstrained])) { + + auto& mclabs = getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC); + auto& info = getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC); + int nconstrained = 0; + for (unsigned i = 0; i < nmatch; i++) { + auto& ctr = trcCont.emplace_back(); + if (makeConstrainedTPCTrack(i, ctr)) { + mtcCont.push_back(info[i]); + if (mMCTruthON) { + MCCont.push_back(mclabs[i]); + } nconstrained++; + } else { + trcCont.pop_back(); } } - container.resize(nconstrained); } void setTS(unsigned long creationTime) { mTimestamp = creationTime; } @@ -201,10 +195,14 @@ class MatchTOF void storeMatchable(bool val = true) { mStoreMatchable = val; } + void setNlanes(int lanes) { mNlanes = lanes; } + private: bool prepareFITData(); int prepareInteractionTimes(); bool prepareTPCData(); + void propagateTPCTracks(int sec); + void propagateConstrTracks(int sec); void addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); void addITSTPCSeed(const o2::dataformats::TrackTPCITS& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); void addTRDSeed(const o2::trd::TrackTRD& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); @@ -215,20 +213,27 @@ class MatchTOF void doMatching(int sec); void doMatchingForTPC(int sec); - void selectBestMatches(); - static void BestMatches(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, const std::vector* TracksWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels, float calibMaxChi2); - static void BestMatchesHP(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels); + void selectBestMatches(int sec); + void BestMatches(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, const std::vector* TracksWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels, float calibMaxChi2); + void BestMatchesHP(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels); bool propagateToRefX(o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, o2::track::TrackLTIntegral& intLT); - bool propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, float bz); + bool propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, float bz); + bool propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, float bz, float pos[3]); + void updateTL(o2::track::TrackLTIntegral& intLT, float deltal); void updateTimeDependentParams(); + static bool mHasFillScheme; + static bool mFillScheme[o2::constants::lhc::LHCMaxBunches]; + //================================================================ // Data members const o2::globaltracking::RecoContainer* mRecoCont = nullptr; o2::InteractionRecord mStartIR{0, 0}; ///< IR corresponding to the start of the TF + int mNlanes = 3; ///< for multi-threading in matching + // TOF matching params (work in progress) const MatchTOFParams* mMatchParams = nullptr; @@ -269,6 +274,8 @@ class MatchTOF unsigned long mTimestamp = 0; ///< in ms + unsigned long mFirstTForbit = 0; ///< First orbit in TF (needed to align FT0 recpoints) + // from ruben gsl::span mTPCTracksArray; ///< input TPC tracks span @@ -280,9 +287,11 @@ class MatchTOF /// data needed for refit of time-constrained TPC tracks gsl::span mTPCTrackClusIdx; ///< input TPC track cluster indices span gsl::span mTPCRefitterShMap; ///< externally set TPC clusters sharing map + gsl::span mTPCRefitterOccMap; ///< externally set TPC clusters occupancy map const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices - o2::gpu::CorrectionMapsHelper* mTPCCorrMapsHelper = nullptr; ///< TPC cluster transformation + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps = nullptr; ///< TPC cluster transformation + float mCTPLumi = {-1}; std::unique_ptr mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction const o2::dataformats::MCTruthContainer* mTOFClusLabels; ///< input TOF clusters MC labels (pointer to read from tree) @@ -294,22 +303,23 @@ class MatchTOF /// <<<----- /// mTracksWork[trkType::SIZE]; /// mTracksLblWork[trkType::SIZE]; /// mLTinfos[trkType::SIZE]; /// mTrackGid[trkType::SIZE]; /// mTracksWork[o2::constants::math::NSectors][trkType::SIZE]; ///< track params prepared for matching + time value + std::vector mTracksLblWork[o2::constants::math::NSectors][trkType::SIZE]; ///< TPCITS track labels + std::vector mLTinfos[o2::constants::math::NSectors][trkType::SIZE]; ///< expected times and others + std::vector mTrackGid[o2::constants::math::NSectors][trkType::SIZE]; ///< expected times and others ///< per sector indices of track entry in mTracksWork std::array, o2::constants::math::NSectors> mTracksSectIndexCache[trkType::SIZE]; + std::array, o2::constants::math::NSectors> mTracksSeed[trkType::SIZE]; + std::vector mVZtpcOnly[o2::constants::math::NSectors]; - std::vector mExtraTPCFwdTime; /// mTOFClusWork; /// mSideTPC; /// mExtraTPCFwdTime[o2::constants::math::NSectors]; ///< track extra params for TPC tracks: Fws Max time + std::vector mTOFClusWork; ///< track params prepared for matching + std::vector mSideTPC[o2::constants::math::NSectors]; ///< track side for TPC tracks ///< per sector indices of TOF cluster entry in mTOFClusWork std::array, o2::constants::math::NSectors> mTOFClusSectIndexCache; - /// mMatchedTracksPairs; + ///< array of track-TOFCluster pairs from the matching std::vector mMatchedTracksPairsSec[o2::constants::math::NSectors]; /// mMatchedTracks[trkType::SIZEALL]; // this is the output of the matching -> UNCONS, CONSTR std::vector mOutTOFLabels[trkType::SIZEALL]; ///< TOF label of matched tracks - std::vector mMatchedTracksIndex[trkType::SIZE]; // vector of indexes of the tracks to be matched + std::vector mMatchedTracksIndex[o2::constants::math::NSectors][trkType::SIZE]; // vector of indexes of the tracks to be matched int mNumOfClusters; // number of clusters to be matched int* mMatchedClustersIndex = nullptr; //[mNumOfClusters] @@ -329,6 +339,9 @@ class MatchTOF UInt_t mDBGFlags = 0; std::string mDebugTreeFileName = "dbg_matchTOF.root"; ///< name for the debug tree file + std::array mCovDiagInner{}; ///< total cov.matrix extra diagonal error from TrackTuneParams + std::array mCovDiagOuter{}; ///< total cov.matrix extra diagonal error from TrackTuneParams + ///----------- aux stuff --------------/// static constexpr float MAXSNP = 0.85; // max snp of ITS or TPC track at xRef to be matched @@ -336,7 +349,7 @@ class MatchTOF TStopwatch mTimerMatchITSTPC; TStopwatch mTimerMatchTPC; TStopwatch mTimerDBG; - ClassDefNV(MatchTOF, 5); + ClassDefNV(MatchTOF, 6); }; } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOFParams.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOFParams.h index 503f96ec36f19..6ed36a6e18450 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOFParams.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOFParams.h @@ -23,8 +23,12 @@ namespace globaltracking { struct MatchTOFParams : public o2::conf::ConfigurableParamHelper { - float calibMaxChi2 = 3.0; - float nsigmaTimeCut = 4.; // number of sigmas for non-TPC track time resolution to consider + float calibMaxChi2 = 3.0; // max value of Chi2 allowed for using matched tracks in calibration + float nsigmaTimeCut = 4.; // number of sigmas for non-TPC track time resolution to consider + float maxResX = 1.; // max value of track resolution (X dir) used in TOF matching (truncation to that in case it is larger) + float maxResZ = 1.; // max value of track resolution (Z dir) used in TOF matching (truncation to that in case it is larger) + float maxChi2 = 10.; // max value of Chi2 accepted for matching to TOF + bool applyPIDcutTPConly = true; // apply PID cut on TPC only tracks O2ParamDef(MatchTOFParams, "MatchTOF"); }; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h index c608e4d0eebf1..e736f0c9c8a42 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h @@ -48,13 +48,19 @@ #include "DataFormatsTPC/ClusterNativeHelper.h" #include "DataFormatsTPC/VDriftCorrFact.h" #include "ITSReconstruction/RecoGeomHelper.h" -#include "TPCFastTransform.h" #include "GPUO2InterfaceRefit.h" +#include "GPUTPCGeometry.h" #include "GlobalTracking/MatchTPCITSParams.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "DataFormatsITSMFT/TrkClusRef.h" #include "ITSMFTReconstruction/ChipMappingITS.h" -#include "CorrectionMapsHelper.h" +#include "TPCFastTransformPOD.h" +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) +#include "MemoryResources/MemoryResources.h" +#endif +#ifdef ENABLE_UPGRADES +#include "ITS3Reconstruction/TopologyDictionary.h" +#endif class TTree; @@ -86,7 +92,7 @@ namespace tpc { class TrackTPC; class VDriftCorrFact; -} +} // namespace tpc namespace gpu { @@ -110,7 +116,8 @@ enum TrackRejFlag : int { RejectOnTgl, RejectOnQ2Pt, RejectOnChi2, - NSigmaShift = 10 + NSigmaShift = 10, + RejectoOnPIDCorr = 20 }; ///< TPC track parameters propagated to reference X, with time bracket and index of @@ -124,7 +131,9 @@ struct TrackLocTPC : public o2::track::TrackParCov { float timeErr = 0.f; ///< time sigma (makes sense for constrained tracks only) int sourceID = 0; ///< TPC track origin in o2::dataformats::GlobalTrackID gid{}; // global track source ID (TPC track may be part of it) - int matchID = MinusOne; ///< entry (non if MinusOne) of its matchTPC struct in the mMatchesTPC + int matchID = MinusOne; ///< entry (non if MinusOne) of its matchTPC struct in the mMatchesTPC + uint8_t lowestRow = -1; + uint8_t padFromEdge = -1; Constraint_t constraint{Constrained}; float getCorrectedTime(float dt) const // return time0 corrected for extra drift (to match certain Z) @@ -133,19 +142,28 @@ struct TrackLocTPC : public o2::track::TrackParCov { } float getSignedDT(float dt) const // account for TPC side in time difference for dt=external_time - tpc.time0 { - return constraint == Constrained ? 0. : (constraint == ASide ? dt : -dt); + return constraint == Constrained ? 0.f : (constraint == ASide ? dt : -dt); } - ClassDefNV(TrackLocTPC, 1); + ClassDefNV(TrackLocTPC, 3); }; ///< ITS track outward parameters propagated to reference X, with time bracket and index of ///< original track in the currently loaded ITS reco output struct TrackLocITS : public o2::track::TrackParCov { + enum : uint16_t { CloneBefore = 0x1, + CloneAfter = 0x2 }; o2::math_utils::Bracketf_t tBracket; ///< bracketing time in \mus - int sourceID = 0; ///< track origin id - int roFrame = MinusOne; ///< ITS readout frame assigned to this track - int matchID = MinusOne; ///< entry (non if MinusOne) of its matchCand struct in the mMatchesITS + int sourceID = 0; ///< track origin id + int roFrame = MinusOne; ///< ITS readout frame assigned to this track + int matchID = MinusOne; ///< entry (non if MinusOne) of its matchCand struct in the mMatchesITS + float xrho = 0; ///< x*rho seen during propagation to reference X (as pion) + float dL = 0; ///< distance integrated during propagation to reference X (as pion) + bool hasCloneBefore() const { return getUserField() & CloneBefore; } + bool hasCloneAfter() const { return getUserField() & CloneAfter; } + int getCloneShift() const { return hasCloneBefore() ? -1 : (hasCloneAfter() ? 1 : 0); } + void setCloneBefore() { setUserField(getUserField() | CloneBefore); } + void setCloneAfter() { setUserField(getUserField() | CloneAfter); } ClassDefNV(TrackLocITS, 1); }; @@ -188,6 +206,8 @@ struct ABTrackLink : public o2::track::TrackParCov { uint8_t ladderID = 0xff; ///< ladder ID in the layer (used for seeds with 2 hits in the layer) float chi2 = 0.f; ///< chi2 after update + ABTrackLink() = default; + ~ABTrackLink() = default; ABTrackLink(const o2::track::TrackParCov& tr, int cl, int parID, int nextID, int lr, int nc, int ld, float _chi2) : o2::track::TrackParCov(tr), clID(cl), parentID(parID), nextOnLr(nextID), layerID(int8_t(lr)), nContLayers(int8_t(nc)), ladderID(uint8_t(ld)), chi2(_chi2) {} @@ -198,17 +218,25 @@ struct ABTrackLink : public o2::track::TrackParCov { float chi2NormPredict(float chi2cl) const { return (chi2 + chi2cl) / (1 + o2::its::RecoGeomHelper::getNLayers() - layerID); } }; +struct LinksPoolMT { + std::vector> threadPool; +}; + // AB primary seed: TPC track propagated to outermost ITS layer under specific InteractionCandidate hypothesis struct TPCABSeed { static constexpr int8_t NeedAlternative = -3; int tpcWID = MinusOne; ///< TPC track ID int ICCanID = MinusOne; ///< interaction candidate ID (they are sorted in increasing time) int winLinkID = MinusOne; ///< ID of the validated link + uint32_t linksEntry = 0; ///< 1st entry of the link + int nLinks = 0; ///< number of links int8_t lowestLayer = o2::its::RecoGeomHelper::getNLayers(); ///< lowest layer reached int8_t status = MinusOne; ///< status (RS TODO) + uint8_t threadID = 0; ///< thread ID o2::track::TrackParCov track{}; ///< Seed propagated to the outer layer under certain time constraint std::array firstInLr; ///< entry of 1st (best) hypothesis on each layer - std::vector trackLinks{}; ///< links + static LinksPoolMT* gLinksPool; ///< pool of links per thread + TPCABSeed(int id, int ic, const o2::track::TrackParCov& trc) : tpcWID(id), ICCanID(ic), track(trc) { firstInLr.fill(MinusOne); @@ -221,10 +249,17 @@ struct TPCABSeed { winLinkID = lID; status = Validated; } + int8_t getNLayers() const { return o2::its::RecoGeomHelper::getNLayers() - lowestLayer; } bool needAlteranative() const { return status == NeedAlternative; } void setNeedAlternative() { status = NeedAlternative; } - ABTrackLink& getLink(int i) { return trackLinks[i]; } - const ABTrackLink& getLink(int i) const { return trackLinks[i]; } + ABTrackLink& getLink(int i) { return gLinksPool->threadPool[threadID][i + linksEntry]; } + const ABTrackLink& getLink(int i) const { return gLinksPool->threadPool[threadID][i + linksEntry]; } + void addLink(const o2::track::TrackParCov& trc, int clID, int parentID, int nextID, int lr, int nc, int laddID, float chi2) + { + gLinksPool->threadPool[threadID].push_back({trc, clID, parentID, nextID, lr, nc, laddID, chi2}); + nLinks++; + } + int getNLinks() const { return nLinks; } auto getBestLinkID() const { return lowestLayer < o2::its::RecoGeomHelper::getNLayers() ? firstInLr[lowestLayer] : -1; @@ -233,7 +268,7 @@ struct TPCABSeed { { // check if some clusters used by the link or its parents are forbidden (already used by validatet track) while (linkID > MinusOne) { - const auto& link = trackLinks[linkID]; + const auto& link = getLink(linkID); if (link.clID > MinusOne && clStatus[link.clID] != MinusOne) { return true; } @@ -245,13 +280,15 @@ struct TPCABSeed { { // check if some clusters used by the link or its parents are forbidden (already used by validated track) while (linkID > MinusOne) { - const auto& link = trackLinks[linkID]; + const auto& link = getLink(linkID); if (link.clID > MinusOne) { clStatus[link.clID] = MinusTen; } linkID = link.parentID; } } + size_t sizeInternal() const { return sizeof(ABTrackLink) * getNLinks(); } + size_t capInternal() const { return sizeof(ABTrackLink) * getNLinks(); } }; struct InteractionCandidate : public o2::InteractionRecord { @@ -259,7 +296,6 @@ struct InteractionCandidate : public o2::InteractionRecord { int rofITS; // corresponding ITS ROF entry (in the ROFRecord vectors) uint32_t flag; // origin, etc. o2::dataformats::RangeReference seedsRef; // references to AB seeds - InteractionCandidate() = default; InteractionCandidate(const o2::InteractionRecord& ir, float t, float dt, int rof, uint32_t f = 0) : o2::InteractionRecord(ir), tBracket(t - dt, t + dt), rofITS(rof), flag(f) {} }; @@ -267,17 +303,30 @@ struct ITSChipClustersRefs { ///< contaner for sorted cluster indices for certain time window (usually ROF) and reference on the start and N clusters ///< for every chip using ClusRange = o2::dataformats::RangeReference; - std::vector clusterID; // indices of sorted clusters + std::vector clusterID; // indices of sorted clusters + +#ifndef ENABLE_UPGRADES std::array chipRefs; // offset and number of clusters in each chip ITSChipClustersRefs(int nclIni = 50000) { clusterID.reserve(nclIni); } +#else + std::vector chipRefs; // offset and number of clusters in each chip + ITSChipClustersRefs(int nchips = o2::its::RecoGeomHelper::getNChips(), int nclIni = 50000) + { + clusterID.reserve(nclIni); + chipRefs.resize(nchips, ClusRange()); + } +#endif + void clear() { clusterID.clear(); std::memset(chipRefs.data(), 0, chipRefs.size() * sizeof(ClusRange)); // reset chip->cluster references } + size_t sizeInternal() const { return sizeof(int) * clusterID.size(); } + size_t capInternal() const { return sizeof(int) * clusterID.capacity(); } }; class MatchTPCITS @@ -285,9 +334,6 @@ class MatchTPCITS public: using ITSCluster = o2::BaseCluster; using ClusRange = o2::dataformats::RangeReference; - using MCLabContCl = o2::dataformats::MCTruthContainer; - using MCLabContTr = std::vector; - using MCLabSpan = gsl::span; using TPCTransform = o2::gpu::TPCFastTransform; using BracketF = o2::math_utils::Bracket; using BracketIR = o2::math_utils::Bracket; @@ -303,9 +349,33 @@ class MatchTPCITS static constexpr int MaxSeedsPerLayer = 50; // TODO static constexpr int NITSLayers = o2::its::RecoGeomHelper::getNLayers(); ///< perform matching for provided input - void run(const o2::globaltracking::RecoContainer& inp); - - void setSkipTPCOnly(bool v) { mSkipTPCOnly = v; } +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) + void run(const o2::globaltracking::RecoContainer& inp, + pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib); + void refitWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib); + bool refitTrackTPCITS(int slot, int iTPC, int& iITS, pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib); + void fillCalibDebug(int ifit, int iTPC, const o2::dataformats::TrackTPCITS& match, pmr::vector>& calib); + void reportSizes(pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib); + bool runAfterBurner(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, pmr::vector& ABTrackletClusterIDs, + pmr::vector& ABTrackletRefs, pmr::vector>& calib); + void refitABWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, pmr::vector& ABTrackletClusterIDs, + pmr::vector& ABTrackletRefs, pmr::vector>& calib); + bool refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vector& matchedTracks, pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs); +#endif // CLING + void setSkipTPCOnly(bool v) + { + mSkipTPCOnly = v; + } void setCosmics(bool v) { mCosmics = v; } bool isCosmics() const { return mCosmics; } void setNThreads(int n); @@ -313,6 +383,7 @@ class MatchTPCITS ///< perform all initializations void init(); void end(); + void reportTiming(); ///< clear results of previous event reco void clear(); @@ -320,6 +391,8 @@ class MatchTPCITS ///< set Bunch filling and init helpers for validation by BCs void setBunchFilling(const o2::BunchFilling& bf); + void setNHBPerTF(int n) { mNHBPerTF = n; } + ///< ITS readout mode void setITSTriggered(bool v) { mITSTriggered = v; } bool isITSTriggered() const { return mITSTriggered; } @@ -341,6 +414,12 @@ class MatchTPCITS // ==================== >> DPL-driven input >> ======================= void setITSDictionary(const o2::itsmft::TopologyDictionary* d) { mITSDict = d; } +#ifdef ENABLE_UPGRADES + void setIT3Dictionary(const o2::its3::TopologyDictionary* d) + { + mIT3Dict = d; + } +#endif ///< set flag to use MC truth void setMCTruthOn(bool v) @@ -354,20 +433,13 @@ class MatchTPCITS mVDriftCalibOn = v; } void setTPCVDrift(const o2::tpc::VDriftCorrFact& v); - void setTPCCorrMaps(o2::gpu::CorrectionMapsHelper* maph); + void setTPCCorrMaps(const o2::gpu::TPCFastTransformPOD* maph, float lumi); ///< print settings void print() const; void printCandidatesTPC() const; void printCandidatesITS() const; - const std::vector& getMatchedTracks() const { return mMatchedTracks; } - const MCLabContTr& getMatchLabels() const { return mOutLabels; } - const MCLabContTr& getABTrackletLabels() const { return mABTrackletLabels; } - const std::vector& getABTrackletClusterIDs() const { return mABTrackletClusterIDs; } - const std::vector& getABTrackletRefs() const { return mABTrackletRefs; } - const std::vector& getTglITSTPC() const { return mTglITSTPC; } - //>>> ====================== options =============================>>> void setUseMatCorrFlag(MatCorrType f) { mUseMatCorrFlag = f; } auto getUseMatCorrFlag() const { return mUseMatCorrFlag; } @@ -376,9 +448,10 @@ class MatchTPCITS #ifdef _ALLOW_DEBUG_TREES_ enum DebugFlagTypes : UInt_t { - MatchTreeAll = 0x1 << 1, ///< produce matching candidates tree for all candidates - MatchTreeAccOnly = 0x1 << 2, ///< fill the matching candidates tree only once the cut is passed - WinnerMatchesTree = 0x1 << 3 ///< separate debug tree for winner matches + MatchTreeAll = 0x1 << 1, ///< produce matching candidates tree for all candidates + MatchTreeAccOnly = 0x1 << 2, ///< fill the matching candidates tree only once the cut is passed + WinnerMatchesTree = 0x1 << 3, ///< separate debug tree for winner matches + TPCOrigTree = 0x1 << 4 ///< original TPC tracks with some aux info }; ///< check if partucular flags are set bool isDebugFlag(UInt_t flags) const { return mDBGFlags & flags; } @@ -403,6 +476,7 @@ class MatchTPCITS ///< fill matching debug tree void fillTPCITSmatchTree(int itsID, int tpcID, int rejFlag, float chi2 = -1., float tCorr = 0.); void dumpWinnerMatches(); + void dumpTPCOrig(bool acc, int tpcIndex); #endif private: @@ -417,7 +491,7 @@ class MatchTPCITS bool prepareFITData(); int prepareInteractionTimes(); int prepareTPCTracksAfterBurner(); - void addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr, o2::dataformats::GlobalTrackID srcGID, int tpcID); + int addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr, o2::dataformats::GlobalTrackID srcGID, int tpcID); int preselectChipClusters(std::vector& clVecOut, const ClusRange& clRange, const ITSChipClustersRefs& itsChipClRefs, float trackY, float trackZ, float tolerY, float tolerZ) const; @@ -426,8 +500,6 @@ class MatchTPCITS void doMatching(int sec); - void refitWinners(); - bool refitTrackTPCITS(int iTPC, int& iITS); bool refitTPCInward(o2::track::TrackParCov& trcIn, float& chi2, float xTgt, int trcID, float timeTB) const; void selectBestMatches(); @@ -441,8 +513,8 @@ class MatchTPCITS int compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& tTPC, float& chi2) const; float getPredictedChi2NoZ(const o2::track::TrackParCov& trITS, const o2::track::TrackParCov& trTPC) const; - bool propagateToRefX(o2::track::TrackParCov& trc); - void addLastTrackCloneForNeighbourSector(int sector); + bool propagateToRefX(o2::track::TrackParCov& trc, o2::track::TrackLTIntegral* lti = nullptr); + void addLastTrackCloneForNeighbourSector(int sector, o2::track::TrackLTIntegral* trackLTInt = nullptr); ///------------------- manipulations with matches records ---------------------- bool registerMatchRecordTPC(int iITS, int iTPC, float chi2, int candIC = MinusOne); @@ -473,7 +545,7 @@ class MatchTPCITS int time2ITSROFrameTrig(float t, int start) const { t -= mITSTimeBiasMUS; - while (start < mITSROFTimes.size()) { + while (start < int(mITSROFTimes.size())) { if (mITSROFTimes[start].getMax() > t) { return start; } @@ -504,14 +576,11 @@ class MatchTPCITS } // ========================= AFTERBURNER ========================= - void runAfterBurner(); int prepareABSeeds(); - void processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs); + void processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs, uint8_t tID); int followABSeed(const o2::track::TrackParCov& seed, const ITSChipClustersRefs& itsChipClRefs, int seedID, int lrID, TPCABSeed& ABSeed); int registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackParCov& trc, int clID, int parentID, int lr, int laddID, float chi2Cl); bool isBetter(float chi2A, float chi2B) { return chi2A < chi2B; } // RS FIMXE TODO - void refitABWinners(); - bool refitABTrack(int iITSAB, const TPCABSeed& seed); void accountForOverlapsAB(int lrSeed); float correctTPCTrack(o2::track::TrackParCov& trc, const TrackLocTPC& tTPC, const InteractionCandidate& cand) const; // RS FIXME will be needed for refit //================================================================ @@ -523,6 +592,9 @@ class MatchTPCITS float mBz = 0; ///< nominal Bz int mTFCount = 0; ///< internal TF counter for debugger int mNThreads = 1; ///< number of OMP threads + int mNHBPerTF = 0; + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + float mNTPCOccBinLengthInv; o2::InteractionRecord mStartIR{0, 0}; ///< IR corresponding to the start of the TF ///========== Parameters to be set externally, e.g. from CCDB ==================== @@ -540,35 +612,36 @@ class MatchTPCITS float YMaxAtXMatchingRef = 999.; ///< max Y in the sector at reference X - float mSectEdgeMargin2 = 0.; ///< crude check if ITS track should be matched also in neighbouring sector + float mSectEdgeMargin = 0.; ///< crude check if ITS track should be matched also in neighbouring sector ///< safety margin in TPC time bins when estimating TPC track tMin and tMax from ///< assigned time0 and its track Z position (converted from mTPCTimeEdgeZSafeMargin) float mTPCTimeEdgeTSafeMargin = 0.f; float mTPCExtConstrainedNSigmaInv = 0.f; // inverse for NSigmas for TPC time-interval from external constraint time sigma - int mITSROFrameLengthInBC = 0; ///< ITS RO frame in BC (for ITS cont. mode only) - float mITSROFrameLengthMUS = -1.; ///< ITS RO frame in \mus - float mITSTimeResMUS = -1.; ///< nominal ITS time resolution derived from ROF - float mITSROFrameLengthMUSInv = -1.; ///< ITS RO frame in \mus inverse - int mITSTimeBiasInBC = 0; ///< ITS RO frame shift in BCs, i.e. t_i = (I_ROF*mITSROFrameLengthInBC + mITSTimeBiasInBC)*BCLength_MUS - float mITSTimeBiasMUS = 0.; ///< ITS RO frame shift in \mus, i.e. t_i = (I_ROF*mITSROFrameLengthInBC)*BCLength_MUS + mITSTimeBiasMUS - float mTPCVDrift = -1.; ///< TPC drift speed in cm/microseconds - float mTPCVDriftInv = -1.; ///< inverse TPC nominal drift speed in cm/microseconds - float mTPCDriftTimeOffset = 0; ///< drift time offset in mus - float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds - float mTPCTBinNS = 0.; ///< TPC time bin duration in ns - float mTPCTBinMUSInv = 0.; ///< inverse TPC time bin duration in microseconds - float mZ2TPCBin = 0.; ///< conversion coeff from Z to TPC time-bin - float mTPCBin2Z = 0.; ///< conversion coeff from TPC time-bin to Z - float mNTPCBinsFullDrift = 0.; ///< max time bin for full drift - float mTPCZMax = 0.; ///< max drift length - float mTPCmeanX0Inv = 1. / 31850.; ///< TPC gas 1/X0 + int mITSROFrameLengthInBC = 0; ///< ITS RO frame in BC (for ITS cont. mode only) + float mITSROFrameLengthMUS = -1.; ///< ITS RO frame in \mus + float mITSTimeResMUS = -1.; ///< nominal ITS time resolution derived from ROF + float mITSROFrameLengthMUSInv = -1.; ///< ITS RO frame in \mus inverse + int mITSTimeBiasInBC = 0; ///< ITS RO frame shift in BCs, i.e. t_i = (I_ROF*mITSROFrameLengthInBC + mITSTimeBiasInBC)*BCLength_MUS + float mITSTimeBiasMUS = 0.; ///< ITS RO frame shift in \mus, i.e. t_i = (I_ROF*mITSROFrameLengthInBC)*BCLength_MUS + mITSTimeBiasMUS + float mTPCVDrift = -1.; ///< TPC drift speed in cm/microseconds + float mTPCVDriftInv = -1.; ///< inverse TPC nominal drift speed in cm/microseconds + float mTPCDriftTimeOffset = 0; ///< drift time offset in mus + float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds + float mTPCTBinNS = 0.; ///< TPC time bin duration in ns + float mTPCTBinMUSInv = 0.; ///< inverse TPC time bin duration in microseconds + float mZ2TPCBin = 0.; ///< conversion coeff from Z to TPC time-bin + float mTPCBin2Z = 0.; ///< conversion coeff from TPC time-bin to Z + float mNTPCBinsFullDrift = 0.; ///< max time bin for full drift + float mTPCZMax = 0.; ///< max drift length + float mTPCmeanX0Inv = 1. / 31850.; ///< TPC gas 1/X0 float mMinTPCTrackPtInv = 999.; ///< cutoff on TPC track inverse pT float mMinITSTrackPtInv = 999.; ///< cutoff on ITS track inverse pT - bool mVDriftCalibOn = false; ///< flag to produce VDrift calibration data + bool mVDriftCalibOn = false; ///< flag to produce VDrift calibration data o2::tpc::VDriftCorrFact mTPCDrift{}; - o2::gpu::CorrectionMapsHelper* mTPCCorrMapsHelper = nullptr; + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps = nullptr; + float mLumiCTP{-1}; std::unique_ptr mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction @@ -576,57 +649,70 @@ class MatchTPCITS std::array mClosestBunchAbove; // closest filled bunch from above std::array mClosestBunchBelow; // closest filled bunch from below + std::array mCovDiagInner{}; ///< total cov.matrix extra diagonal error from TrackTuneParams + std::array mCovDiagOuter{}; ///< total cov.matrix extra diagonal error from TrackTuneParams + const o2::itsmft::ChipMappingITS ITSChMap{}; const o2::globaltracking::RecoContainer* mRecoCont = nullptr; ///>>>------ these are input arrays which should not be modified by the matching code // since this info is provided by external device - gsl::span mTPCTracksArray; ///< input TPC tracks span - gsl::span mTPCTrackClusIdx; ///< input TPC track cluster indices span - gsl::span mITSTrackROFRec; ///< input ITS tracks ROFRecord span - gsl::span mITSTracksArray; ///< input ITS tracks span - gsl::span mITSTrackClusIdx; ///< input ITS track cluster indices span - std::vector mITSClustersArray; ///< ITS clusters created in loadInput + gsl::span mTPCTracksArray; ///< input TPC tracks span + gsl::span mTPCTrackClusIdx; ///< input TPC track cluster indices span + gsl::span mITSTrackROFRec; ///< input ITS tracks ROFRecord span + gsl::span mITSTracksArray; ///< input ITS tracks span + gsl::span mITSTrackClusIdx; ///< input ITS track cluster indices span + std::vector mITSClustersArray; ///< ITS clusters created in loadInput + std::vector mITSClusterSizes; ///< ITS cluster sizes created in loadInput + gsl::span mITSClusterROFRec; ///< input ITS clusters ROFRecord span gsl::span mFITInfo; ///< optional input FIT info span gsl::span mTPCRefitterShMap; ///< externally set TPC clusters sharing map + gsl::span mTPCRefitterOccMap; ///< externally set TPC clusters occupancy map const o2::itsmft::TopologyDictionary* mITSDict{nullptr}; // cluster patterns dictionary +#ifdef ENABLE_UPGRADES + const o2::its3::TopologyDictionary* mIT3Dict{nullptr}; // cluster patterns dictionary +#endif const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices - const MCLabContCl* mITSClsLabels = nullptr; ///< input ITS Cluster MC labels - MCLabSpan mITSTrkLabels; ///< input ITS Track MC labels - MCLabSpan mTPCTrkLabels; ///< input TPC Track MC labels + const o2::dataformats::MCTruthContainer* mITSClsLabels = nullptr; ///< input ITS Cluster MC labels + gsl::span mITSTrkLabels; ///< input ITS Track MC labels + gsl::span mTPCTrkLabels; ///< input TPC Track MC labels /// <<<----- - std::vector mInteractions; ///< possible interaction times - std::vector mInteractionMUSLUT; ///< LUT for interactions in 1MUS bins + size_t mNMatches = 0; + size_t mNCalibPrelim = 0; + size_t mNMatchesControl = 0; + + size_t mNABRefsClus = 0; + float mAB2MatchGuess = 0.2; // heuristic guess about fraction of AB matches in total matches + std::vector mInteractions; ///< possible interaction times + std::vector mInteractionMUSLUT; ///< LUT for interactions in 1MUS bins ///< container for record the match of TPC track to single ITS track - std::vector mMatchRecordsTPC; + std::vector mMatchRecordsTPC; // RSS DEQ ///< container for reference to MatchRecord involving particular ITS track - std::vector mMatchRecordsITS; + std::vector mMatchRecordsITS; // RSS DEQ //// std::vector mITSROFofTPCBin; ///< aux structure for mapping of TPC time-bins on ITS ROFs - std::vector mITSROFTimes; ///< min/max times of ITS ROFs in \mus - std::vector mTPCWork; ///< TPC track params prepared for matching - std::vector mITSWork; ///< ITS track params prepared for matching - MCLabContTr mTPCLblWork; ///< TPC track labels - MCLabContTr mITSLblWork; ///< ITS track labels - std::vector mWinnerChi2Refit; ///< vector of refitChi2 for winners + std::vector mITSROFTimes; ///< min/max times of ITS ROFs in \mus + std::vector mTPCWork; ///< TPC track params prepared for matching + std::vector mITSWork; ///< ITS track params prepared for matching + std::vector mTPCLblWork; ///< TPC track labels + std::vector mITSLblWork; ///< ITS track labels + std::vector mWinnerChi2Refit; ///< vector of refitChi2 for winners + std::vector mTBinClOcc; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting from the TB = i*mNTPCOccBinLength // ------------------------------ std::vector mTPCABSeeds; ///< pool of primary TPC seeds for AB ///< indices of selected track entries in mTPCWork (for tracks selected by AfterBurner) std::vector mTPCABIndexCache; std::vector mABWinnersIDs; - std::vector mABTrackletClusterIDs; ///< IDs of ITS clusters for AfterBurner winners - std::vector mABTrackletRefs; ///< references on AfterBurner winners clusters - std::vector mABClusterLinkIndex; ///< index of 1st ABClusterLink for every cluster used by AfterBurner, -1: unused, -10: used by external ITS tracks - MCLabContTr mABTrackletLabels; - // ------------------------------ + std::vector mABClusterLinkIndex; ///< index of 1st ABClusterLink for every cluster used by AfterBurner, -1: unused, -10: used by external ITS tracks + LinksPoolMT mABLinksPool; ///< per sector indices of TPC track entry in mTPCWork std::array, o2::constants::math::NSectors> mTPCSectIndexCache; @@ -641,13 +727,6 @@ class MatchTPCITS /// mapping for tracks' continuos ROF cycle to actual continuous readout ROFs with eventual gaps std::vector mITSTrackROFContMapping; - ///< outputs tracks container - std::vector mMatchedTracks; - MCLabContTr mOutLabels; ///< Labels: = TPC labels with flag isFake set in case of fake matching - - ///< container for for vdrift calibration - std::vector mTglITSTPC; - o2::its::RecoGeomHelper mRGHelper; ///< helper for cluster and geometry access #ifdef _ALLOW_DEBUG_TREES_ @@ -662,6 +741,7 @@ class MatchTPCITS static constexpr float MaxSnp = 0.9; // max snp of ITS or TPC track at xRef to be matched static constexpr float MaxTgp = 2.064; // max tg corresponting to MaxSnp = MaxSnp/std::sqrt(1.-MaxSnp^2) static constexpr float MinTBToCleanCache = 600.; // keep in AB ITS cluster refs cache at most this number of TPC bins + static const o2::gpu::GPUTPCGeometry TPCGeometry; enum TimerIDs { SWTot, SWPrepITS, @@ -679,7 +759,6 @@ class MatchTPCITS static constexpr std::string_view TimerName[] = {"Total", "PrepareITS", "PrepareTPC", "DoMatching", "SelectBest", "Refit", "ABSeeds", "ABMatching", "ABWinners", "ABRefit", "IO", "Debug"}; TStopwatch mTimer[NStopWatches]; - }; //______________________________________________ @@ -700,7 +779,6 @@ inline bool MatchTPCITS::isDisabledITS(const TrackLocITS& t) const { return t.ma //______________________________________________ inline bool MatchTPCITS::isDisabledTPC(const TrackLocTPC& t) const { return t.matchID < 0; } - } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h index 7bdd883336859..3ec189deff54b 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h @@ -29,18 +29,27 @@ struct MatchTPCITSParams : public o2::conf::ConfigurableParamHelper +struct is_messageable; +template <> +struct is_messageable : std::true_type { +}; +} // namespace framework + } // end namespace o2 #endif diff --git a/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h b/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h index 1c277b24ecdca..eaafcca527d7d 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h @@ -52,6 +52,32 @@ using GIndex = o2::dataformats::VtxTrackIndex; class TrackCuts { public: + //////////////////////// Selection setters /////////////////////// + /// ITS + void setMinPtITSCut(float value) { mPtITSCut = value; } + void setEtaITSCut(float value) { mEtaITSCut = value; } + void setMinNClustersITS(int32_t value) { mMinNClustersITS = value; } + void setMaxChi2PerClusterITS(float value) { mMaxChi2PerClusterITS = value; } + void setRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) + { + mRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); + LOG(info) << "Track selection, set require hits in ITS layers: " << static_cast(minNRequiredHits); + } + /// TPC + void setMinPtTPCCut(float value) { mPtTPCCut = value; } + void setEtaTPCCut(float value) { mEtaTPCCut = value; } + void setMinNTPCClustersCut(int32_t value) { mNTPCClustersCut = value; } + void setMaxDCATPCCut(float value) { mDCATPCCut = value; } + void setMaxDCATPCCutY(float value) { mDCATPCCutY = value; } + /// ITS+TPC kinematics + void setMinPtCut(float value) { mMinPt = value; } + void setMaxPtCut(float value) { mMaxPt = value; } + void setEtaCut(float valueMin, float valueMax) + { + mMinEta = valueMin; + mMaxEta = valueMax; + } + //////////////////////////////// O2 //////////////////////////////// bool isSelected(GID trackIndex, o2::globaltracking::RecoContainer& data) { @@ -60,13 +86,14 @@ class TrackCuts auto src = trackIndex.getSource(); // make selections depending on track source // ITS tracks if (contributorsGID[GIndex::Source::ITS].isIndexSet()) { // ITS tracks selection - isBarrelTrack = true; const auto& itsTrk = data.getITSTrack(contributorsGID[GID::ITS]); int ITSnClusters = itsTrk.getNClusters(); float ITSchi2 = itsTrk.getChi2(); float itsChi2NCl = ITSnClusters != 0 ? ITSchi2 / (float)ITSnClusters : 0; uint8_t itsClusterMap = itsTrk.getPattern(); - if (ITSnClusters <= mMinNClustersITS || + if (itsTrk.getPt() <= mPtITSCut || + std::abs(itsTrk.getEta()) > mEtaITSCut || // TODO: define 2 different values for min and max (**) + ITSnClusters <= mMinNClustersITS || itsChi2NCl >= mMaxChi2PerClusterITS || TrackMethods::FulfillsITSHitRequirements(itsClusterMap, mRequiredITSHits) == false) { return false; @@ -74,21 +101,26 @@ class TrackCuts } // TPC tracks if (contributorsGID[GIndex::Source::TPC].isIndexSet()) { - isBarrelTrack = true; const auto& tpcTrk = data.getTPCTrack(contributorsGID[GID::TPC]); - math_utils::Point3D v{}; + math_utils::Point3D v{}; // vertex not defined?! std::array dca; - if (tpcTrk.getPt() < mPtTPCCut || - std::abs(tpcTrk.getEta()) > mEtaTPCCut || - tpcTrk.getNClusters() < mNTPCClustersCut || - (!(const_cast(tpcTrk).propagateParamToDCA(v, mBz, &dca, mDCACut)) || - std::abs(dca[0]) > mDCACutY)) { + if (tpcTrk.getPt() < mPtTPCCut || std::abs(tpcTrk.getEta()) > mEtaTPCCut || tpcTrk.getNClusters() < mNTPCClustersCut) { // TODO: define 2 different values for min and max (***) + return false; + } + o2::track::TrackPar trTmp(tpcTrk); + if (!trTmp.propagateParamToDCA(v, mBz, &dca, mDCATPCCut) || std::abs(dca[0]) > mDCATPCCutY || std::hypot(dca[0], dca[1]) > mDCATPCCut) { return false; } } - if (isBarrelTrack) { // track selection for barrel tracks + // ITS-TPC matched cuts + // --> currently inactive in MatchITSTPCQC, since either GID::TPC or GID::ITS + if (src == o2::dataformats::GlobalTrackID::ITSTPC || + src == o2::dataformats::GlobalTrackID::ITSTPCTRD || + src == o2::dataformats::GlobalTrackID::ITSTPCTOF || + src == o2::dataformats::GlobalTrackID::ITSTPCTRDTOF) { // track selection for barrel tracks (ITS-TPC matched) trk = data.getTrackParam(trackIndex); - if (trk.getPt() < mMinPt && trk.getPt() > mMaxPt && trk.getEta() < mMinEta && trk.getEta() > mMaxEta) { + float trkEta = trk.getEta(); + if (trk.getPt() < mMinPt || trk.getPt() > mMaxPt || trkEta < mMinEta || trkEta > mMaxEta) { return false; } } @@ -96,26 +128,26 @@ class TrackCuts } private: - bool isBarrelTrack = false; // all barrel tracks must have either ITS or TPC contribution -> true if ITS || TPC track source condition is passed - // cut values - float mPtTPCCut = 0.1f; - float mEtaTPCCut = 1.4f; - int32_t mNTPCClustersCut = 60; - float mDCACut = 100.f; - float mDCACutY = 10.f; - // kinematic cuts - float mMinPt{0.f}, - mMaxPt{1e10f}; // range in pT + ////////////////////// cut values ////////////////////////////// + /// ITS track + float mPtITSCut = 0.f; // min pT for ITS track + float mEtaITSCut = 1e10f; // eta window for ITS track --> TODO: define 2 different values for min and max (**) + int mMinNClustersITS{0}; // min number of ITS clusters + float mMaxChi2PerClusterITS{1e10f}; // max its fit chi2 per ITS cluster + std::vector>> mRequiredITSHits{}; // vector of ITS requirements (minNRequiredHits in specific requiredLayers) + /// TPC track + float mPtTPCCut = 0.1f; // min pT for TPC track + float mEtaTPCCut = 1.4f; // eta window for TPC track --> TODO: define 2 different values for min and max (***) + int32_t mNTPCClustersCut = 60; // minimum number of TPC clusters for TPC track + float mDCATPCCut = 100.f; // max DCA 3D to PV for TPC track + float mDCATPCCutY = 10.f; // max DCA xy to PV for TPC track + // ITS+TPC track kinematics + float mMinPt{0.f}, mMaxPt{1e10f}; // range in pT float mMinEta{-1e10f}, mMaxEta{1e10f}; // range in eta - float mBz = o2::base::Propagator::Instance()->getNominalBz(); - float mMaxChi2PerClusterITS{1e10f}; // max its fit chi2 per ITS cluster - int mMinNClustersITS{0}; // min number of ITS clusters - - // vector of ITS requirements (minNRequiredHits in specific requiredLayers) - std::vector>> mRequiredITSHits{}; + float mBz = o2::base::Propagator::Instance()->getNominalBz(); - ClassDefNV(TrackCuts, 1); + ClassDefNV(TrackCuts, 2); }; } // namespace o2 diff --git a/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h b/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h index 2d67a9595008e..f65d9ffd260e6 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h @@ -33,9 +33,9 @@ class TrackMethods const o2::tpc::ClusterNativeAccess& tpcClusAcc, uint8_t& shared, uint8_t& found, uint8_t& crossed) { - LOGP(info, "tpcClusRefs {}/{}", (void*)tpcClusRefs.data(), tpcClusRefs.size()); - LOGP(info, "tpcClusShMap {}/{}", (void*)tpcClusShMap.data(), tpcClusShMap.size()); - LOGP(info, " tpcClusAcc{}/{}", (void*)tpcClusAcc.clustersLinear, tpcClusAcc.nClustersTotal); + LOGP(debug, "tpcClusRefs {}/{}", (void*)tpcClusRefs.data(), tpcClusRefs.size()); + LOGP(debug, "tpcClusShMap {}/{}", (void*)tpcClusShMap.data(), tpcClusShMap.size()); + LOGP(debug, "tpcClusAcc {}/{}", (void*)tpcClusAcc.clustersLinear, tpcClusAcc.nClustersTotal); constexpr int maxRows = 152; constexpr int neighbour = 2; std::array clMap{}, shMap{}; @@ -47,7 +47,7 @@ class TrackMethods o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef()); unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex; clMap[rowIndex] = true; - if (tpcClusShMap[absoluteIndex] & GPUCA_NAMESPACE::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { if (!shMap[rowIndex]) { shared++; } @@ -93,4 +93,4 @@ class TrackMethods }; } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h b/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h index 1b76ad0456ac7..8125147add297 100644 --- a/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h +++ b/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h @@ -16,6 +16,7 @@ #pragma link off all functions; #pragma link C++ class o2::globaltracking::MatchTOF + ; +#pragma link C++ class o2::globaltracking::MatchHMP + ; #pragma link C++ class o2::globaltracking::TrackLocTPC + ; #pragma link C++ class o2::globaltracking::TrackLocITS + ; #pragma link C++ class o2::globaltracking::MatchTPCITSParams + ; @@ -28,8 +29,6 @@ #pragma link C++ class o2::globaltracking::MatchTOFParams + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::globaltracking::MatchTOFParams> + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::globaltracking::ITSTPCMatchingQCParams> + ; - #pragma link C++ class o2::globaltracking::GloFwdAssessment + ; #pragma link C++ class o2::globaltracking::GlobalFwdMatchingParam + ; @@ -44,4 +43,6 @@ #pragma link C++ class std::vector < std::tuple < float, float, float>> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::globaltracking::ITSTPCMatchingQCParams> + ; + #endif diff --git a/Detectors/GlobalTracking/src/MatchCosmics.cxx b/Detectors/GlobalTracking/src/MatchCosmics.cxx index 697f9ed0175b6..53c87d39974c7 100644 --- a/Detectors/GlobalTracking/src/MatchCosmics.cxx +++ b/Detectors/GlobalTracking/src/MatchCosmics.cxx @@ -23,6 +23,9 @@ #include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/TrackTPCTOF.h" #include "ReconstructionDataFormats/MatchInfoTOF.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ReconstructionDataFormats/DCA.h" #include "ITStracking/IOUtils.h" #include "ITSBase/GeometryTGeo.h" #include "TPCBase/ParameterElectronics.h" @@ -32,9 +35,10 @@ #include "CommonConstants/GeomConstants.h" #include "DataFormatsTPC/WorkflowHelper.h" #include "DataFormatsTPC/VDriftCorrFact.h" -#include "CorrectionMapsHelper.h" +#include "TPCFastTransformPOD.h" #include #include +#include using namespace o2::globaltracking; @@ -52,21 +56,53 @@ void MatchCosmics::process(const o2::globaltracking::RecoContainer& data) createSeeds(data); int ntr = mSeeds.size(); - + const auto prop = o2::base::Propagator::Instance(); // propagate to DCA to origin const o2::math_utils::Point3D v{0., 0., 0}; for (int i = 0; i < ntr; i++) { auto& trc = mSeeds[i]; - if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(v, trc, mMatchParams->maxStep, mMatchParams->matCorr)) { - trc.matchID = Reject; // reject track + if (trc.matchID != Reject) { + if (!prop->propagateToDCABxByBz(v, trc, mMatchParams->maxStep, mMatchParams->matCorr)) { + trc.matchID = Reject; // reject track + continue; + } + if (mMatchParams->dcaCutChi2[trc.origID.getSource()] > 0.f && mUsePVInfo && (std::abs(trc.getY()) < mMatchParams->fiducialRIP && std::abs(trc.getZ()) < mMatchParams->fiducialZIP)) { + // do the propagation only if we are in the fiducial IP range. + for (int iv = trc.vtIDMin; iv < trc.vtIDMax; iv++) { + const auto& pv = data.getPrimaryVertex(iv); + o2::track::TrackParCov trcatPV(trc); + o2::dataformats::DCA dca; + if (!trcatPV.propagateToDCA(pv, mBz, &dca)) { + trc.matchID = Reject; + break; + } + if (trc.origID.getSource() == GTrackID::TPC) { // correct the track Z position for the vertex time + const auto& trcTPC = data.getTPCTrack(trc.origID); + float deltaZ = trcTPC.hasBothSidesClusters() ? 0.f : (pv.getTimeStamp().getTimeStamp() - trcTPC.getTime0() * 8 * o2::constants::lhc::LHCBunchSpacingMUS) * mTPCVDrift; + dca.setZ(dca.getZ() + (trcTPC.hasASideClustersOnly() ? deltaZ : -deltaZ)); + } + dca.addCov({mMatchParams->systSigma2[0], 0.f, mMatchParams->systSigma2[1]}); + if (dca.calcChi2() < mMatchParams->dcaCutChi2[trc.origID.getSource()]) { + trc.matchID = Reject; + break; + } + } + } } } - - // sort in time bracket lower edge + // sort in time bracket lower edge, putting rejected tracks in the end std::vector sortID(ntr); std::iota(sortID.begin(), sortID.end(), 0); - std::sort(sortID.begin(), sortID.end(), [this](int a, int b) { return mSeeds[a].tBracket.getMin() < mSeeds[b].tBracket.getMin(); }); - + std::sort(sortID.begin(), sortID.end(), [this](int a, int b) { return mSeeds[a].matchID == Reject ? false : (mSeeds[b].matchID == Reject ? true : (mSeeds[a].tBracket.getMin() < mSeeds[b].tBracket.getMin())); }); + int lastValid = ntr - 1; + for (; lastValid >= 0; lastValid--) { + if (mSeeds[sortID[lastValid]].matchID != Reject) { + break; + } + } + ntr = lastValid >= 0 ? lastValid + 1 : 0; + LOGP(info, "Collected {} seeds, validated: {}", mSeeds.size(), ntr); + sortID.resize(ntr); for (int i = 0; i < ntr; i++) { for (int j = i + 1; j < ntr; j++) { if (checkPair(sortID[i], sortID[j]) == RejTime) { @@ -89,12 +125,13 @@ void MatchCosmics::refitWinners(const o2::globaltracking::RecoContainer& data) auto tpcTBinMUSInv = 1. / mTPCTBinMUS; const auto& tpcClusRefs = data.getTPCTracksClusterRefs(); const auto& tpcClusShMap = data.clusterShMapTPC; + const auto& tpcClusOccMap = data.occupancyMapTPC; std::unique_ptr tpcRefitter; if (data.inputsTPCclusters) { tpcRefitter = std::make_unique(&data.inputsTPCclusters->clusterIndex, - mTPCCorrMapsHelper, mBz, - tpcClusRefs.data(), tpcClusShMap.data(), - nullptr, o2::base::Propagator::Instance()); + mTPCCorrMaps, mBz, + tpcClusRefs.data(), 0, tpcClusShMap.data(), + tpcClusOccMap.data(), tpcClusOccMap.size(), nullptr, o2::base::Propagator::Instance()); } const auto& itsClusters = prepareITSClusters(data); @@ -170,7 +207,7 @@ void MatchCosmics::refitWinners(const o2::globaltracking::RecoContainer& data) if (!mFieldON) { trCosm.setQ2Pt(-o2::track::kMostProbablePt); } - int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosm, tpcTrOrig.getClusterRef(), (t0 + mTPCDriftTimeOffset) * tpcTBinMUSInv, &chi2, false, false); // inward refit, reset + int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosm, tpcTrOrig.getClusterRef(), t0 * tpcTBinMUSInv, &chi2, false, false); // inward refit, reset if (retVal < 0) { // refit failed LOG(debug) << "Inward refit of btm TPC track failed."; continue; @@ -228,7 +265,7 @@ void MatchCosmics::refitWinners(const o2::globaltracking::RecoContainer& data) } } const auto& tpcTrOrig = data.getTPCTrack(gidxListTop[GTrackID::TPC]); - int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosm, tpcTrOrig.getClusterRef(), (t0 + mTPCDriftTimeOffset) * tpcTBinMUSInv, &chi2, true, false); // outward refit, no reset + int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosm, tpcTrOrig.getClusterRef(), t0 * tpcTBinMUSInv, &chi2, true, false); // outward refit, no reset if (retVal < 0) { // refit failed LOG(debug) << "Outward refit of top TPC track failed."; continue; @@ -242,7 +279,7 @@ void MatchCosmics::refitWinners(const o2::globaltracking::RecoContainer& data) auto trCosmTop = outerLegs[top]; if (gidxListTop[GTrackID::TPC].isIndexSet()) { // inward refit in TPC const auto& tpcTrOrig = data.getTPCTrack(gidxListTop[GTrackID::TPC]); - int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosmTop, tpcTrOrig.getClusterRef(), (t0 + mTPCDriftTimeOffset) * tpcTBinMUSInv, &chi2Dummy, false, true); // inward refit, reset + int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosmTop, tpcTrOrig.getClusterRef(), t0 * tpcTBinMUSInv, &chi2Dummy, false, true); // inward refit, reset if (retVal < 0) { // refit failed LOG(debug) << "Outward refit of top TPC track failed."; continue; @@ -423,6 +460,8 @@ MatchCosmics::RejFlag MatchCosmics::checkPair(int i, int j) break; } bool ignoreZ = seed0.origID.getSource() == o2d::GlobalTrackID::TPC || seed1.origID.getSource() == o2d::GlobalTrackID::TPC; + // RSTODO this is simplification: one should constraint the TPC-only track Z by the time of other candidate (at least their difference of both tracks are TPC only). + // If the shift is large, eventually the tracks need to be refitted. if (!ignoreZ) { // cut on Z makes no sense for TPC only tracks auto dZ = seed0.getZ() - seed1Inv.getZ(); if (dZ * dZ > (seed0.getSigmaZ2() + seed1Inv.getSigmaZ2()) * mMatchParams->crudeNSigma2Cut[o2::track::kZ]) { @@ -491,16 +530,19 @@ void MatchCosmics::createSeeds(const o2::globaltracking::RecoContainer& data) // Scan all inputs and create seeding tracks mSeeds.clear(); + std::unordered_map trackEntry; - auto creator = [this](auto& _tr, GTrackID _origID, float t0, float terr) { + auto creator = [this, &trackEntry](auto& _tr, GTrackID _origID, float t0, float terr) { if constexpr (std::is_base_of_v>) { if (std::abs(_tr.getQ2Pt()) > this->mQ2PtCutoff) { return true; } if constexpr (isTPCTrack()) { + if (!this->mMatchParams->allowTPCOnly) { + return true; + } // unconstrained TPC track, with t0 = TrackTPC.getTime0+0.5*(DeltaFwd-DeltaBwd) and terr = 0.5*(DeltaFwd+DeltaBwd) in TimeBins t0 *= this->mTPCTBinMUS; - t0 -= this->mTPCDriftTimeOffset; terr *= this->mTPCTBinMUS; } else if (isITSTrack()) { t0 += 0.5 * this->mITSROFrameLengthMUS; // time 0 is supplied as beginning of ROF in \mus @@ -509,7 +551,11 @@ void MatchCosmics::createSeeds(const o2::globaltracking::RecoContainer& data) terr *= this->mMatchParams->nSigmaTError; } terr += this->mMatchParams->timeToleranceMUS; + trackEntry[_origID] = mSeeds.size(); mSeeds.emplace_back(TrackSeed{_tr, {t0 - terr, t0 + terr}, _origID, MinusOne}); + if constexpr (isTPCTrack()) { + mSeeds.back().setCov(this->mMatchParams->tpcExtraZError2 + _tr.getCov()[o2::track::kSigZ2], o2::track::kSigZ2); + } return true; } else { return false; @@ -518,7 +564,32 @@ void MatchCosmics::createSeeds(const o2::globaltracking::RecoContainer& data) data.createTracksVariadic(creator); - LOG(info) << "collected " << mSeeds.size() << " seeds"; + if (mUsePVInfo) { // if needed, veto with the primary vertex info + auto trackIndex = data.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = data.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + int nv = vtxRefs.size() - 1; // The last entry is for unassigned tracks, no need to check them + const auto propagator = o2::base::Propagator::Instance(); + for (int iv = 0; iv < nv; iv++) { + const auto& vtref = vtxRefs[iv]; + int it = vtref.getFirstEntry(), itLim = it + vtref.getEntries(); + for (; it < itLim; it++) { + auto tvid = trackIndex[it]; + auto entry = trackEntry.find(tvid); + if (entry == trackEntry.end()) { + continue; + } + auto& seed = mSeeds[entry->second]; + if (seed.matchID == Reject || (mMatchParams->discardPVContributors && tvid.isPVContributor())) { + seed.matchID = Reject; + continue; + } + if (seed.vtIDMin < 0) { + seed.vtIDMin = iv; + } + seed.vtIDMax = std::max(short(iv), seed.vtIDMax); + } + } + } } //________________________________________________________ @@ -594,9 +665,9 @@ void MatchCosmics::setTPCVDrift(const o2::tpc::VDriftCorrFact& v) } //______________________________________________ -void MatchCosmics::setTPCCorrMaps(o2::gpu::CorrectionMapsHelper* maph) +void MatchCosmics::setTPCCorrMaps(const o2::gpu::TPCFastTransformPOD* maph) { - mTPCCorrMapsHelper = maph; + mTPCCorrMaps = maph; } #endif diff --git a/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx b/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx index f4a8389f47165..7ed50ce7400ae 100644 --- a/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx +++ b/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "GlobalTracking/MatchGlobalFwd.h" +#include using namespace o2::globaltracking; @@ -64,8 +65,13 @@ void MatchGlobalFwd::init() mUseMIDMCHMatch = matchingParam.useMIDMatch; LOG(info) << "UseMIDMCH Matching = " << (mUseMIDMCHMatch ? "true" : "false"); + mUseTrackTime = matchingParam.useTrackTime; + LOG(info) << "Use track time = " << (mUseTrackTime ? "true" : "false"); + mSaveMode = matchingParam.saveMode; LOG(info) << "Save mode MFTMCH candidates = " << mSaveMode; + + mNCandidates = matchingParam.nCandidates; } //_________________________________________________________ @@ -98,6 +104,9 @@ void MatchGlobalFwd::run(const o2::globaltracking::RecoContainer& inp) case kSaveTrainingData: doMatching(); break; + case kSaveNCandidates: + doMatching(); + break; default: LOG(fatal) << "Invalid MFTMCH save mode"; } @@ -132,6 +141,7 @@ void MatchGlobalFwd::clear() mMatchLabels.clear(); mMFTTrackROFContMapping.clear(); mMatchingInfo.clear(); + mCandidates.clear(); } //_________________________________________________________ @@ -151,6 +161,8 @@ bool MatchGlobalFwd::prepareMCHData() return false; } mMCHWork.reserve(mMCHTracks.size()); + mMCHID2Work.clear(); + mMCHID2Work.resize(mMCHTracks.size(), -1); static int BCDiffErrCount = 0; constexpr int MAXBCDiffErrCount = 2; @@ -173,6 +185,7 @@ bool MatchGlobalFwd::prepareMCHData() for (int it = rofRec.getFirstIdx(); it < trlim; it++) { auto& trcOrig = mMCHTracks[it]; int nWorkTracks = mMCHWork.size(); + mMCHID2Work[it] = nWorkTracks; // working copy MCH track propagated to matching plane and converted to the forward track format o2::mch::TrackParam tempParam(trcOrig.getZ(), trcOrig.getParameters(), trcOrig.getCovariances()); if (!o2::mch::TrackExtrap::extrapToVertexWithoutBranson(tempParam, mMatchingPlaneZ)) { @@ -202,11 +215,12 @@ bool MatchGlobalFwd::processMCHMIDMatches() for (const auto& MIDMatch : mMCHMIDMatches) { const auto& MCHId = MIDMatch.getMCHRef().getIndex(); const auto& MIDId = MIDMatch.getMIDRef().getIndex(); - auto& thisMuonTrack = mMCHWork[MCHId]; + auto& thisMuonTrack = mMCHWork[mMCHID2Work[MCHId]]; + LOG(debug) << " MCHId: " << MCHId << " --> mMCHID2Work[MCHId]:" << mMCHID2Work[MCHId]; const auto& IR = MIDMatch.getIR(); int nBC = IR.differenceInBC(mStartIR); - float tMin = nBC * o2::constants::lhc::LHCBunchSpacingMUS; - float tMax = (nBC + 1) * o2::constants::lhc::LHCBunchSpacingMUS; + float tMin = (nBC - 1) * o2::constants::lhc::LHCBunchSpacingMUS; + float tMax = (nBC + 2) * o2::constants::lhc::LHCBunchSpacingMUS; thisMuonTrack.setMIDTrackID(MIDId); thisMuonTrack.setTimeMUS(MIDMatch.getTimeMUS(mStartIR).first); thisMuonTrack.tBracket.set(tMin, tMax); @@ -251,7 +265,6 @@ bool MatchGlobalFwd::prepareMFTData() for (int irof = 0; irof < nROFs; irof++) { const auto& rofRec = mMFTTrackROFRec[irof]; - int nBC = rofRec.getBCData().differenceInBC(mStartIR); if (nBC < 0) { if (BCDiffErrCount++ < MAXBCDiffErrCount) { @@ -259,10 +272,10 @@ bool MatchGlobalFwd::prepareMFTData() } return false; } - float tMin = nBC * o2::constants::lhc::LHCBunchSpacingMUS; - float tMax = (nBC + mMFTROFrameLengthInBC) * o2::constants::lhc::LHCBunchSpacingMUS; + float tMin = (nBC + mMFTROFrameBiasInBC) * o2::constants::lhc::LHCBunchSpacingMUS; + float tMax = (nBC + mMFTROFrameLengthInBC + mMFTROFrameBiasInBC) * o2::constants::lhc::LHCBunchSpacingMUS; if (!mMFTTriggered) { - auto irofCont = nBC / mMFTROFrameLengthInBC; + auto irofCont = (nBC + mMFTROFrameBiasInBC) / mMFTROFrameLengthInBC; if (mMFTTrackROFContMapping.size() <= irofCont) { // there might be gaps in the non-empty rofs, this will map continuous ROFs index to non empty ones mMFTTrackROFContMapping.resize((1 + irofCont / 128) * 128, 0); } @@ -282,6 +295,9 @@ bool MatchGlobalFwd::prepareMFTData() trc.setZ(trcOrig.getOutParam().getZ()); trc.setCovariances(trcOrig.getOutParam().getCovariances()); trc.setTrackChi2(trcOrig.getOutParam().getTrackChi2()); + // Extrapolate MFT track parameters and covariances matrix to "mMatchingPlaneZ" + // Parameters: helix track model; Error propagation: Quadratic + // If "mBz" is zero: linear track model trc.propagateToZ(mMatchingPlaneZ, mBz); } } @@ -306,7 +322,7 @@ void MatchGlobalFwd::loadMatches() auto MCHId = match.getMCHTrackID(); LOG(debug) << " ==> MFTId = " << MFTId << " MCHId = " << MCHId << std::endl; - auto& thisMCHTrack = mMCHWork[MCHId]; + auto& thisMCHTrack = mMCHWork[mMCHID2Work[MCHId]]; thisMCHTrack.setMatchInfo(match); mMatchedTracks.emplace_back(thisMCHTrack); if (mMCTruthON) { @@ -332,6 +348,8 @@ void MatchGlobalFwd::doMatching() // ROFrame of first MFT track auto firstMFTTrackIdInROF = 0; auto MFTROFId = mMFTWork.front().roFrame; + LOG(debug) << "(*) nMCHROFs: " << nMCHROFs << ", mMFTTracks.size(): " << mMFTTracks.size() << " MFTROFId: " << MFTROFId << ", mMFTTrackROFRec.size(): " << mMFTTrackROFRec.size(); + while ((firstMFTTrackIdInROF < mMFTTracks.size()) && (MFTROFId < mMFTTrackROFRec.size())) { auto MFTROFId = mMFTWork[firstMFTTrackIdInROF].roFrame; const auto& thisMFTBracket = mMFTROFTimes[MFTROFId]; @@ -392,6 +410,25 @@ void MatchGlobalFwd::doMatching() if (mMCTruthON) { LOG(info) << " MFT-MCH Matching: nFakes = " << nFakes << " nTrue = " << nTrue; } + } else if constexpr (saveAllMode == SaveMode::kSaveNCandidates) { + int nFakes = 0, nTrue = 0; + auto& matchAllChi2 = mMatchingFunctionMap["matchALL"]; + for (auto MCHId = 0; MCHId < mMCHWork.size(); MCHId++) { + auto& thisMCHTrack = mMCHWork[MCHId]; + for (auto& pairCandidate : mCandidates[MCHId]) { + thisMCHTrack.setMFTTrackID(pairCandidate.second); + auto& thisMFTTrack = mMFTWork[pairCandidate.second]; + auto chi2 = matchAllChi2(thisMCHTrack, thisMFTTrack); // Matching chi2 is stored independently + thisMCHTrack.setMFTMCHMatchingScore(pairCandidate.first); + thisMCHTrack.setMFTMCHMatchingChi2(chi2); + mMatchedTracks.emplace_back(thisMCHTrack); + mMatchingInfo.emplace_back(thisMCHTrack); + if (mMCTruthON) { + mMatchLabels.push_back(computeLabel(MCHId, pairCandidate.second)); + mMatchLabels.back().isFake() ? nFakes++ : nTrue++; + } + } + } } } @@ -401,10 +438,15 @@ void MatchGlobalFwd::ROFMatch(int MFTROFId, int firstMCHROFId, int lastMCHROFId) { /// Matches MFT tracks on a given ROF with MCH tracks in a range of ROFs const auto& thisMFTROF = mMFTTrackROFRec[MFTROFId]; + const auto& thisMFTBracket = mMFTROFTimes[MFTROFId]; const auto& firstMCHROF = mMCHTrackROFRec[firstMCHROFId]; const auto& lastMCHROF = mMCHTrackROFRec[lastMCHROFId]; int nFakes = 0, nTrue = 0; + auto compare = [](const std::pair& a, const std::pair& b) { + return a.first < b.first; + }; + auto firstMFTTrackID = thisMFTROF.getFirstEntry(); auto lastMFTTrackID = firstMFTTrackID + thisMFTROF.getNEntries() - 1; @@ -426,6 +468,12 @@ void MatchGlobalFwd::ROFMatch(int MFTROFId, int firstMCHROFId, int lastMCHROFId) // loop over all MCH tracks for (auto MCHId = firstMCHTrackID; MCHId <= lastMCHTrackID; MCHId++) { auto& thisMCHTrack = mMCHWork[MCHId]; + + // If enabled, use the muon track time to check if the track is correlated with the MFT ROF + if (mUseTrackTime && (thisMFTBracket.isOutside(thisMCHTrack.tBracket))) { + continue; + } + o2::MCCompLabel matchLabel; for (auto MFTId = firstMFTTrackID; MFTId <= lastMFTTrackID; MFTId++) { auto& thisMFTTrack = mMFTWork[MFTId]; @@ -456,12 +504,21 @@ void MatchGlobalFwd::ROFMatch(int MFTROFId, int firstMCHROFId, int lastMCHROFId) } } + if constexpr (saveAllMode == SaveMode::kSaveNCandidates) { // Save best N matching candidates + auto score = mMatchFunc(thisMCHTrack, thisMFTTrack); + std::pair scoreID = {score, MFTId}; + mCandidates[MCHId].push_back(scoreID); + std::sort(mCandidates[MCHId].begin(), mCandidates[MCHId].end(), compare); + if (mCandidates[MCHId].size() > mNCandidates) { + mCandidates[MCHId].pop_back(); + } + } + if constexpr (saveAllMode == SaveMode::kSaveTrainingData) { // In save training data mode store track parameters at matching plane thisMCHTrack.setMFTTrackID(MFTId); mMatchingInfo.emplace_back(thisMCHTrack); mMCHMatchPlaneParams.emplace_back(thisMCHTrack); mMFTMatchPlaneParams.emplace_back(static_cast(thisMFTTrack)); - if (mMCTruthON) { mMatchLabels.push_back(matchLabel); mMatchLabels.back().isFake() ? nFakes++ : nTrue++; @@ -502,7 +559,7 @@ void MatchGlobalFwd::doMCMatching() // loop over all MCH tracks for (auto MCHId = 0; MCHId < mMCHWork.size(); MCHId++) { auto& thisMCHTrack = mMCHWork[MCHId]; - const o2::MCCompLabel& thisMCHLabel = mMCHTrkLabels[MCHId]; + const o2::MCCompLabel& thisMCHLabel = mMCHTrkLabels[mMCHID2Work[MCHId]]; LOG(debug) << " MCH Track # " << MCHId << " Label: " << thisMCHLabel; if (!((thisMCHLabel).isSet())) { @@ -643,6 +700,14 @@ void MatchGlobalFwd::setMFTROFrameLengthInBC(int nbc) mMFTROFrameLengthMUSInv = 1. / mMFTROFrameLengthMUS; } +//_________________________________________________________ +void MatchGlobalFwd::setMFTROFrameBiasInBC(int nbc) +{ + mMFTROFrameBiasInBC = nbc; + mMFTROFrameBiasMUS = nbc * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; + mMFTROFrameBiasMUSInv = 1. / mMFTROFrameBiasMUS; +} + //_________________________________________________________ void MatchGlobalFwd::setBunchFilling(const o2::BunchFilling& bf) { @@ -754,6 +819,76 @@ o2::dataformats::GlobalFwdTrack MatchGlobalFwd::MCHtoFwd(const o2::mch::TrackPar return convertedTrack; } +//_________________________________________________________________________________________________ +o2::mch::TrackParam MatchGlobalFwd::FwdtoMCH(const o2::dataformats::GlobalFwdTrack& fwdtrack) +{ + // Convert Forward Track parameters and covariances matrix to the + // MCH track format. + + // Parameter conversion + double alpha1, alpha3, alpha4, x2, x3, x4; + + x2 = fwdtrack.getPhi(); + x3 = fwdtrack.getTanl(); + x4 = fwdtrack.getInvQPt(); + + auto sinx2 = TMath::Sin(x2); + auto cosx2 = TMath::Cos(x2); + + alpha1 = cosx2 / x3; + alpha3 = sinx2 / x3; + alpha4 = x4 / TMath::Sqrt(x3 * x3 + sinx2 * sinx2); + + auto K = TMath::Sqrt(x3 * x3 + sinx2 * sinx2); + auto K3 = K * K * K; + + // Covariances matrix conversion + SMatrix55Std jacobian; + SMatrix55Sym covariances; + + covariances(0, 0) = fwdtrack.getCovariances()(0, 0); + covariances(0, 1) = fwdtrack.getCovariances()(0, 1); + covariances(0, 2) = fwdtrack.getCovariances()(0, 2); + covariances(0, 3) = fwdtrack.getCovariances()(0, 3); + covariances(0, 4) = fwdtrack.getCovariances()(0, 4); + + covariances(1, 1) = fwdtrack.getCovariances()(1, 1); + covariances(1, 2) = fwdtrack.getCovariances()(1, 2); + covariances(1, 3) = fwdtrack.getCovariances()(1, 3); + covariances(1, 4) = fwdtrack.getCovariances()(1, 4); + + covariances(2, 2) = fwdtrack.getCovariances()(2, 2); + covariances(2, 3) = fwdtrack.getCovariances()(2, 3); + covariances(2, 4) = fwdtrack.getCovariances()(2, 4); + + covariances(3, 3) = fwdtrack.getCovariances()(3, 3); + covariances(3, 4) = fwdtrack.getCovariances()(3, 4); + + covariances(4, 4) = fwdtrack.getCovariances()(4, 4); + + jacobian(0, 0) = 1; + + jacobian(1, 2) = -sinx2 / x3; + jacobian(1, 3) = -cosx2 / (x3 * x3); + + jacobian(2, 1) = 1; + + jacobian(3, 2) = cosx2 / x3; + jacobian(3, 3) = -sinx2 / (x3 * x3); + + jacobian(4, 2) = -x4 * sinx2 * cosx2 / K3; + jacobian(4, 3) = -x3 * x4 / K3; + jacobian(4, 4) = 1 / K; + // jacobian*covariances*jacobian^T + covariances = ROOT::Math::Similarity(jacobian, covariances); + + double cov[] = {covariances(0, 0), covariances(1, 0), covariances(1, 1), covariances(2, 0), covariances(2, 1), covariances(2, 2), covariances(3, 0), covariances(3, 1), covariances(3, 2), covariances(3, 3), covariances(4, 0), covariances(4, 1), covariances(4, 2), covariances(4, 3), covariances(4, 4)}; + double param[] = {fwdtrack.getX(), alpha1, fwdtrack.getY(), alpha3, alpha4}; + + o2::mch::TrackParam convertedTrack(fwdtrack.getZ(), param, cov); + return o2::mch::TrackParam(convertedTrack); +} + //_________________________________________________________________________________________________ MatchGlobalFwd::MatchGlobalFwd() { diff --git a/Detectors/GlobalTracking/src/MatchGlobalFwdAssessment.cxx b/Detectors/GlobalTracking/src/MatchGlobalFwdAssessment.cxx index 443a6ad6b5407..fc5fce57b9a9f 100644 --- a/Detectors/GlobalTracking/src/MatchGlobalFwdAssessment.cxx +++ b/Detectors/GlobalTracking/src/MatchGlobalFwdAssessment.cxx @@ -46,7 +46,7 @@ void GloFwdAssessment::reset() mTrackEtaPhiNCls[nHisto]->Reset(); } - mTrackTanl->Reset(); + mTrackCotl->Reset(); if (mUseMC) { mPairables.clear(); @@ -86,9 +86,9 @@ void GloFwdAssessment::createHistos() mTrackNumberOfClusters = std::make_unique("mGlobalFwdNumberOfMFTClusters", "Number Of Clusters Per Track; # clusters; # entries", 10, 0.5, 10.5); - mTrackInvQPt = std::make_unique("mGlobalFwdInvQPt", "Track q/p_{T}; q/p_{T} [1/GeV]; # entries", 50, -2, 2); + mTrackInvQPt = std::make_unique("mGlobalFwdInvQPt", "Track q/p_{T}; q/p_{T} [1/GeV]; # entries", 100, -5, 5); - mTrackChi2 = std::make_unique("mGlobalFwdChi2", "Track #chi^{2}; #chi^{2}; # entries", 21, -0.5, 20.5); + mTrackChi2 = std::make_unique("mGlobalFwdChi2", "Track #chi^{2}; #chi^{2}; # entries", 202, -0.5, 100.5); mTrackCharge = std::make_unique("mGlobalFwdCharge", "Track Charge; q; # entries", 3, -1.5, 1.5); @@ -109,7 +109,7 @@ void GloFwdAssessment::createHistos() mTrackEtaPhiNCls[nHisto]->SetOption("COLZ"); } - mTrackTanl = std::make_unique("mGlobalFwdTanl", "Track tan #lambda; tan #lambda; # entries", 100, -25, 0); + mTrackCotl = std::make_unique("mGlobalFwdCotl", "Track cot #lambda; cot #lambda; # entries", 100, -0.25, 0); // Creating MC-based histos if (mUseMC) { @@ -223,7 +223,7 @@ void GloFwdAssessment::runBasicQC(o2::framework::ProcessingContext& ctx) mTrackCharge->Fill(oneTrack.getCharge()); mTrackPhi->Fill(oneTrack.getPhi()); mTrackEta->Fill(oneTrack.getEta()); - mTrackTanl->Fill(oneTrack.getTanl()); + mTrackCotl->Fill(1. / oneTrack.getTanl()); for (auto minNClusters : sMinNClustersList) { if (nClusters >= minNClusters) { @@ -400,7 +400,7 @@ void GloFwdAssessment::processTrueTracks() const auto y_res = fwdTrack.getY() - vyGen; const auto eta_res = fwdTrack.getEta() - etaGen; const auto phi_res = fwdTrack.getPhi() - phiGen; - const auto tanl_res = fwdTrack.getTanl() - tanlGen; + const auto cotl_res = (1. / fwdTrack.getTanl()) - (1. / tanlGen); const auto invQPt_res = invQPt_Rec - invQPtGen; mHistPtVsEta[kRecoTrue]->Fill(eta_Rec, pt_Rec); mHistPhiVsEta[kRecoTrue]->Fill(eta_Rec, phi_Rec); @@ -421,7 +421,7 @@ void GloFwdAssessment::processTrueTracks() mTH3Histos[kTH3GMTrackXPullPtEta]->Fill(ptGen, etaGen, x_res / sqrt(fwdTrack.getCovariances()(0, 0))); mTH3Histos[kTH3GMTrackYPullPtEta]->Fill(ptGen, etaGen, y_res / sqrt(fwdTrack.getCovariances()(1, 1))); mTH3Histos[kTH3GMTrackPhiPullPtEta]->Fill(ptGen, etaGen, phi_res / sqrt(fwdTrack.getCovariances()(2, 2))); - mTH3Histos[kTH3GMTrackTanlPullPtEta]->Fill(ptGen, etaGen, tanl_res / sqrt(fwdTrack.getCovariances()(3, 3))); + mTH3Histos[kTH3GMTrackCotlPullPtEta]->Fill(ptGen, etaGen, cotl_res / sqrt(1. / fwdTrack.getCovariances()(3, 3))); mTH3Histos[kTH3GMTrackInvQPtPullPtEta]->Fill(ptGen, etaGen, invQPt_res / sqrt(fwdTrack.getCovariances()(4, 4))); mTH3Histos[kTH3GMTrackInvQPtResolutionPtEta]->Fill(ptGen, etaGen, (invQPt_Rec - invQPtGen) / invQPtGen); mTH3Histos[kTH3GMTrackInvQPtResMCHPtEta]->Fill(ptGen, etaGen, (invQPt_MCH - invQPtGen) / invQPtGen); @@ -477,7 +477,7 @@ void GloFwdAssessment::getHistos(TObjArray& objar) objar.Add(mTrackXYNCls[nHisto].get()); objar.Add(mTrackEtaPhiNCls[nHisto].get()); } - objar.Add(mTrackTanl.get()); + objar.Add(mTrackCotl.get()); if (mUseMC) { objar.Add(mHistPhiRecVsPhiGen.get()); @@ -552,7 +552,7 @@ void GloFwdAssessment::loadHistos() mTrackEtaPhiNCls[nHisto] = std::unique_ptr((TH2F*)f->Get(Form("mGlobalFwdEtaPhi_%d_MinClusters", minNClusters))); } - mTrackTanl = std::unique_ptr((TH1F*)f->Get("mGlobalFwdTanl")); + mTrackCotl = std::unique_ptr((TH1F*)f->Get("mGlobalFwdCotl")); // Creating MC-based histos if (mUseMC) { diff --git a/Detectors/GlobalTracking/src/MatchHMP.cxx b/Detectors/GlobalTracking/src/MatchHMP.cxx new file mode 100644 index 0000000000000..207bbdb9bfbf6 --- /dev/null +++ b/Detectors/GlobalTracking/src/MatchHMP.cxx @@ -0,0 +1,625 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include +#include + +#include "FairLogger.h" +#include "Field/MagneticField.h" +#include "Field/MagFieldFast.h" +#include "TOFBase/Geo.h" + +#include "SimulationDataFormat/MCTruthContainer.h" + +#include "DetectorsBase/Propagator.h" + +#include "MathUtils/Cartesian.h" +#include "MathUtils/Utils.h" +#include "CommonConstants/MathConstants.h" +#include "CommonConstants/PhysicsConstants.h" +#include "CommonConstants/GeomConstants.h" +#include "DetectorsBase/GeometryManager.h" + +#include +#include +#include +#include +#include "DataFormatsParameters/GRPObject.h" +#include "ReconstructionDataFormats/PID.h" +#include "ReconstructionDataFormats/TrackLTIntegral.h" +#include "ReconstructionDataFormats/TrackHMP.h" + +#include "GlobalTracking/MatchHMP.h" + +#include "TPCBase/ParameterGas.h" +#include "TPCBase/ParameterElectronics.h" +#include "TPCReconstruction/TPCFastTransformHelperO2.h" + +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "HMPIDBase/Param.h" +#include "HMPIDReconstruction/Recon.h" + +#include "CommonDataFormat/InteractionRecord.h" + +using namespace o2::globaltracking; +using evGIdx = o2::dataformats::EvIndex; +using Cluster = o2::hmpid::Cluster; +using Recon = o2::hmpid::Recon; +using MatchInfo = o2::dataformats::MatchInfoHMP; +using Trigger = o2::hmpid::Trigger; +using GTrackID = o2::dataformats::GlobalTrackID; +using TrackHMP = o2::dataformats::TrackHMP; +using timeEst = o2::dataformats::TimeStampWithError; + +ClassImp(MatchHMP); +//================================================================================================================================================== +void MatchHMP::run(const o2::globaltracking::RecoContainer& inp) +{ + ///< running the matching + + mRecoCont = &inp; + mStartIR = inp.startIR; + + for (int i = 0; i < o2::globaltracking::MatchHMP::trackType::SIZE; i++) { + mTrackGid[i].clear(); + mMatchedTracks[i].clear(); + mOutHMPLabels[i].clear(); + mTracksWork[i].clear(); + mMatchedTracksIndex[i].clear(); + } + + for (int it = 0; it < o2::globaltracking::MatchHMP::trackType::SIZE; it++) { + mTracksIndexCache[it].clear(); + if (mMCTruthON) { + mTracksLblWork[it].clear(); + } + } + + mHMPTriggersIndexCache.clear(); + + bool isPrepareHMPClusters = prepareHMPClusters(); + + if (!isPrepareHMPClusters) { // check cluster before of tracks to see also if MC is required + return; + } + + // mExtraTPCFwdTime.clear(); + + mTimerTot.Start(); + if (!prepareTracks()) { + return; + } + + if (mIsITSTPCused || mIsTPCTRDused || mIsITSTPCTRDused || mIsITSTPCTOFused || mIsTPCTOFused || mIsITSTPCTRDTOFused || mIsTPCTRDTOFused) { + doMatching(); + } + + mIsTPCused = false; + mIsITSTPCused = false; + mIsTPCTRDused = false; + mIsITSTPCTRDused = false; + mIsTPCTOFused = false; + mIsITSTPCTRDTOFused = false; + mIsTPCTRDTOFused = false; +} +//================================================================================================================================================== +bool MatchHMP::prepareTracks() +{ + + auto creator = [this](auto& trk, GTrackID gid, float time0, float terr) { + const int nclustersMin = 0; + if constexpr (isTPCTrack()) { + if (trk.getNClusters() < nclustersMin) { + return true; + } + if (std::abs(trk.getQ2Pt()) > mMaxInvPt) { + return true; + } + this->addTPCSeed(trk, gid, time0, terr); + } + if constexpr (isTPCITSTrack()) { + if (trk.getParamOut().getX() < o2::constants::geom::XTPCOuterRef - 1.) { + return true; + } + this->addITSTPCSeed(trk, gid, time0, terr); + } + if constexpr (isTRDTrack()) { + this->addTRDSeed(trk, gid, time0, terr); + } + if constexpr (isTPCTOFTrack()) { + this->addTPCTOFSeed(trk, gid, time0, terr); + } + return true; + }; + + mRecoCont->createTracksVariadic(creator); + + for (int it = 0; it < o2::globaltracking::MatchHMP::trackType::SIZE; it++) { + mMatchedTracksIndex[it].resize(mTracksWork[it].size()); + std::fill(mMatchedTracksIndex[it].begin(), mMatchedTracksIndex[it].end(), -1); // initializing all to -1 + } + + // Unconstrained tracks + /* + if (mIsTPCused) { + + // sort tracks in each sector according to their time (increasing in time) + // for (int sec = o2::constants::math::NSectors; sec--;) { + auto& indexCache = mTracksIndexCache[o2::globaltracking::MatchHMP::trackType::UNCONS]; + LOG(debug) << indexCache.size() << " tracks"; + if (!indexCache.size()) { + return false; + } + std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { + auto& trcA = mTracksWork[o2::globaltracking::MatchHMP::trackType::UNCONS][a].second; + auto& trcB = mTracksWork[o2::globaltracking::MatchHMP::trackType::UNCONS][b].second; + return ((trcA.getTimeStamp() - trcA.getTimeStampError()) - (trcB.getTimeStamp() - trcB.getTimeStampError()) < 0.); + }); + // } // loop over tracks of single sector + } // unconstrained tracks + */ + // Constrained tracks + + if (mIsITSTPCused || mIsTPCTRDused || mIsITSTPCTRDused || mIsITSTPCTOFused || mIsTPCTOFused || mIsITSTPCTRDTOFused || mIsTPCTRDTOFused) { + + auto& indexCache = mTracksIndexCache[o2::globaltracking::MatchHMP::trackType::CONSTR]; + LOG(debug) << indexCache.size() << " tracks"; + if (!indexCache.size()) { + return false; + } + std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { + auto& trcA = mTracksWork[o2::globaltracking::MatchHMP::trackType::CONSTR][a].second; + auto& trcB = mTracksWork[o2::globaltracking::MatchHMP::trackType::CONSTR][b].second; + return ((trcA.getTimeStamp() - mSigmaTimeCut * trcA.getTimeStampError()) - (trcB.getTimeStamp() - mSigmaTimeCut * trcB.getTimeStampError()) < 0.); + }); + // } // loop over tracks of single sector + } // constrained tracks + + return true; +} +//______________________________________________ +void MatchHMP::addITSTPCSeed(const o2::dataformats::TrackTPCITS& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) +{ + mIsITSTPCused = true; + + auto trc = _tr.getParamOut(); + o2::track::TrackLTIntegral intLT0 = _tr.getLTIntegralOut(); + + timeEst ts(time0, terr); + + addConstrainedSeed(trc, srcGID, ts); +} +//______________________________________________ +void MatchHMP::addTRDSeed(const o2::trd::TrackTRD& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) +{ + if (srcGID.getSource() == o2::dataformats::GlobalTrackID::TPCTRD) { + mIsTPCTRDused = true; + } else if (srcGID.getSource() == o2::dataformats::GlobalTrackID::ITSTPCTRD) { + mIsITSTPCTRDused = true; + } else if (srcGID.getSource() == o2::dataformats::GlobalTrackID::TPCTRDTOF) { + mIsTPCTRDTOFused = true; + } else if (srcGID.getSource() == o2::dataformats::GlobalTrackID::ITSTPCTRDTOF) { + mIsTPCTRDTOFused = true; + } else { // shouldn't happen + LOG(error) << "MatchHMP::addTRDSee: srcGID.getSource() = " << int(srcGID.getSource()) << " not allowed; expected ones are: " << int(o2::dataformats::GlobalTrackID::TPCTRD) << " and " << int(o2::dataformats::GlobalTrackID::ITSTPCTRD) << " and " << int(o2::dataformats::GlobalTrackID::TPCTRDTOF) << " and " << int(o2::dataformats::GlobalTrackID::ITSTPCTRDTOF); + } + + auto trc = _tr.getOuterParam(); + + o2::track::TrackLTIntegral intLT0 = _tr.getLTIntegralOut(); + + // o2::dataformats::TimeStampWithError + timeEst ts(time0, terr + mExtraTimeToleranceTRD); + + addConstrainedSeed(trc, srcGID, ts); +} +//______________________________________________ +void MatchHMP::addTPCTOFSeed(const o2::dataformats::TrackTPCTOF& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) +{ + if (srcGID.getSource() == o2::dataformats::GlobalTrackID::TPCTOF) { + mIsTPCTOFused = true; + } else if (srcGID.getSource() == o2::dataformats::GlobalTrackID::TPCTRDTOF) { + mIsTPCTRDTOFused = true; + } else if (srcGID.getSource() == o2::dataformats::GlobalTrackID::ITSTPCTRDTOF) { + mIsITSTPCTRDTOFused = true; + } else { // shouldn't happen + LOG(error) << "MatchHMP::addTPCTOFCSeed: srcGID.getSource() = " << int(srcGID.getSource()) << " not allowed; expected ones are: " << int(o2::dataformats::GlobalTrackID::TPCTOF) << " and " << int(o2::dataformats::GlobalTrackID::TPCTRDTOF) << " and " << int(o2::dataformats::GlobalTrackID::ITSTPCTRDTOF); + } + + auto trc = _tr.getParamOut(); + + timeEst ts(time0, terr + mExtraTimeToleranceTOF); + + addConstrainedSeed(trc, srcGID, ts); +} +//______________________________________________ +void MatchHMP::addConstrainedSeed(o2::track::TrackParCov& trc, o2::dataformats::GlobalTrackID srcGID, timeEst timeMUS) +{ + std::array globalPos; + + // current track index + int it = mTracksWork[o2::globaltracking::MatchHMP::trackType::CONSTR].size(); + + auto prop = o2::base::Propagator::Instance(); + float bxyz[3]; + prop->getFieldXYZ(trc.getXYZGlo(), bxyz); + double bz = -bxyz[2]; + + float pCut = 0.; + + if (TMath::Abs(bz - 5.0) < 0.5) { + pCut = 0.450; + } + + if (TMath::Abs(bz - 2.0) < 0.5) { + pCut = 0.200; + } + + if (trc.getP() > pCut && TMath::Abs(trc.getTgl()) < 0.544 && TMath::Abs(trc.getPhi() - TMath::Pi()) > (TMath::Pi() * 0.5)) { + // create working copy of track param + mTracksWork[o2::globaltracking::MatchHMP::trackType::CONSTR].emplace_back(std::make_pair(trc, timeMUS)); + + mTrackGid[o2::globaltracking::MatchHMP::trackType::CONSTR].emplace_back(srcGID); + + if (mMCTruthON) { + mTracksLblWork[o2::globaltracking::MatchHMP::trackType::CONSTR].emplace_back(mRecoCont->getTPCITSTrackMCLabel(srcGID)); + } + + mTracksIndexCache[o2::globaltracking::MatchHMP::trackType::CONSTR].push_back(it); + } +} +//______________________________________________ +void MatchHMP::addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) +{ + mIsTPCused = true; + + std::array globalPos; + + // current track index + int it = mTracksWork[o2::globaltracking::MatchHMP::trackType::UNCONS].size(); + + // create working copy of track param + timeEst timeInfo; + // set + float extraErr = 0; + + auto trc = _tr.getOuterParam(); + + float trackTime0 = _tr.getTime0() * mTPCTBinMUS; + + timeInfo.setTimeStampError((_tr.getDeltaTBwd() + 5) * mTPCTBinMUS + extraErr); + // mExtraTPCFwdTime.push_back((_tr.getDeltaTFwd() + 5) * mTPCTBinMUS + extraErr); + + timeInfo.setTimeStamp(trackTime0); + + trc.getXYZGlo(globalPos); + + mTracksWork[o2::globaltracking::MatchHMP::trackType::UNCONS].emplace_back(std::make_pair(trc, timeInfo)); + + if (mMCTruthON) { + mTracksLblWork[o2::globaltracking::MatchHMP::trackType::UNCONS].emplace_back(mRecoCont->getTPCTrackMCLabel(srcGID)); + } + + mTracksIndexCache[o2::globaltracking::MatchHMP::trackType::UNCONS].push_back(it); +} +//================================================================================================================================================== +bool MatchHMP::prepareHMPClusters() +{ + mHMPClustersArray = mRecoCont->getHMPClusters(); + mHMPTriggersArray = mRecoCont->getHMPClusterTriggers(); + + mHMPClusLabels = mRecoCont->getHMPClustersMCLabels(); + mMCTruthON = mHMPClusLabels && mHMPClusLabels->getNElements(); + + mNumOfTriggers = 0; + + mHMPTriggersWork.clear(); + + int nTriggersInCurrentChunk = mHMPTriggersArray.size(); + LOG(debug) << "nTriggersInCurrentChunk = " << nTriggersInCurrentChunk; + mNumOfTriggers += nTriggersInCurrentChunk; + mHMPTriggersWork.reserve(mHMPTriggersWork.size() + mNumOfTriggers); + for (int it = 0; it < nTriggersInCurrentChunk; it++) { + const Trigger& clOrig = mHMPTriggersArray[it]; + // create working copy of track param + mHMPTriggersWork.emplace_back(clOrig); + // cache work track index + mHMPTriggersIndexCache.push_back(mHMPTriggersWork.size() - 1); + } + + // sort hmp events according to their time (increasing in time) + auto& indexCache = mHMPTriggersIndexCache; + LOG(debug) << indexCache.size() << " HMP triggers"; + if (!indexCache.size()) { + return false; + } + + std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { + auto& clA = mHMPTriggersWork[a]; + auto& clB = mHMPTriggersWork[b]; + auto timeA = o2::InteractionRecord::bc2ns(clA.getBc(), clA.getOrbit()); + auto timeB = o2::InteractionRecord::bc2ns(clB.getBc(), clB.getOrbit()); + return (timeA - timeB) < 0.; }); + + return true; +} +//================================================================================================================================================== +void MatchHMP::doMatching() +{ + o2::globaltracking::MatchHMP::trackType type = o2::globaltracking::MatchHMP::trackType::CONSTR; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; // material correction method + Recon* recon = new o2::hmpid::Recon(); + o2::hmpid::Param* pParam = o2::hmpid::Param::instance(); + + const float kdRadiator = 10.; // distance between radiator and the plane + + //< do the real matching + auto& cacheTriggerHMP = mHMPTriggersIndexCache; // array of cached HMP triggers indices; reminder: they are ordered in time! + auto& cacheTrk = mTracksIndexCache[type]; // array of cached tracks indices; + int nTracks = cacheTrk.size(), nHMPtriggers = cacheTriggerHMP.size(); + + LOG(debug) << " ************************number of tracks: " << nTracks << ", number of HMP triggers: " << nHMPtriggers; + if (!nTracks || !nHMPtriggers) { + return; + } + + auto prop = o2::base::Propagator::Instance(); + + float bxyz[3]; + + double cluLORS[2] = {0}; + + LOG(debug) << "Trying to match %d tracks" << cacheTrk.size(); + + float timeFromTF = o2::InteractionRecord::bc2ns(mStartIR.bc, mStartIR.orbit); + + for (int ievt = 0; ievt < cacheTriggerHMP.size(); ievt++) { // events loop + + auto& event = mHMPTriggersWork[cacheTriggerHMP[ievt]]; + auto evtTime = event.getIr().differenceInBCMUS(mStartIR); + + int evtTracks = 0; + + for (int itrk = 0; itrk < cacheTrk.size(); itrk++) { // tracks loop + + auto& trackWork = mTracksWork[type][cacheTrk[itrk]]; + auto& trackGid = mTrackGid[type][cacheTrk[itrk]]; + auto& trefTrk = trackWork.first; + + prop->getFieldXYZ(trefTrk.getXYZGlo(), bxyz); + double bz = -bxyz[2]; + + double timeUncert = trackWork.second.getTimeStampError(); + + float minTrkTime = (trackWork.second.getTimeStamp() - mSigmaTimeCut * timeUncert); + float maxTrkTime = (trackWork.second.getTimeStamp() + mSigmaTimeCut * timeUncert); + + // if (evtTime < (maxTrkTime + timeFromTF) && evtTime > (minTrkTime + timeFromTF)) { + if (evtTime < maxTrkTime && evtTime > minTrkTime) { + + evtTracks++; + + MatchInfo matching(999999, mTrackGid[type][cacheTrk[itrk]]); + + matching.setHMPIDtrk(0, 0, 0, 0); // no intersection found + matching.setHMPIDmip(0, 0, 0, 0); // store mip info in any case + matching.setIdxHMPClus(99, 99999); // chamber not found, mip not yet considered + matching.setHMPsignal(Recon::kNotPerformed); // ring reconstruction not yet performed + matching.setIdxTrack(trackGid); + TrackHMP hmpTrk(trefTrk); // create a hmpid track to be used for propagation and matching + + hmpTrk.set(trefTrk.getX(), trefTrk.getAlpha(), trefTrk.getParams(), trefTrk.getCharge(), trefTrk.getPID()); + + double xPc, yPc, xRa, yRa, theta, phi; + + Int_t iCh = intTrkCha(&trefTrk, xPc, yPc, xRa, yRa, theta, phi, bz); // find the intersected chamber for this track + if (iCh < 0) { + continue; + } // no intersection at all, go next track + + matching.setHMPIDtrk(xPc, yPc, theta, phi); // store initial infos + matching.setIdxHMPClus(iCh, 9999); // set chamber, index of cluster + cluster size + + int index = -1; + + double dmin = 999999; //, distCut = 1.; + + bool isOkDcut = kFALSE; + bool isOkQcut = kFALSE; + bool isMatched = kFALSE; + + Cluster* bestHmpCluster = nullptr; // the best matching cluster + std::vector oneEventClusters; + + for (int j = event.getFirstEntry(); j <= event.getLastEntry(); j++) { // event clusters loop + auto& cluster = (o2::hmpid::Cluster&)mHMPClustersArray[j]; + + if (cluster.ch() != iCh) { + continue; + } + oneEventClusters.push_back(cluster); + double qthre = pParam->qCut(); + + if (cluster.q() < 150. || cluster.size() > 10) { + continue; + } + + isOkQcut = kTRUE; + + cluLORS[0] = cluster.x(); + cluLORS[1] = cluster.y(); // get the LORS coordinates of the cluster + + double dist = 0.; + + if (TMath::Abs((xPc - cluLORS[0]) * (xPc - cluLORS[0]) + (yPc - cluLORS[1]) * (yPc - cluLORS[1])) > 0.0001) { + + dist = TMath::Sqrt((xPc - cluLORS[0]) * (xPc - cluLORS[0]) + (yPc - cluLORS[1]) * (yPc - cluLORS[1])); + } + + if (dist < dmin) { + dmin = dist; + index = oneEventClusters.size() - 1; + bestHmpCluster = &cluster; + } + + } // event clusters loop + + // 2. Propagate track to the MIP cluster using the central method + + if (!bestHmpCluster) { + oneEventClusters.clear(); + continue; + } + + TVector3 vG = pParam->lors2Mars(iCh, bestHmpCluster->x(), bestHmpCluster->y()); + float gx = vG.X(); + float gy = vG.Y(); + float gz = vG.Z(); + float alpha = TMath::ATan2(gy, gx); + float radiusH = TMath::Sqrt(gy * gy + gx * gx); + if (!(hmpTrk.rotate(alpha))) { + continue; + } + if (!prop->PropagateToXBxByBz(hmpTrk, radiusH, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, matCorr)) { + oneEventClusters.clear(); + continue; + } + + // 3. Update the track with MIP cluster (Improved angular and position resolution - to be used for Cherenkov angle calculation) + + o2::track::TrackParCov trackC(hmpTrk); + + std::array trkPos{0, gz}; + std::array trkCov{0.1 * 0.1, 0., 0.1 * 0.1}; + + // auto chi2 = trackC.getPredictedChi2(trkPos, trkCov); + trackC.update(trkPos, trkCov); + + // 4. Propagate back the constrained track to the radiator radius + + TrackHMP hmpTrkConstrained(trackC); + hmpTrkConstrained.set(trackC.getX(), trackC.getAlpha(), trackC.getParams(), trackC.getCharge(), trackC.getPID()); + if (!prop->PropagateToXBxByBz(hmpTrkConstrained, radiusH - kdRadiator, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, matCorr)) { + oneEventClusters.clear(); + continue; + } + + float hmpMom = hmpTrkConstrained.getP() * hmpTrkConstrained.getSign(); + + matching.setHmpMom(hmpMom); + + // 5. Propagation in the last 10 cm with the fast method + + double xPc0 = 0., yPc0 = 0.; + intTrkCha(iCh, &hmpTrkConstrained, xPc0, yPc0, xRa, yRa, theta, phi, bz); + + // 6. Set match information + + int cluSize = bestHmpCluster->size(); + matching.setHMPIDmip(bestHmpCluster->x(), bestHmpCluster->y(), (int)bestHmpCluster->q(), 0); // store mip info in any case + matching.setMipClusSize(bestHmpCluster->size()); + matching.setIdxHMPClus(iCh, index + 1000 * cluSize); // set chamber, index of cluster + cluster size + matching.setHMPIDtrk(xPc, yPc, theta, phi); + + if (!isOkQcut) { + matching.setHMPsignal(pParam->kMipQdcCut); + } + + // dmin recalculated + + dmin = TMath::Sqrt((xPc - bestHmpCluster->x()) * (xPc - bestHmpCluster->x()) + (yPc - bestHmpCluster->y()) * (yPc - bestHmpCluster->y())); + + if (dmin < 6.) { + isOkDcut = kTRUE; + } + // isOkDcut = kTRUE; // switch OFF cut + + if (!isOkDcut) { + matching.setHMPsignal(pParam->kMipDistCut); // closest cluster with enough charge is still too far from intersection + } + + if (isOkQcut * isOkDcut) { + isMatched = kTRUE; + } // MIP-Track matched !! + + if (!isMatched) { + mMatchedTracks[type].push_back(matching); + oneEventClusters.clear(); + continue; + } // If matched continue... + + double nmean = pParam->meanIdxRad(); + + // 7. Calculate the Cherenkov angle + + recon->setImpPC(xPc, yPc); // store track impact to PC + recon->ckovAngle(&matching, oneEventClusters, index, nmean, xRa, yRa); // search for Cerenkov angle of this track + + mMatchedTracks[type].push_back(matching); + + oneEventClusters.clear(); + + } // if matching in time + } // tracks loop + } // events loop + + delete recon; + recon = nullptr; +} +//================================================================================================================================================== +int MatchHMP::intTrkCha(o2::track::TrackParCov* pTrk, double& xPc, double& yPc, double& xRa, double& yRa, double& theta, double& phi, double bz) +{ + // Static method to find intersection in between given track and HMPID chambers + // Arguments: pTrk- ESD track; xPc,yPc- track intersection with PC in LORS [cm] + // Returns: intersected chamber ID or -1 + TrackHMP* hmpTrk = new TrackHMP(*pTrk); // create a hmpid track to be used for propagation and matching + for (Int_t i = o2::hmpid::Param::kMinCh; i <= o2::hmpid::Param::kMaxCh; i++) { // chambers loop + Int_t chInt = intTrkCha(i, hmpTrk, xPc, yPc, xRa, yRa, theta, phi, bz); + if (chInt >= 0) { + delete hmpTrk; + hmpTrk = nullptr; + return chInt; + } + } // chambers loop + delete hmpTrk; + hmpTrk = nullptr; + return -1; // no intersection with HMPID chambers +} // IntTrkCha() +//================================================================================================================================================== +int MatchHMP::intTrkCha(int ch, o2::dataformats::TrackHMP* pHmpTrk, double& xPc, double& yPc, double& xRa, double& yRa, double& theta, double& phi, double bz) +{ + // Static method to find intersection in between given track and HMPID chambers + // Arguments: pTrk- HMPID track; xPc,yPc- track intersection with PC in LORS [cm] + // Returns: intersected chamber ID or -1 + o2::hmpid::Param* pParam = o2::hmpid::Param::instance(); + Double_t p1[3], n1[3]; + pParam->norm(ch, n1); + pParam->point(ch, p1, o2::hmpid::Param::kRad); // point & norm for middle of radiator plane + Double_t p2[3], n2[3]; + pParam->norm(ch, n2); + pParam->point(ch, p2, o2::hmpid::Param::kPc); // point & norm for entrance to PC plane + + if (pHmpTrk->intersect(p1, n1, bz) == kFALSE) { + return -1; + } // try to intersect track with the middle of radiator + if (pHmpTrk->intersect(p2, n2, bz) == kFALSE) { + return -1; + } + pParam->mars2LorsVec(ch, n1, theta, phi); // track angles at RAD + pParam->mars2Lors(ch, p1, xRa, yRa); // TRKxRAD position + pParam->mars2Lors(ch, p2, xPc, yPc); // TRKxPC position + + if (pParam->isInside(xPc, yPc, pParam->distCut()) == kTRUE) { + return ch; + } // return intersected chamber + return -1; // no intersection with HMPID chambers +} // IntTrkCha() +//================================================================================================================================================== diff --git a/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx b/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx index 575f0dd9a975f..440bda85813a3 100644 --- a/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx +++ b/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx @@ -15,11 +15,11 @@ #include "Framework/InputSpec.h" #include "ReconstructionDataFormats/TrackParametrization.h" #include "DetectorsBase/Propagator.h" -#include "DetectorsBase/GeometryManager.h" #include "SimulationDataFormat/MCUtils.h" #include #include "TGraphAsymmErrors.h" #include "GlobalTracking/TrackCuts.h" +#include using namespace o2::globaltracking; using namespace o2::mcutils; @@ -36,27 +36,76 @@ MatchITSTPCQC::~MatchITSTPCQC() void MatchITSTPCQC::deleteHistograms() { - // Pt - delete mPt; - delete mPtTPC; - delete mFractionITSTPCmatch; - delete mPtPhysPrim; - delete mPtTPCPhysPrim; - delete mFractionITSTPCmatchPhysPrim; - // Phi - delete mPhi; - delete mPhiTPC; - delete mFractionITSTPCmatchPhi; - delete mPhiPhysPrim; - delete mPhiTPCPhysPrim; - delete mFractionITSTPCmatchPhiPhysPrim; - // Eta - delete mEta; - delete mEtaTPC; - delete mFractionITSTPCmatchEta; - delete mEtaPhysPrim; - delete mEtaTPCPhysPrim; - delete mFractionITSTPCmatchEtaPhysPrim; + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + delete mPtNum[i]; + delete mPtDen[i]; + delete mFractionITSTPCmatch[i]; + delete mPtNum_noEta0[i]; + delete mPtDen_noEta0[i]; + delete mFractionITSTPCmatch_noEta0[i]; + delete mPtPhysPrimNum[i]; + delete mPtPhysPrimDen[i]; + delete mFractionITSTPCmatchPhysPrim[i]; + + // Phi + delete mPhiNum[i]; + delete mPhiDen[i]; + delete mFractionITSTPCmatchPhi[i]; + delete mPhiPhysPrimNum[i]; + delete mPhiPhysPrimDen[i]; + delete mFractionITSTPCmatchPhiPhysPrim[i]; + delete mPhiVsPtNum[i]; + delete mPhiVsPtDen[i]; + delete mFractionITSTPCmatchPhiVsPt[i]; + + // Eta + delete mEtaNum[i]; + delete mEtaDen[i]; + delete mFractionITSTPCmatchEta[i]; + delete mEtaPhysPrimNum[i]; + delete mEtaPhysPrimDen[i]; + delete mFractionITSTPCmatchEtaPhysPrim[i]; + delete mEtaVsPtNum[i]; + delete mEtaVsPtDen[i]; + delete mFractionITSTPCmatchEtaVsPt[i]; + + // Clusters + delete mClsVsPtNum[i]; + delete mClsVsPtDen[i]; + delete mFractionITSTPCmatchClsVsPt[i]; + + // Chi2 + delete mChi2VsPtNum[i]; + delete mChi2VsPtDen[i]; + delete mFractionITSTPCmatchChi2VsPt[i]; + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + delete mPtNumVsTrkPID[i][j]; + delete mPtDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchPtVsTrkPID[i][j]; + // Phi + delete mPhiNumVsTrkPID[i][j]; + delete mPhiDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchPhiVsTrkPID[i][j]; + // Eta + delete mEtaNumVsTrkPID[i][j]; + delete mEtaDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchEtaVsTrkPID[i][j]; + } + } + + // 1/Pt + delete m1OverPtNum[i]; + delete m1OverPtDen[i]; + delete mFractionITSTPCmatch1OverPt[i]; + delete m1OverPtPhysPrimNum[i]; + delete m1OverPtPhysPrimDen[i]; + delete mFractionITSTPCmatchPhysPrim1OverPt[i]; + } + // Residuals delete mResidualPt; delete mResidualPhi; @@ -65,27 +114,76 @@ void MatchITSTPCQC::deleteHistograms() delete mChi2Matching; delete mChi2Refit; delete mTimeResVsPt; + delete mDCAr; + delete mDCArVsPtNum; + delete mDCArVsPtDen; + delete mFractionITSTPCmatchDCArVsPt; } //__________________________________________________________ void MatchITSTPCQC::reset() { - // Pt - mPt->Reset(); - mPtTPC->Reset(); - mPtPhysPrim->Reset(); - mPtTPCPhysPrim->Reset(); - // Phi - mPhi->Reset(); - mPhiTPC->Reset(); - mPhiPhysPrim->Reset(); - mPhiTPCPhysPrim->Reset(); - // Eta - mEta->Reset(); - mEtaTPC->Reset(); - mEtaPhysPrim->Reset(); - mEtaTPCPhysPrim->Reset(); + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + mPtNum[i]->Reset(); + mPtDen[i]->Reset(); + mPtNum_noEta0[i]->Reset(); + mPtDen_noEta0[i]->Reset(); + + // Phi + mPhiNum[i]->Reset(); + mPhiDen[i]->Reset(); + mPhiVsPtNum[i]->Reset(); + mPhiVsPtDen[i]->Reset(); + + // Eta + mEtaNum[i]->Reset(); + mEtaDen[i]->Reset(); + mEtaVsPtNum[i]->Reset(); + mEtaVsPtDen[i]->Reset(); + + // Clusters + mClsVsPtNum[i]->Reset(); + mClsVsPtDen[i]->Reset(); + + // Chi2 + mChi2VsPtNum[i]->Reset(); + mChi2VsPtDen[i]->Reset(); + + // 1/Pt + m1OverPtNum[i]->Reset(); + m1OverPtDen[i]->Reset(); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + mPtNumVsTrkPID[i][j]->Reset(); + mPtDenVsTrkPID[i][j]->Reset(); + // Phi + mPhiNumVsTrkPID[i][j]->Reset(); + mPhiDenVsTrkPID[i][j]->Reset(); + // Eta + mEtaNumVsTrkPID[i][j]->Reset(); + mEtaDenVsTrkPID[i][j]->Reset(); + } + } + + if (mUseMC) { + mPtPhysPrimNum[i]->Reset(); + mPtPhysPrimDen[i]->Reset(); + + mPhiPhysPrimNum[i]->Reset(); + mPhiPhysPrimDen[i]->Reset(); + + mEtaPhysPrimNum[i]->Reset(); + mEtaPhysPrimDen[i]->Reset(); + + m1OverPtPhysPrimNum[i]->Reset(); + m1OverPtPhysPrimDen[i]->Reset(); + } + } + // Residuals mResidualPt->Reset(); mResidualPhi->Reset(); @@ -94,36 +192,18 @@ void MatchITSTPCQC::reset() mChi2Matching->Reset(); mChi2Refit->Reset(); mTimeResVsPt->Reset(); + mDCAr->Reset(); + mDCArVsPtNum->Reset(); + mDCArVsPtDen->Reset(); } //__________________________________________________________ bool MatchITSTPCQC::init() { - - mPtTPC = new TH1D("mPtTPC", "Pt distribution of TPC tracks; Pt [GeV/c]; dNdPt", 100, 0.f, 20.f); - mFractionITSTPCmatch = new TEfficiency("mFractionITSTPCmatch", "Fraction of ITSTPC matched tracks vs Pt; Pt [GeV/c]; Eff", 100, 0.f, 20.f); - mPt = new TH1D("mPt", "Pt distribution of matched tracks; Pt [GeV/c]; dNdPt", 100, 0.f, 20.f); - mPhiTPC = new TH1F("mPhiTPC", "Phi distribution of TPC tracks; Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchPhi = new TEfficiency("mFractionITSTPCmatchPhi", "Fraction of ITSTPC matched tracks vs Phi; Phi [rad]; Eff", 100, 0.f, 2 * TMath::Pi()); - mPhi = new TH1F("mPhi", "Phi distribution of matched tracks; Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchEta = new TEfficiency("mFractionITSTPCmatchEta", "Fraction of ITSTPC matched tracks vs Eta; Eta; Eff", 100, -2.f, 2.f); - // These will be empty in case of no MC info... - mPhiTPCPhysPrim = new TH1F("mPhiTPCPhysPrim", "Phi distribution of TPC tracks (physical primary); Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchPhiPhysPrim = new TEfficiency("mFractionITSTPCmatchPhiPhysPrim", "Fraction of ITSTPC matched tracks vs Phi (physical primary); Phi [rad]; Eff", 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchEtaPhysPrim = new TEfficiency("mFractionITSTPCmatchEtaPhysPrim", "Fraction of ITSTPC matched tracks vs Eta (physical primary); Eta; Eff", 100, -2.f, 2.f); - mPhiPhysPrim = new TH1F("mPhiPhysPrim", "Phi distribution of matched tracks (physical primary); Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mEtaPhysPrim = new TH1F("mEtaPhysPrim", "Eta distribution of matched tracks (physical primary); Eta; dNdEta", 100, -2.f, 2.f); - mEtaTPCPhysPrim = new TH1F("mEtaTPCPhysPrim", "Eta distribution of TPC tracks (physical primary); Eta; dNdEta", 100, -2.f, 2.f); - // ...till here - mEta = new TH1F("mEta", "Eta distribution of matched tracks; Eta; dNdEta", 100, -2.f, 2.f); - mEtaTPC = new TH1F("mEtaTPC", "Eta distribution of TPC tracks; Eta; dNdEta", 100, -2.f, 2.f); - - mResidualPt = new TH2F("mResidualPt", "Residuals of ITS-TPC matching in #it{p}_{T}; #it{p}_{T}^{ITS-TPC} [GeV/c]; #it{p}_{T}^{ITS-TPC} - #it{p}_{T}^{TPC} [GeV/c]", 100, 0.f, 20.f, 100, -1.f, 1.f); - mResidualPhi = new TH2F("mResidualPhi", "Residuals of ITS-TPC matching in #it{#phi}; #it{#phi}^{ITS-TPC} [rad]; #it{#phi}^{ITS-TPC} - #it{#phi}^{TPC} [rad]", 100, 0.f, 2 * TMath::Pi(), 100, -1.f, 1.f); - mResidualEta = new TH2F("mResidualEta", "Residuals of ITS-TPC matching in #it{#eta}; #it{#eta}^{ITS-TPC}; #it{#eta}^{ITS-TPC} - #it{#eta}^{TPC}", 100, -2.f, 2.f, 100, -1.f, 1.f); - mChi2Matching = new TH1F("mChi2Matching", "Chi2 of matching; chi2", 300, 0, 300); - mChi2Refit = new TH1F("mChi2Refit", "Chi2 of refit; chi2", 200, 0, 200); - + LOGP(debug, "Creating Variable Binning"); + std::array title{"TPC", "ITS"}; + std::array etaSel{"", ", |eta| < 0.9"}; + std::array maxNCls{156, 7}; // log binning for pT const Int_t nbinsPt = 100; const Double_t xminPt = 0.01; @@ -136,43 +216,151 @@ bool MatchITSTPCQC::init() Double_t xlogPt = xlogminPt + i * dlogxPt; xbinsPt[i] = TMath::Exp(TMath::Log(10) * xlogPt); } - mTimeResVsPt = new TH2F("mTimeResVsPt", "Time resolution vs Pt; Pt [GeV/c]; time res [us]", nbinsPt, xbinsPt, 100, 0.f, 2.f); - mPtTPCPhysPrim = new TH1F("mPtTPPhysPrimC", "Pt distribution of TPC tracks (physical primary); Pt [GeV/c]; dNdPt", nbinsPt, xbinsPt); - mFractionITSTPCmatchPhysPrim = new TEfficiency("mFractionITSTPCmatchPhysPrim", "Fraction of ITSTPC matched tracks vs Pt (physical primary); Pt [GeV/c]; Eff", nbinsPt, xbinsPt); - mPtPhysPrim = new TH1F("mPtPhysPrim", "Pt distribution of matched tracks (physical primary); Pt [GeV/c]; dNdPt", nbinsPt, xbinsPt); - - mPtTPC->Sumw2(); - mPt->Sumw2(); - mPhiTPC->Sumw2(); - mPhi->Sumw2(); - mPtTPCPhysPrim->Sumw2(); - mPtPhysPrim->Sumw2(); - mPhiTPCPhysPrim->Sumw2(); - mPhiPhysPrim->Sumw2(); - mEtaTPC->Sumw2(); - mEtaPhysPrim->Sumw2(); - mEtaTPCPhysPrim->Sumw2(); - - mPtTPC->SetOption("logy"); - mPt->SetOption("logy"); - mEta->SetOption("logy"); - mChi2Matching->SetOption("logy"); - mChi2Refit->SetOption("logy"); - mTimeResVsPt->SetOption("colz logz logy logx"); - mPtTPC->GetYaxis()->SetTitleOffset(1.4); - mPt->GetYaxis()->SetTitleOffset(1.4); - mEta->GetYaxis()->SetTitleOffset(1.4); + LOGP(debug, "Creating Histograms"); + // Data and MC + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + mPtNum[i] = new TH1D(Form("mPtNum_%s", title[i].c_str()), Form("Pt distribution of ITSTPC matched tracks, wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtNum[i]->Sumw2(); + mPtNum[i]->SetOption("logy"); + mPtNum[i]->GetYaxis()->SetTitleOffset(1.4); + mPtDen[i] = new TH1D(Form("mPtDen_%s", title[i].c_str()), Form("Pt distribution of %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtDen[i]->Sumw2(); + mPtDen[i]->SetOption("logy"); + mPtDen[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatch[i] = new TEfficiency(Form("mFractionITSTPCmatch_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtNum_noEta0[i] = new TH1D(Form("mPtNum_noEta0_%s", title[i].c_str()), Form("Pt distribution of ITSTPC matched tracks without |eta| < 0.05, wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtNum_noEta0[i]->Sumw2(); + mPtNum_noEta0[i]->SetOption("logy"); + mPtNum_noEta0[i]->GetYaxis()->SetTitleOffset(1.4); + mPtDen_noEta0[i] = new TH1D(Form("mPtDen_noEta0_%s", title[i].c_str()), Form("Pt distribution of %s tracks without |eta| < 0.05 %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtDen_noEta0[i]->Sumw2(); + mPtDen_noEta0[i]->SetOption("logy"); + mPtDen_noEta0[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatch_noEta0[i] = new TEfficiency(Form("mFractionITSTPCmatch_noEta0_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt without |eta| < 0.05 %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + + // Phi + mPhiNum[i] = new TH1F(Form("mPhiNum_%s", title[i].c_str()), Form("Phi distribution of ITSTPC matched tracks, wrt %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiNum[i]->Sumw2(); + mPhiDen[i] = new TH1F(Form("mPhiDen_%s", title[i].c_str()), Form("Phi distribution of %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiDen[i]->Sumw2(); + mFractionITSTPCmatchPhi[i] = new TEfficiency(Form("mFractionITSTPCmatchPhi_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Phi wrt %s tracks %s; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtNum[i] = new TH2F(Form("mPhiVsPtNum_%s", title[i].c_str()), Form("Phi vs Pt distribution of ITSTPC matched tracks wrt %s %s; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtNum[i]->Sumw2(); + mPhiVsPtDen[i] = new TH2F(Form("mPhiVsPtDen_%s", title[i].c_str()), Form("Phi vs Pt distribution of %s tracks %s; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchPhiVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchPhiVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks %s, Phi vs Pt; #it{p}_{T} [GeV#it{c}]; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + + // Eta + mEtaNum[i] = new TH1F(Form("mEtaNum_%s", title[i].c_str()), Form("Eta distribution of ITSTPC matched tracks, wrt %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaNum[i]->Sumw2(); + mEtaNum[i]->GetYaxis()->SetTitleOffset(1.4); + mEtaDen[i] = new TH1F(Form("mEtaDen_%s", title[i].c_str()), Form("Eta distribution of %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaDen[i]->Sumw2(); + mEtaDen[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatchEta[i] = new TEfficiency(Form("mFractionITSTPCmatchEta_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks , wrt %s tracks, vs Eta; Eta; Eff", title[i].c_str()), 100, -2.f, 2.f); + mEtaVsPtNum[i] = new TH2F(Form("mEtaVsPtNum_%s", title[i].c_str()), Form("Eta vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; Eta", title[i].c_str()), 100, 0.f, 20.f, 100, -2.f, 2.f); + mEtaVsPtNum[i]->Sumw2(); + mEtaVsPtDen[i] = new TH2F(Form("mEtaVsPtDen_%s", title[i].c_str()), Form("Eta vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; Eta", title[i].c_str()), 100, 0.f, 20.f, 100, -2.f, 2.f); + mEtaVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchEtaVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchEtaVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, Eta vs Pt; #it{p}_{T} [GeV#it{c}]; Eta; Eff", title[i].c_str()), 100, 0.f, 20.f, 100, -2.f, 2.f); + + // Clusters + mClsVsPtNum[i] = new TH2F(Form("mClsVsPtNum_%s", title[i].c_str()), Form("#Clusters vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; #Clusters", title[i].c_str()), 100, 0.f, 20.f, maxNCls[i], 0, maxNCls[i]); + mClsVsPtNum[i]->Sumw2(); + mClsVsPtDen[i] = new TH2F(Form("mClsVsPtDen_%s", title[i].c_str()), Form("#Clusters vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; #Clusters", title[i].c_str()), 100, 0.f, 20.f, maxNCls[i], 0, maxNCls[i]); + mClsVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchClsVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchClsVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, #Clusters vs Pt; #it{p}_{T} [GeV#it{c}]; #Clusters; Eff", title[i].c_str()), 100, 0.f, 20.f, maxNCls[i], 0, maxNCls[i]); + + // Chi2 + mChi2VsPtNum[i] = new TH2F(Form("mChi2VsPtNum_%s", title[i].c_str()), Form("Chi2 vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; Chi2", title[i].c_str()), 100, 0.f, 20.f, 200, 0, 300); + mChi2VsPtNum[i]->Sumw2(); + mChi2VsPtDen[i] = new TH2F(Form("mChi2VsPtDen_%s", title[i].c_str()), Form("Chi2 vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; Chi2", title[i].c_str()), 100, 0.f, 20.f, 200, 0, 300); + mChi2VsPtDen[i]->Sumw2(); + mFractionITSTPCmatchChi2VsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchChi2VsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, Chi2 vs Pt; #it{p}_{T} [GeV#it{c}]; Chi2; Eff", title[i].c_str()), 100, 0.f, 20.f, 200, 0, 300); + + // 1/pt + m1OverPtNum[i] = new TH1D(Form("m1OverPtNum_%s", title[i].c_str()), Form("1/Pt distribution of matched tracks, wrt %s tracks %s; 1/Pt [c/GeV]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtNum[i]->Sumw2(); + m1OverPtDen[i] = new TH1D(Form("m1OverPtDen_%s", title[i].c_str()), Form("1/Pt distribution of %s tracks %s; 1/Pt [c/GeV]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtDen[i]->Sumw2(); + mFractionITSTPCmatch1OverPt[i] = new TEfficiency(Form("mFractionITSTPCmatch1OverPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs 1/Pt, wrt %s tracks %s; 1/Pt [c/GeV]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + mPtNumVsTrkPID[i][j] = new TH1D(Form("mPtNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Pt distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 20.f); + mPtNumVsTrkPID[i][j]->Sumw2(); + mPtDenVsTrkPID[i][j] = new TH1D(Form("mPtDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Pt distribution of %s tracks %s, TrkPID %i; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 20.f); + mPtDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchPtVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchPtVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt %s, TrkPID %i; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 20.f); + + // Phi + mPhiNumVsTrkPID[i][j] = new TH1D(Form("mPhiNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Phi distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + mPhiNumVsTrkPID[i][j]->Sumw2(); + mPhiDenVsTrkPID[i][j] = new TH1D(Form("mPhiDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Phi distribution of %s tracks %s, TrkPID %i; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + mPhiDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchPhiVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchPhiVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Phi %s, TrkPID %i; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + + // Eta + mEtaNumVsTrkPID[i][j] = new TH1D(Form("mEtaNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Eta distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Eta; dNdEta", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + mEtaNumVsTrkPID[i][j]->Sumw2(); + mEtaDenVsTrkPID[i][j] = new TH1D(Form("mEtaDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Eta distribution of %s tracks %s, TrkPID %i; Eta; dNdEta", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + mEtaDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchEtaVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchEtaVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Eta %s, TrkPID %i; Eta; Eff", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + } + } + } + + mResidualPt = new TH2F("mResidualPt", "Residuals of ITS-TPC matching in #it{p}_{T}; #it{p}_{T}^{ITS-TPC} [GeV/c]; #it{p}_{T}^{ITS-TPC} - #it{p}_{T}^{TPC} [GeV/c]", 100, 0.f, 20.f, 100, -1.f, 1.f); + mResidualPhi = new TH2F("mResidualPhi", "Residuals of ITS-TPC matching in #it{#phi}; #it{#phi}^{ITS-TPC} [rad]; #it{#phi}^{ITS-TPC} - #it{#phi}^{TPC} [rad]", 100, 0.f, 2 * TMath::Pi(), 100, -1.f, 1.f); + mResidualEta = new TH2F("mResidualEta", "Residuals of ITS-TPC matching in #it{#eta}; #it{#eta}^{ITS-TPC}; #it{#eta}^{ITS-TPC} - #it{#eta}^{TPC}", 100, -2.f, 2.f, 100, -1.f, 1.f); + mChi2Matching = new TH1F("mChi2Matching", "Chi2 of matching; chi2", 200, 0, 300); + mChi2Matching->SetOption("logy"); mChi2Matching->GetYaxis()->SetTitleOffset(1.4); + mChi2Refit = new TH1F("mChi2Refit", "Chi2 of refit; chi2", 200, 0, 300); + mChi2Refit->SetOption("logy"); mChi2Refit->GetYaxis()->SetTitleOffset(1.4); - mTimeResVsPt->GetYaxis()->SetTitleOffset(1.4); + mDCAr = new TH1F("mDCAr", "DCA of TPC tracks; DCAr", 200, -100, 100); + mDCArVsPtNum = new TH2F("mDCArVsPtNum", "DCA of TPC tracks Vs Pt Num; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 200, -30, 30); + mDCArVsPtNum->Sumw2(); + mDCArVsPtDen = new TH2F("mDCArVsPtDen", "DCA of TPC tracks Vs Pt Den; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 200, -30, 30); + mDCArVsPtDen->Sumw2(); + mFractionITSTPCmatchDCArVsPt = new TEfficiency("mFractionITSTPCmatchDCArVsPt", "Fraction of ITSTPC matched tracks wrt TPC vs DCAr; #it{p}_{T} [GeV#it{c}]; DCAr; Eff", 100, 0, 20., 200, -30, 30); - o2::base::GeometryManager::loadGeometry(mGeomFileName); - o2::base::Propagator::initFieldFromGRP(mGRPFileName); - mBz = o2::base::Propagator::Instance()->getNominalBz(); + mTimeResVsPt = new TH2F("mTimeResVsPt", "Time resolution vs Pt; Pt [GeV/c]; time res [us]", nbinsPt, xbinsPt, 100, 0.f, 2.f); + mTimeResVsPt->SetOption("colz logz logy logx"); + mTimeResVsPt->GetYaxis()->SetTitleOffset(1.4); if (mUseMC) { mcReader.initFromDigitContext("collisioncontext.root"); + + for (int i = 0; i < matchType::SIZE; ++i) { + mPtPhysPrimNum[i] = new TH1F(Form("mPtPhysPrimNum_%s", title[i].c_str()), Form("Pt distribution of matched tracks (physical primary), wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + mPtPhysPrimNum[i]->Sumw2(); + mPtPhysPrimDen[i] = new TH1F(Form("mPtPhysPrimDen_%s", title[i].c_str()), Form("Pt distribution of %s tracks (physical primary) %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + mPtPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhiPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchPhiPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Phi (physical primary), wrt %s tracks %s; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + + mEtaPhysPrimNum[i] = new TH1F(Form("mEtaPhysPrimNum_%s", title[i].c_str()), Form("Eta distribution of matched tracks (physical primary), wrt %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaPhysPrimNum[i]->Sumw2(); + mEtaPhysPrimDen[i] = new TH1F(Form("mEtaPhysPrimDen_%s", title[i].c_str()), Form("Eta distribution of %s tracks (physical primary); Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchEtaPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchEtaPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Eta (physical primary), wrt %s tracks; Eta; Eff", title[i].c_str()), 100, -2.f, 2.f); + + mPhiPhysPrimNum[i] = new TH1F(Form("mPhiPhysPrimNum_%s", title[i].c_str()), Form("Phi distribution of matched tracks (physical primary), wrt %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiPhysPrimNum[i]->Sumw2(); + mPhiPhysPrimDen[i] = new TH1F(Form("mPhiPhysPrimDen_%s", title[i].c_str()), Form("Phi distribution of %s tracks (physical primary) %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Pt (physical primary), wrt %s tracks %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + + m1OverPtPhysPrimNum[i] = new TH1D(Form("m1OverPtPhysPrimNum_%s", title[i].c_str()), Form("1/Pt distribution of matched tracks (physical primary), wrt %s tracks %s; 1/Pt [c/GeV]; dNd1/Pt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtPhysPrimNum[i]->Sumw2(); + m1OverPtPhysPrimDen[i] = new TH1D(Form("m1OverPtPhysPrimDen_%s", title[i].c_str()), Form("1/PtPt distribution of %s tracks (physical primary) %s; 1/Pt [c/GeV]; dNd1/Pt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhysPrim1OverPt[i] = new TEfficiency(Form("mFractionITSTPCmatchPhysPrim1OverPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs 1/Pt (physical primary), wrt %s tracks %s; 1/Pt [c/GeV]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + } } return true; @@ -187,7 +375,7 @@ void MatchITSTPCQC::initDataRequest() mSrc &= mAllowedSources; - if ((mSrc[GID::Source::ITSTPC] == 0 || mSrc[GID::Source::TPC] == 0)) { + if (mSrc[GID::Source::ITSTPC] == 0 || mSrc[GID::Source::TPC] == 0 || mSrc[GID::Source::ITS] == 0) { LOG(fatal) << "We cannot do ITSTPC QC, some sources are missing, check sources in " << mSrc; } @@ -199,51 +387,116 @@ void MatchITSTPCQC::initDataRequest() void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) { + + // Getting the B field + mBz = o2::base::Propagator::Instance()->getNominalBz(); + static int evCount = 0; mRecoCont.collectData(ctx, *mDataRequest.get()); mTPCTracks = mRecoCont.getTPCTracks(); + mITSTracks = mRecoCont.getITSTracks(); mITSTPCTracks = mRecoCont.getTPCITSTracks(); LOG(debug) << "****** Number of found ITSTPC tracks = " << mITSTPCTracks.size(); LOG(debug) << "****** Number of found TPC tracks = " << mTPCTracks.size(); + LOG(debug) << "****** Number of found ITS tracks = " << mITSTracks.size(); - // cache selection for TPC tracks + // cache selection for TPC and ITS tracks std::vector isTPCTrackSelectedEntry(mTPCTracks.size(), false); + std::vector isITSTrackSelectedEntry(mITSTracks.size(), false); TrackCuts cuts; + // ITS track + cuts.setMinPtITSCut(mPtITSCut); + cuts.setEtaITSCut(mEtaITSCut); + cuts.setMinNClustersITS(mMinNClustersITS); + cuts.setMaxChi2PerClusterITS(mMaxChi2PerClusterITS); + for (auto it = mRequiredITSHits.begin(); it != mRequiredITSHits.end(); it++) { + cuts.setRequireHitsInITSLayers((*it).first, (*it).second); + } + // TPC track + cuts.setMinPtTPCCut(mPtTPCCut); + cuts.setEtaTPCCut(mEtaTPCCut); + cuts.setMinNTPCClustersCut(mNTPCClustersCut); + cuts.setMaxDCATPCCut(mDCATPCCut); + cuts.setMaxDCATPCCutY(mDCATPCCutY); + // ITS-TPC track kinematics + cuts.setMinPtCut(mPtCut); + cuts.setMaxPtCut(mPtMaxCut); + cuts.setEtaCut(-mEtaCut, mEtaCut); for (size_t itrk = 0; itrk < mTPCTracks.size(); ++itrk) { auto const& trkTpc = mTPCTracks[itrk]; - // if (selectTrack(trkTpc)) { - // isTPCTrackSelectedEntry[itrk] = true; - // } o2::dataformats::GlobalTrackID id(itrk, GID::TPC); if (cuts.isSelected(id, mRecoCont)) { + // NB: same cuts for numerator and denominator tracks of ITS-TPC matching + // To change cuts only for numerator, something like o2::dataformats::GlobalTrackID id(itrk, GID::ITSTPC) is necessary isTPCTrackSelectedEntry[itrk] = true; } } + for (size_t itrk = 0; itrk < mITSTracks.size(); ++itrk) { + auto const& trkIts = mITSTracks[itrk]; + o2::dataformats::GlobalTrackID id(itrk, GID::ITS); + if (cuts.isSelected(id, mRecoCont)) { + // NB: same cuts for numerator and denominator tracks of ITS-TPC matching + // To change cuts only for numerator, something like o2::dataformats::GlobalTrackID id(itrk, GID::ITSTPC) is necessary + isITSTrackSelectedEntry[itrk] = true; + } + } + // numerator + eta, chi2... if (mUseMC) { - mMapLabels.clear(); + for (int i = 0; i < matchType::SIZE; ++i) { + mMapLabels[i].clear(); + } for (int itrk = 0; itrk < static_cast(mITSTPCTracks.size()); ++itrk) { auto const& trk = mITSTPCTracks[itrk]; auto idxTrkTpc = trk.getRefTPC().getIndex(); + if (trk.getRefITS().getSource() != GID::ITS) { + continue; + } if (isTPCTrackSelectedEntry[idxTrkTpc] == true) { auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); - if (mMapLabels.find(lbl) == mMapLabels.end()) { + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::TPC].find(lbl) == mMapLabels[matchType::TPC].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapLabels[matchType::TPC].insert({lbl, {itrk, true}}); + } else { + mMapLabels[matchType::TPC].insert({lbl, {itrk, false}}); + } + } else { + // winner (if more tracks have the same label) has the highest pt + if (mITSTPCTracks[mMapLabels[matchType::TPC].at(lbl).mIdx].getPt() < trk.getPt()) { + mMapLabels[matchType::TPC].at(lbl).mIdx = itrk; + } + } + } + auto idxTrkIts = trk.getRefITS().getIndex(); + if (isITSTrackSelectedEntry[idxTrkIts] == true) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::ITS].find(lbl) == mMapLabels[matchType::ITS].end()) { int source = lbl.getSourceID(); int event = lbl.getEventID(); const std::vector& pcontainer = mcReader.getTracks(source, event); const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { - mMapLabels.insert({lbl, {itrk, true}}); + mMapLabels[matchType::ITS].insert({lbl, {itrk, true}}); } else { - mMapLabels.insert({lbl, {itrk, false}}); + mMapLabels[matchType::ITS].insert({lbl, {itrk, false}}); } } else { // winner (if more tracks have the same label) has the highest pt - if (mITSTPCTracks[mMapLabels.at(lbl).mIdx].getPt() < trk.getPt()) { - mMapLabels.at(lbl).mIdx = itrk; + if (mITSTPCTracks[mMapLabels[matchType::ITS].at(lbl).mIdx].getPt() < trk.getPt()) { + mMapLabels[matchType::ITS].at(lbl).mIdx = itrk; } } } @@ -251,200 +504,424 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) LOG(info) << "number of entries in map for nominator (without duplicates) = " << mMapLabels.size(); // now we use only the tracks in the map to fill the histograms (--> tracks have passed the // track selection and there are no duplicated tracks wrt the same MC label) - for (auto const& el : mMapLabels) { - auto const& trk = mITSTPCTracks[el.second.mIdx]; - auto const& trkTpc = mTPCTracks[trk.getRefTPC()]; - mPt->Fill(trkTpc.getPt()); - mPhi->Fill(trkTpc.getPhi()); - mEta->Fill(trkTpc.getEta()); - // we fill also the denominator - mPtTPC->Fill(trkTpc.getPt()); - mPhiTPC->Fill(trkTpc.getPhi()); - mEtaTPC->Fill(trkTpc.getEta()); - if (el.second.mIsPhysicalPrimary) { - mPtPhysPrim->Fill(trkTpc.getPt()); - mPhiPhysPrim->Fill(trkTpc.getPhi()); - mEtaPhysPrim->Fill(trkTpc.getEta()); + for (int i = 0; i < matchType::SIZE; ++i) { + for (auto const& el : mMapLabels[i]) { + auto const& trk = mITSTPCTracks[el.second.mIdx]; + o2::track::TrackParCov trkDen; + bool isEtaITSOk = true; + if (i == matchType::TPC) { + trkDen = mTPCTracks[trk.getRefTPC()]; + } else { + trkDen = mITSTracks[trk.getRefITS()]; + if (std::abs(trkDen.getEta()) > 0.9) { + // ITS track outside |eta | < 0.9, we don't fill pt, nor phi , nor phi vs pt histos + isEtaITSOk = false; + } + } + if (isEtaITSOk) { + mPtNum[i]->Fill(trkDen.getPt()); + if (std::abs(trkDen.getEta()) > 0.05) { + mPtNum_noEta0[i]->Fill(trkDen.getPt()); + } + mPhiNum[i]->Fill(trkDen.getPhi()); + mPhiVsPtNum[i]->Fill(trkDen.getPt(), trkDen.getPhi()); + m1OverPtNum[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + // we fill also the denominator + mPtDen[i]->Fill(trkDen.getPt()); + if (std::abs(trkDen.getEta()) > 0.05) { + mPtDen_noEta0[i]->Fill(trkDen.getPt()); + } + mPhiDen[i]->Fill(trkDen.getPhi()); + mPhiVsPtDen[i]->Fill(trkDen.getPt(), trkDen.getPhi()); + m1OverPtDen[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mPtNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPt()); + mPhiNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPhi()); + // we fill also the denominator + mPtDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPt()); + mPhiDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPhi()); + } + } + mEtaNum[i]->Fill(trkDen.getEta()); + mEtaVsPtNum[i]->Fill(trkDen.getPt(), trkDen.getEta()); // we fill also the denominator - mPtTPCPhysPrim->Fill(trkTpc.getPt()); - mPhiTPCPhysPrim->Fill(trkTpc.getPhi()); - mEtaTPCPhysPrim->Fill(trkTpc.getEta()); + mEtaDen[i]->Fill(trkDen.getEta()); + mEtaVsPtDen[i]->Fill(trkDen.getPt(), trkDen.getEta()); + if (i == matchType::TPC) { + auto tpcTrk = mTPCTracks[trk.getRefTPC()]; + mClsVsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + mClsVsPtDen[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtDen[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (tpcTrk.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtNum->Fill(tpcTrk.getPt(), dca[0]); + mDCArVsPtDen->Fill(tpcTrk.getPt(), dca[0]); + } + } else { + const auto& itsTrk = mITSTracks[trk.getRefITS()]; + mClsVsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + mClsVsPtDen[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtDen[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + } + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mEtaNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getEta()); + // we fill also the denominator + mEtaDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getEta()); + } + if (el.second.mIsPhysicalPrimary) { + if (isEtaITSOk) { + mPtPhysPrimNum[i]->Fill(trkDen.getPt()); + mPhiPhysPrimNum[i]->Fill(trkDen.getPhi()); + m1OverPtPhysPrimNum[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + // we fill also the denominator + mPtPhysPrimDen[i]->Fill(trkDen.getPt()); + mPhiPhysPrimDen[i]->Fill(trkDen.getPhi()); + m1OverPtPhysPrimDen[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + } + mEtaPhysPrimNum[i]->Fill(trkDen.getEta()); + // we fill also the denominator + mEtaPhysPrimDen[i]->Fill(trkDen.getEta()); + } + ++mNITSTPCSelectedTracks[i]; } - ++mNITSTPCSelectedTracks; } } - + int iITSTPC = 0; for (auto const& trk : mITSTPCTracks) { if (trk.getRefTPC().getIndex() >= mTPCTracks.size()) { - LOG(fatal) << "******************** ATTENTION! idx = " << trk.getRefTPC().getIndex() << ", size of container = " << mTPCTracks.size() << " in TF " << evCount; - continue; + LOG(fatal) << "******************** ATTENTION! for TPC track associated to matched track: idx = " << trk.getRefTPC().getIndex() << ", size of container = " << mTPCTracks.size() << " in TF " << evCount; } - auto const& trkTpc = mTPCTracks[trk.getRefTPC()]; - auto idxTrkTpc = trk.getRefTPC().getIndex(); - if (isTPCTrackSelectedEntry[idxTrkTpc] == true) { - if (!mUseMC) { - mPt->Fill(trkTpc.getPt()); - mPhi->Fill(trkTpc.getPhi()); - mEta->Fill(trkTpc.getEta()); + std::array title{"TPC", "ITS"}; + for (int i = 0; i < matchType::SIZE; ++i) { + o2::track::TrackParCov trkRef; + int idxTrkRef; + bool fillHisto = false; + bool isEtaITSOk = true; + if (i == matchType::TPC) { + trkRef = mTPCTracks[trk.getRefTPC()]; + idxTrkRef = trk.getRefTPC().getIndex(); + if (isTPCTrackSelectedEntry[idxTrkRef] == true) { + fillHisto = true; + ++mNITSTPCSelectedTracks[i]; + } + } else { + idxTrkRef = trk.getRefITS().getIndex(); + if (trk.getRefITS().getSource() == GID::ITSAB) { + // do not use afterburner tracks + LOG(debug) << "Track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " is from afterburner"; + continue; + } + if (idxTrkRef >= mITSTracks.size()) { + LOG(fatal) << "******************** ATTENTION! for ITS track associated to matched track (NOT from AB): idx = " << trk.getRefITS().getIndex() << ", size of container = " << mITSTracks.size() << " in TF " << evCount; + } + trkRef = mITSTracks[trk.getRefITS()]; + LOG(debug) << "Checking track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " and pt = " << trkRef.getPt(); + if (isITSTrackSelectedEntry[idxTrkRef] == true) { + LOG(debug) << "Track was selected (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " , we keep it in the numerator, pt = " << trkRef.getPt(); + fillHisto = true; + ++mNITSTPCSelectedTracks[i]; + } else { + LOG(debug) << "Track was not selected (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " , we don't keep it in the numerator, pt = " << trkRef.getPt(); + } + if (std::abs(trkRef.getEta()) > 0.9) { + // ITS track outside |eta | < 0.9, we don't fill pt, nor phi , nor phi vs pt histos + isEtaITSOk = false; + LOG(debug) << "Track (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " will be discarded when filling pt of phi related histograms, since eta = " << trkRef.getEta() << " , we don't keep it in the numerator, pt = " << trkRef.getPt(); + } + } + if (fillHisto == true) { + if (!mUseMC) { + LOG(debug) << "Filling num (" << title[i] << ") with track with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " with pt = " << trkRef.getPt(); + if (isEtaITSOk) { + mPtNum[i]->Fill(trkRef.getPt()); + if (std::abs(trkRef.getEta()) > 0.05) { + mPtNum_noEta0[i]->Fill(trkRef.getPt()); + } + mPhiNum[i]->Fill(trkRef.getPhi()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mPtNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getPt()); + mPhiNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getPhi()); + } + mPhiVsPtNum[i]->Fill(trkRef.getPt(), trkRef.getPhi()); + m1OverPtNum[i]->Fill(trkRef.getSign() * trkRef.getPtInv()); + } + mEtaNum[i]->Fill(trkRef.getEta()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mEtaNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getEta()); + } + mEtaVsPtNum[i]->Fill(trkRef.getPt(), trkRef.getEta()); + if (i == matchType::TPC) { + const auto& tpcTrk = mTPCTracks[trk.getRefTPC()]; + mClsVsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + } else { + const auto& itsTrk = mITSTracks[trk.getRefITS()]; + mClsVsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + } + } + if (i == matchType::TPC) { + mResidualPt->Fill(trk.getPt(), trk.getPt() - trkRef.getPt()); + mResidualPhi->Fill(trk.getPhi(), trk.getPhi() - trkRef.getPhi()); + mResidualEta->Fill(trk.getEta(), trk.getEta() - trkRef.getEta()); + mChi2Matching->Fill(trk.getChi2Match()); + mChi2Refit->Fill(trk.getChi2Refit()); + mTimeResVsPt->Fill(trkRef.getPt(), trk.getTimeMUS().getTimeStampError()); + math_utils::Point3D v{}; + std::array dca; + if (trkRef.propagateParamToDCA(v, mBz, &dca)) { + mDCAr->Fill(dca[0]); + if (!mUseMC) { + mDCArVsPtNum->Fill(trkRef.getPt(), dca[0]); + } + } + LOG(debug) << "*** chi2Matching = " << trk.getChi2Match() << ", chi2refit = " << trk.getChi2Refit() << ", timeResolution = " << trk.getTimeMUS().getTimeStampError(); + } + } else { + LOG(debug) << "Not filling num (" << title[i] << ") for ITSTPC track " << iITSTPC << " for track with pt " << trkRef.getPt(); } - mResidualPt->Fill(trk.getPt(), trk.getPt() - trkTpc.getPt()); - mResidualPhi->Fill(trk.getPhi(), trk.getPhi() - trkTpc.getPhi()); - mResidualEta->Fill(trk.getEta(), trk.getEta() - trkTpc.getEta()); - mChi2Matching->Fill(trk.getChi2Match()); - mChi2Refit->Fill(trk.getChi2Refit()); - mTimeResVsPt->Fill(trkTpc.getPt(), trk.getTimeMUS().getTimeStampError()); - LOG(debug) << "*** chi2Matching = " << trk.getChi2Match() << ", chi2refit = " << trk.getChi2Refit() << ", timeResolution = " << trk.getTimeMUS().getTimeStampError(); - ++mNITSTPCSelectedTracks; } + ++iITSTPC; } // now filling the denominator for the efficiency calculation if (mUseMC) { - mMapTPCLabels.clear(); + for (int i = 0; i < matchType::SIZE; ++i) { + mMapRefLabels[i].clear(); + } // filling the map where we store for each MC label, the track id of the reconstructed // track with the highest number of TPC clusters for (int itrk = 0; itrk < static_cast(mTPCTracks.size()); ++itrk) { auto const& trk = mTPCTracks[itrk]; if (isTPCTrackSelectedEntry[itrk] == true) { auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::TPC}); - if (mMapLabels.find(lbl) != mMapLabels.end()) { + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::TPC].find(lbl) != mMapLabels[matchType::TPC].end()) { // the track was already added to the denominator continue; } - if (mMapTPCLabels.find(lbl) == mMapTPCLabels.end()) { + if (mMapRefLabels[matchType::TPC].find(lbl) == mMapRefLabels[matchType::TPC].end()) { int source = lbl.getSourceID(); int event = lbl.getEventID(); const std::vector& pcontainer = mcReader.getTracks(source, event); const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { - mMapTPCLabels.insert({lbl, {itrk, true}}); + mMapRefLabels[matchType::TPC].insert({lbl, {itrk, true}}); } else { - mMapTPCLabels.insert({lbl, {itrk, false}}); + mMapRefLabels[matchType::TPC].insert({lbl, {itrk, false}}); } } else { // winner (if more tracks have the same label) has the highest number of TPC clusters - if (mTPCTracks[mMapTPCLabels.at(lbl).mIdx].getNClusters() < trk.getNClusters()) { - mMapTPCLabels.at(lbl).mIdx = itrk; + if (mTPCTracks[mMapRefLabels[matchType::TPC].at(lbl).mIdx].getNClusters() < trk.getNClusters()) { + mMapRefLabels[matchType::TPC].at(lbl).mIdx = itrk; } } } } - LOG(info) << "number of entries in map for denominator (without duplicates) = " << mMapTPCLabels.size() + mMapLabels.size(); + // same for ITS + // filling the map where we store for each MC label, the track id of the reconstructed + // track with the highest number of ITS clusters + for (int itrk = 0; itrk < static_cast(mITSTracks.size()); ++itrk) { + auto const& trk = mITSTracks[itrk]; + if (isITSTrackSelectedEntry[itrk] == true) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITS}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::ITS].find(lbl) != mMapLabels[matchType::ITS].end()) { + // the track was already added to the denominator + continue; + } + if (mMapRefLabels[matchType::ITS].find(lbl) == mMapRefLabels[matchType::ITS].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapRefLabels[matchType::ITS].insert({lbl, {itrk, true}}); + } else { + mMapRefLabels[matchType::ITS].insert({lbl, {itrk, false}}); + } + } else { + // winner (if more tracks have the same label) has the highest number of ITS clusters + if (mITSTracks[mMapRefLabels[matchType::ITS].at(lbl).mIdx].getNClusters() < trk.getNClusters()) { + mMapRefLabels[matchType::ITS].at(lbl).mIdx = itrk; + } + } + } + } + LOG(info) << "number of entries in map for denominator of TPC tracks (without duplicates) = " << mMapRefLabels[matchType::TPC].size() + mMapLabels[matchType::TPC].size(); + LOG(info) << "number of entries in map for denominator of ITS tracks (without duplicates) = " << mMapRefLabels[matchType::ITS].size() + mMapLabels[matchType::ITS].size(); // now we use only the tracks in the map to fill the histograms (--> tracks have passed the // track selection and there are no duplicated tracks wrt the same MC label) - for (auto const& el : mMapTPCLabels) { + for (auto const& el : mMapRefLabels[matchType::TPC]) { auto const& trk = mTPCTracks[el.second.mIdx]; - mPtTPC->Fill(trk.getPt()); - mPhiTPC->Fill(trk.getPhi()); - mEtaTPC->Fill(trk.getEta()); + mPtDen[matchType::TPC]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::TPC]->Fill(trk.getPt()); + } + mPhiDen[matchType::TPC]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getPhi()); + mEtaDen[matchType::TPC]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getEta()); + m1OverPtDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); + mClsVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (auto trc = trk; trc.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtDen->Fill(trc.getPt(), dca[0]); + } if (el.second.mIsPhysicalPrimary) { - mPtTPCPhysPrim->Fill(trk.getPt()); - mPhiTPCPhysPrim->Fill(trk.getPhi()); - mEtaTPCPhysPrim->Fill(trk.getEta()); + mPtPhysPrimDen[matchType::TPC]->Fill(trk.getPt()); + mPhiPhysPrimDen[matchType::TPC]->Fill(trk.getPhi()); + mEtaPhysPrimDen[matchType::TPC]->Fill(trk.getEta()); + m1OverPtPhysPrimDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); } ++mNTPCSelectedTracks; } + for (auto const& el : mMapRefLabels[matchType::ITS]) { + auto const& trk = mITSTracks[el.second.mIdx]; + if (std::abs(trk.getEta()) < 0.9) { + mPtDen[matchType::ITS]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::ITS]->Fill(trk.getPt()); + } + mPhiDen[matchType::ITS]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getPhi()); + m1OverPtDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } + mEtaDen[matchType::ITS]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getEta()); + mClsVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getChi2()); + if (el.second.mIsPhysicalPrimary) { + if (std::abs(trk.getEta()) < 0.9) { + mPtPhysPrimDen[matchType::ITS]->Fill(trk.getPt()); + mPhiPhysPrimDen[matchType::ITS]->Fill(trk.getPhi()); + m1OverPtPhysPrimDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } + mEtaPhysPrimDen[matchType::ITS]->Fill(trk.getEta()); + } + ++mNITSSelectedTracks; + } } else { // if we are in data, we loop over all tracks (no check on the label) for (size_t itrk = 0; itrk < mTPCTracks.size(); ++itrk) { auto const& trk = mTPCTracks[itrk]; if (isTPCTrackSelectedEntry[itrk] == true) { - mPtTPC->Fill(trk.getPt()); - mPhiTPC->Fill(trk.getPhi()); - mEtaTPC->Fill(trk.getEta()); + LOG(debug) << "Filling den (TPC) with track with pt = " << trk.getPt(); + mPtDen[matchType::TPC]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::TPC]->Fill(trk.getPt()); + } else { + LOG(debug) << "Track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta() << " not used for den pt, phi, phi vs pt, 1.pt histos"; + } + mPhiDen[matchType::TPC]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getPhi()); + mEtaDen[matchType::TPC]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getEta()); + m1OverPtDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); + mClsVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (auto trc = trk; trc.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtDen->Fill(trc.getPt(), dca[0]); + } ++mNTPCSelectedTracks; } } + for (size_t itrk = 0; itrk < mITSTracks.size(); ++itrk) { + auto const& trk = mITSTracks[itrk]; + LOG(debug) << "Checking den for track (ITS) " << itrk << " with pt " << trk.getPt() << " and eta = " << trk.getEta(); + if (isITSTrackSelectedEntry[itrk] == true) { + if (std::abs(trk.getEta()) < 0.9) { + LOG(debug) << "Filling den for track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta(); + mPtDen[matchType::ITS]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::ITS]->Fill(trk.getPt()); + } + mPhiDen[matchType::ITS]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getPhi()); + m1OverPtDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } else { + LOG(debug) << "Track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta() << " not used for num pt, phi, phi vs pt, 1.pt histos"; + } + mEtaDen[matchType::ITS]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getEta()); + mClsVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getChi2()); + ++mNITSSelectedTracks; + } else { + LOG(debug) << "Not filling for this track (ITS) " << itrk << " with pt = " << trk.getPt(); + } + } } evCount++; } //__________________________________________________________ - -bool MatchITSTPCQC::selectTrack(o2::tpc::TrackTPC const& track) -{ - - if (track.getPt() < mPtCut) { - return false; - } - if (std::abs(track.getEta()) > mEtaCut) { - return false; - } - if (track.getNClusters() < mNTPCClustersCut) { - return false; - } - - math_utils::Point3D v{}; - std::array dca; - if (!(const_cast(track).propagateParamToDCA(v, mBz, &dca, mDCACut)) || std::abs(dca[0]) > mDCACutY) { - return false; - } - - return true; -} - -//__________________________________________________________ - void MatchITSTPCQC::finalize() { + std::array title{"TPC", "ITS"}; + // first we use denominators and nominators to set the TEfficiency; later they are scaled - for (int i = 0; i < mPtTPC->GetNbinsX(); ++i) { - if (mPtTPC->GetBinContent(i + 1) < mPt->GetBinContent(i + 1)) { - LOG(error) << "bin " << i + 1 << ": mPtTPC[i] = " << mPtTPC->GetBinContent(i + 1) << ", mPt[i] = " << mPt->GetBinContent(i + 1); + // some checks + for (int ti = 0; ti < matchType::SIZE; ++ti) { + for (int i = 0; i < mPtDen[ti]->GetNbinsX(); ++i) { + if (mPtDen[ti]->GetBinContent(i + 1) < mPtNum[ti]->GetBinContent(i + 1)) { + LOG(error) << title[ti] << ": bin " << i + 1 << " in [" << mPtNum[ti]->GetBinLowEdge(i + 1) << " , " << mPtNum[ti]->GetBinLowEdge(i + 1) + mPtNum[ti]->GetBinWidth(i + 1) << "]: mPtDen[i] = " << mPtDen[ti]->GetBinContent(i + 1) << ", mPtNum[i] = " << mPtNum[ti]->GetBinContent(i + 1); + } } - } - for (int i = 0; i < mPhiTPC->GetNbinsX(); ++i) { - if (mPhiTPC->GetBinContent(i + 1) < mPhi->GetBinContent(i + 1)) { - LOG(error) << "bin " << i + 1 << ": mPhiTPC[i] = " << mPhiTPC->GetBinContent(i + 1) << ", mPhi[i] = " << mPhi->GetBinContent(i + 1); + for (int i = 0; i < mPtDen_noEta0[ti]->GetNbinsX(); ++i) { + if (mPtDen_noEta0[ti]->GetBinContent(i + 1) < mPtNum_noEta0[ti]->GetBinContent(i + 1)) { + LOG(error) << title[ti] << ": bin " << i + 1 << " in [" << mPtNum_noEta0[ti]->GetBinLowEdge(i + 1) << " , " << mPtNum_noEta0[ti]->GetBinLowEdge(i + 1) + mPtNum_noEta0[ti]->GetBinWidth(i + 1) << "]: mPtDen_noEta0[i] = " << mPtDen_noEta0[ti]->GetBinContent(i + 1) << ", mPtNum_noEta0[i] = " << mPtNum_noEta0[ti]->GetBinContent(i + 1); + } } - } - for (int i = 0; i < mEtaTPC->GetNbinsX(); ++i) { - if (mEtaTPC->GetBinContent(i + 1) < mEta->GetBinContent(i + 1)) { - LOG(error) << "bin " << i + 1 << ": mEtaTPC[i] = " << mEtaTPC->GetBinContent(i + 1) << ", mEta[i] = " << mEta->GetBinContent(i + 1); + for (int i = 0; i < mPhiDen[ti]->GetNbinsX(); ++i) { + if (mPhiDen[ti]->GetBinContent(i + 1) < mPhiNum[ti]->GetBinContent(i + 1)) { + LOG(error) << title[ti] << ": bin " << i + 1 << " in [" << mPhiNum[ti]->GetBinLowEdge(i + 1) << " , " << mPhiNum[ti]->GetBinLowEdge(i + 1) + mPhiNum[ti]->GetBinWidth(i + 1) << "]: mPhiDen[i] = " << mPhiDen[ti]->GetBinContent(i + 1) << ", mPhiNum[i] = " << mPhiNum[ti]->GetBinContent(i + 1); + } } - } - - // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents - if (!mFractionITSTPCmatch->SetTotalHistogram(*mPtTPC, "f") || - !mFractionITSTPCmatch->SetPassedHistogram(*mPt, "")) { - LOG(fatal) << "Something went wrong when defining the efficiency histograms vs Pt!"; - } - mFractionITSTPCmatch->SetTitle(Form("%s;%s;%s", mFractionITSTPCmatch->GetTitle(), mPt->GetXaxis()->GetTitle(), "Efficiency")); - - if (!mFractionITSTPCmatchPhi->SetTotalHistogram(*mPhiTPC, "f") || - !mFractionITSTPCmatchPhi->SetPassedHistogram(*mPhi, "")) { - LOG(fatal) << "Something went wrong when defining the efficiency histograms vs Phi!"; - } - mFractionITSTPCmatchPhi->SetTitle(Form("%s;%s;%s", mFractionITSTPCmatchPhi->GetTitle(), mPhi->GetXaxis()->GetTitle(), "Efficiency")); - - if (!mFractionITSTPCmatchEta->SetTotalHistogram(*mEtaTPC, "f") || - !mFractionITSTPCmatchEta->SetPassedHistogram(*mEta, "")) { - LOG(fatal) << "Something went wrong when defining the efficiency histograms vs Eta!"; - } - mFractionITSTPCmatchEta->SetTitle(Form("%s;%s;%s", mFractionITSTPCmatchEta->GetTitle(), mEta->GetXaxis()->GetTitle(), "Efficiency")); - - if (mUseMC) { - if (!mFractionITSTPCmatchPhysPrim->SetTotalHistogram(*mPtTPCPhysPrim, "f") || - !mFractionITSTPCmatchPhysPrim->SetPassedHistogram(*mPtPhysPrim, "")) { - LOG(fatal) << "Something went wrong when defining the efficiency histograms vs Pt (PhysPrim)!"; + for (int i = 0; i < mEtaDen[ti]->GetNbinsX(); ++i) { + if (mEtaDen[ti]->GetBinContent(i + 1) < mEtaNum[ti]->GetBinContent(i + 1)) { + LOG(error) << title[ti] << ": bin " << i + 1 << " in [" << mEtaNum[ti]->GetBinLowEdge(i + 1) << " , " << mEtaNum[ti]->GetBinLowEdge(i + 1) + mEtaNum[ti]->GetBinWidth(i + 1) << "]: mEtaDen[i] = " << mEtaDen[ti]->GetBinContent(i + 1) << ", mEtaNum[i] = " << mEtaNum[ti]->GetBinContent(i + 1); + } } - mFractionITSTPCmatchPhysPrim->SetTitle(Form("%s;%s;%s", mFractionITSTPCmatchPhysPrim->GetTitle(), mPtPhysPrim->GetXaxis()->GetTitle(), "Efficiency")); - if (!mFractionITSTPCmatchPhiPhysPrim->SetTotalHistogram(*mPhiTPCPhysPrim, "f") || - !mFractionITSTPCmatchPhiPhysPrim->SetPassedHistogram(*mPhiPhysPrim, "")) { - LOG(fatal) << "Something went wrong when defining the efficiency histograms vs Phi (PhysPrim)!"; + // filling the efficiency + setEfficiency(mFractionITSTPCmatch[ti], mPtNum[ti], mPtDen[ti]); + setEfficiency(mFractionITSTPCmatch_noEta0[ti], mPtNum_noEta0[ti], mPtDen_noEta0[ti]); + setEfficiency(mFractionITSTPCmatchPhi[ti], mPhiNum[ti], mPhiDen[ti]); + setEfficiency(mFractionITSTPCmatchEta[ti], mEtaNum[ti], mEtaDen[ti]); + setEfficiency(mFractionITSTPCmatchPhiVsPt[ti], mPhiVsPtNum[ti], mPhiVsPtDen[ti], true); + setEfficiency(mFractionITSTPCmatchEtaVsPt[ti], mEtaVsPtNum[ti], mEtaVsPtDen[ti], true); + setEfficiency(mFractionITSTPCmatch1OverPt[ti], m1OverPtNum[ti], m1OverPtDen[ti]); + setEfficiency(mFractionITSTPCmatchClsVsPt[ti], mClsVsPtNum[ti], mClsVsPtDen[ti], true); + setEfficiency(mFractionITSTPCmatchChi2VsPt[ti], mChi2VsPtNum[ti], mChi2VsPtDen[ti], true); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + setEfficiency(mFractionITSTPCmatchPtVsTrkPID[ti][j], mPtNumVsTrkPID[ti][j], mPtDenVsTrkPID[ti][j]); + setEfficiency(mFractionITSTPCmatchPhiVsTrkPID[ti][j], mPhiNumVsTrkPID[ti][j], mPhiDenVsTrkPID[ti][j]); + setEfficiency(mFractionITSTPCmatchEtaVsTrkPID[ti][j], mEtaNumVsTrkPID[ti][j], mEtaDenVsTrkPID[ti][j]); + } } - mFractionITSTPCmatchPhiPhysPrim->SetTitle(Form("%s;%s;%s", mFractionITSTPCmatchPhiPhysPrim->GetTitle(), mPhiPhysPrim->GetXaxis()->GetTitle(), "Efficiency")); - - if (!mFractionITSTPCmatchEtaPhysPrim->SetTotalHistogram(*mEtaTPCPhysPrim, "f") || - !mFractionITSTPCmatchEtaPhysPrim->SetPassedHistogram(*mEtaPhysPrim, "")) { - LOG(fatal) << "Something went wrong when defining the efficiency histograms vs Eta (PhysPrim)!"; + if (mUseMC) { + setEfficiency(mFractionITSTPCmatchPhysPrim[ti], mPtPhysPrimNum[ti], mPtPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchPhiPhysPrim[ti], mPhiPhysPrimNum[ti], mPhiPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchEtaPhysPrim[ti], mEtaPhysPrimNum[ti], mEtaPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchPhysPrim1OverPt[ti], m1OverPtPhysPrimNum[ti], m1OverPtPhysPrimDen[ti]); } - mFractionITSTPCmatchEtaPhysPrim->SetTitle(Form("%s;%s;%s", mFractionITSTPCmatchEtaPhysPrim->GetTitle(), mEtaPhysPrim->GetXaxis()->GetTitle(), "Efficiency")); } - + setEfficiency(mFractionITSTPCmatchDCArVsPt, mDCArVsPtNum, mDCArVsPtDen, true); /* mPtTPC->Scale(scaleFactTPC); mPt->Scale(scaleFactITSTPC); @@ -465,29 +942,150 @@ void MatchITSTPCQC::finalize() //__________________________________________________________ +void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool is2D) +{ + if (eff == nullptr) { + LOG(fatal) << "Cannot get TEfficiency object "; + } + if (hnum == nullptr) { + LOG(fatal) << "Cannot get numerator histogram for TEfficiency object " << eff->GetName(); + } + if (hden == nullptr) { + LOG(fatal) << "Cannot get denominator histogram for TEfficiency object " << eff->GetName(); + } + + // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents + if constexpr (false) { // checking + bool bad{false}; + LOG(info) << "Setting efficiency " << eff->GetName() << " from " << hnum->GetName() << " and " << hden->GetName(); + LOG(info) << "Num " << hnum->GetName() << " " << hnum->GetNbinsX() << " " << hnum->GetNbinsY() << " with " << hnum->GetEntries() << " entries"; + LOG(info) << "Den " << hden->GetName() << " " << hden->GetNbinsX() << " " << hden->GetNbinsY() << " with " << hden->GetEntries() << " entries"; + if (hnum->GetDimension() != hden->GetDimension()) { + LOGP(warning, "Histograms have different dimensions (num={} to den={})", hnum->GetDimension(), hden->GetDimension()); + bad = true; + } + if (!TEfficiency::CheckBinning(*hnum, *hden)) { + LOGP(warning, "Histograms do not have a compatible binning"); + bad = true; + } + if (!is2D) { + for (int i = 1; i <= hden->GetNbinsX(); i++) { + if (hden->GetBinContent(i) < hnum->GetBinContent(i)) { + LOG(warning) << "bin " << i << " den: " << hden->GetBinContent(i) << " < num: " << hnum->GetBinContent(i) << " should be the opposite"; + bad = true; + } + } + } else { + for (int i = 1; i <= hden->GetNbinsX(); i++) { + for (int j = 1; j <= hden->GetNbinsY(); j++) { + if (hden->GetBinContent(i, j) < hnum->GetBinContent(i, j)) { + LOGP(warning, "bin {}/{} -> den: {} < num: {}", i, j, hden->GetBinContent(i, j), hnum->GetBinContent(i, j)); + bad = true; + } + } + } + } + if (bad) { + return; + } + } + // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents + if (!eff->SetTotalHistogram(*hden, "f")) { + LOG(fatal) << "Something went wrong when defining the efficiency denominator " << eff->GetName() << " from " << hnum->GetName(); + } + if (!eff->SetPassedHistogram(*hnum, "")) { + LOG(fatal) << "Something went wrong when defining the efficiency numerator " << eff->GetName() << " from " << hnum->GetName(); + } + if (is2D) { + eff->SetTitle(Form("%s;%s;%s;%s", eff->GetTitle(), hnum->GetXaxis()->GetTitle(), hnum->GetYaxis()->GetTitle(), "Efficiency")); + } else { + eff->SetTitle(Form("%s;%s;%s", eff->GetTitle(), hnum->GetXaxis()->GetTitle(), "Efficiency")); + } +} + +//__________________________________________________________ + void MatchITSTPCQC::getHistos(TObjArray& objar) { - objar.Add(mPtTPC); - objar.Add(mFractionITSTPCmatch); - objar.Add(mPt); - objar.Add(mPhiTPC); - objar.Add(mFractionITSTPCmatchPhi); - objar.Add(mPhi); - objar.Add(mPtTPCPhysPrim); - objar.Add(mFractionITSTPCmatchPhysPrim); - objar.Add(mPtPhysPrim); - objar.Add(mPhiTPCPhysPrim); - objar.Add(mFractionITSTPCmatchPhiPhysPrim); - objar.Add(mPhiPhysPrim); - objar.Add(mEta); + for (int i = 0; i < matchType::SIZE; ++i) { + objar.Add(mPtNum[i]); + objar.Add(mPtDen[i]); + objar.Add(mFractionITSTPCmatch[i]); + + objar.Add(mPtNum_noEta0[i]); + objar.Add(mPtDen_noEta0[i]); + objar.Add(mFractionITSTPCmatch_noEta0[i]); + + objar.Add(mPtPhysPrimNum[i]); + objar.Add(mPtPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhysPrim[i]); + + objar.Add(mPhiNum[i]); + objar.Add(mPhiDen[i]); + objar.Add(mFractionITSTPCmatchPhi[i]); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + objar.Add(mPtNumVsTrkPID[i][j]); + objar.Add(mPtDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchPtVsTrkPID[i][j]); + // Phi + objar.Add(mPhiNumVsTrkPID[i][j]); + objar.Add(mPhiDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchPhiVsTrkPID[i][j]); + // Eta + objar.Add(mEtaNumVsTrkPID[i][j]); + objar.Add(mEtaDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchEtaVsTrkPID[i][j]); + } + } + + objar.Add(mPhiPhysPrimNum[i]); + objar.Add(mPhiPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhiPhysPrim[i]); + + objar.Add(mPhiVsPtNum[i]); + objar.Add(mPhiVsPtDen[i]); + objar.Add(mFractionITSTPCmatchPhiVsPt[i]); + + objar.Add(mEtaNum[i]); + objar.Add(mEtaDen[i]); + objar.Add(mFractionITSTPCmatchEta[i]); + + objar.Add(mEtaPhysPrimNum[i]); + objar.Add(mEtaPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchEtaPhysPrim[i]); + + objar.Add(mEtaVsPtNum[i]); + objar.Add(mEtaVsPtDen[i]); + objar.Add(mFractionITSTPCmatchEtaVsPt[i]); + + objar.Add(mClsVsPtNum[i]); + objar.Add(mClsVsPtDen[i]); + objar.Add(mFractionITSTPCmatchClsVsPt[i]); + + objar.Add(mChi2VsPtNum[i]); + objar.Add(mChi2VsPtDen[i]); + objar.Add(mFractionITSTPCmatchChi2VsPt[i]); + + objar.Add(m1OverPtNum[i]); + objar.Add(m1OverPtDen[i]); + objar.Add(mFractionITSTPCmatch1OverPt[i]); + + objar.Add(m1OverPtPhysPrimNum[i]); + objar.Add(m1OverPtPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhysPrim1OverPt[i]); + } objar.Add(mChi2Matching); objar.Add(mChi2Refit); objar.Add(mTimeResVsPt); - objar.Add(mEtaPhysPrim); - objar.Add(mEtaTPC); - objar.Add(mEtaTPCPhysPrim); objar.Add(mResidualPt); objar.Add(mResidualPhi); objar.Add(mResidualEta); + objar.Add(mDCAr); + objar.Add(mDCArVsPtNum); + objar.Add(mDCArVsPtDen); + objar.Add(mFractionITSTPCmatchDCArVsPt); } diff --git a/Detectors/GlobalTracking/src/MatchTOF.cxx b/Detectors/GlobalTracking/src/MatchTOF.cxx index a8da2c4865401..5416a612ad1f3 100644 --- a/Detectors/GlobalTracking/src/MatchTOF.cxx +++ b/Detectors/GlobalTracking/src/MatchTOF.cxx @@ -46,18 +46,28 @@ #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" #include "TOFBase/Utils.h" +#ifdef WITH_OPENMP +#include +#endif + using namespace o2::globaltracking; using evGIdx = o2::dataformats::EvIndex; using trkType = o2::dataformats::MatchInfoTOFReco::TrackType; using Cluster = o2::tof::Cluster; using GTrackID = o2::dataformats::GlobalTrackID; using timeEst = o2::dataformats::TimeStampWithError; +using TrackTunePar = o2::globaltracking::TrackTuneParams; + +bool MatchTOF::mHasFillScheme = false; +bool MatchTOF::mFillScheme[o2::constants::lhc::LHCMaxBunches] = {0}; ClassImp(MatchTOF); //______________________________________________ -void MatchTOF::run(const o2::globaltracking::RecoContainer& inp) +void MatchTOF::run(const o2::globaltracking::RecoContainer& inp, unsigned long firstTForbit) { + mFirstTForbit = firstTForbit; + if (!mMatchParams) { mMatchParams = &o2::globaltracking::MatchTOFParams::Instance(); mSigmaTimeCut = mMatchParams->nsigmaTimeCut; @@ -79,23 +89,24 @@ void MatchTOF::run(const o2::globaltracking::RecoContainer& inp) mOutTOFLabels[i].clear(); } - for (int i = 0; i < trkType::SIZE; i++) { - mTracksWork[i].clear(); - mTrackGid[i].clear(); - } for (int it = 0; it < trkType::SIZE; it++) { - mMatchedTracksIndex[it].clear(); - mLTinfos[it].clear(); - if (mMCTruthON) { - mTracksLblWork[it].clear(); - } - for (int sec = o2::constants::math::NSectors; sec--;) { + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + mMatchedTracksIndex[sec][it].clear(); + mTracksWork[sec][it].clear(); + mTrackGid[sec][it].clear(); + mLTinfos[sec][it].clear(); + if (mMCTruthON) { + mTracksLblWork[sec][it].clear(); + } mTracksSectIndexCache[it][sec].clear(); + mTracksSeed[it][sec].clear(); } } - mSideTPC.clear(); - mExtraTPCFwdTime.clear(); + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + mSideTPC[sec].clear(); + mExtraTPCFwdTime[sec].clear(); + } mTimerTot.Start(); bool isPrepareTOFClusters = prepareTOFClusters(); @@ -122,32 +133,46 @@ void MatchTOF::run(const o2::globaltracking::RecoContainer& inp) mTimerTot.Start(); std::array nMatches = {0}; - for (int sec = o2::constants::math::NSectors; sec--;) { - mMatchedTracksPairs.clear(); // new sector - LOG(debug) << "Doing matching for sector " << sec << "..."; - if (mIsITSTPCused || mIsTPCTRDused || mIsITSTPCTRDused) { - mTimerMatchITSTPC.Start(sec == o2::constants::math::NSectors - 1); + // run matching (this can be multi-thread) + mTimerMatchITSTPC.Start(); + + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + mMatchedTracksPairsSec[sec].clear(); // new sector + } + + o2::tof::Geo::Init(); + + if (mIsITSTPCused || mIsTPCTRDused || mIsITSTPCTRDused) { +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNlanes) +#endif + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { doMatching(sec); - mTimerMatchITSTPC.Stop(); } - if (mIsTPCused) { - mTimerMatchTPC.Start(sec == o2::constants::math::NSectors - 1); + } + mTimerMatchITSTPC.Stop(); + + mTimerMatchTPC.Start(); + if (mIsTPCused) { +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNlanes) +#endif + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { doMatchingForTPC(sec); - mTimerMatchTPC.Stop(); } + } + mTimerMatchTPC.Stop(); + // finalize + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { if (mStoreMatchable) { - // fill per sector - mMatchedTracksPairsSec[sec].clear(); - mMatchedTracksPairsSec[sec].insert(mMatchedTracksPairsSec[sec].end(), mMatchedTracksPairs.begin(), mMatchedTracksPairs.end()); - // if MC check if good or fake matches if (mMCTruthON) { for (auto& matchingPair : mMatchedTracksPairsSec[sec]) { int trkType = (int)matchingPair.getTrackType(); int itrk = matchingPair.getIdLocal(); const auto& labelsTOF = mTOFClusLabels->getLabels(matchingPair.getTOFClIndex()); - const auto& labelTrack = mTracksLblWork[trkType][itrk]; + const auto& labelTrack = mTracksLblWork[sec][trkType][itrk]; // we have not found the track label among those associated to the TOF cluster --> fake match! We will associate the label of the main channel, but negative bool fake = true; @@ -160,15 +185,50 @@ void MatchTOF::run(const o2::globaltracking::RecoContainer& inp) matchingPair.setFakeMatch(); } } + } else { + for (auto& matchingPair : mMatchedTracksPairsSec[sec]) { + int bct0 = int((matchingPair.getSignal() - matchingPair.getLTIntegralOut().getTOF(0) + 5000) * Geo::BC_TIME_INPS_INV); // bc taken assuming speed of light (el) and 5 ns of margin + if (bct0 < 0) { // if negative time (it can happen at the beginng of the TF int was truncated per excess... adjusting) + bct0--; + } + float tof = matchingPair.getSignal() - bct0 * Geo::BC_TIME_INPS; + if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(2)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(3)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(4)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(0)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(1)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(5)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(6)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(7)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(8)) < 600) { + } else { // no pion, kaon, proton, electron, muon, deuteron, triton, 3He, 4He + matchingPair.setFakeMatch(); + } + } } } LOG(debug) << "...done. Now check the best matches"; - nMatches[sec] = mMatchedTracksPairs.size(); - selectBestMatches(); + nMatches[sec] = mMatchedTracksPairsSec[sec].size(); + selectBestMatches(sec); + + if (mStoreMatchable) { + for (auto& matchingPair : mMatchedTracksPairsSec[sec]) { + trkType trkTypeSplitted = trkType::TPC; + auto sourceID = matchingPair.getTrackRef().getSource(); + if (sourceID == o2::dataformats::GlobalTrackID::ITSTPC) { + trkTypeSplitted = trkType::ITSTPC; + } else if (sourceID == o2::dataformats::GlobalTrackID::TPCTRD) { + trkTypeSplitted = trkType::TPCTRD; + } else if (sourceID == o2::dataformats::GlobalTrackID::ITSTPCTRD) { + trkTypeSplitted = trkType::ITSTPCTRD; + } + matchingPair.setTrackType(trkTypeSplitted); + } + } } std::string nMatchesStr = "Number of pairs matched per sector: "; - for (int sec = o2::constants::math::NSectors; sec--;) { + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { nMatchesStr += fmt::format("{} : {} ; ", sec, nMatches[sec]); } LOG(info) << nMatchesStr; @@ -195,9 +255,10 @@ void MatchTOF::setTPCVDrift(const o2::tpc::VDriftCorrFact& v) } //______________________________________________ -void MatchTOF::setTPCCorrMaps(o2::gpu::CorrectionMapsHelper* maph) +void MatchTOF::setTPCCorrMaps(const o2::gpu::TPCFastTransformPOD* maph, float lumi) { - mTPCCorrMapsHelper = maph; + mTPCCorrMaps = maph; + mCTPLumi = lumi; } //______________________________________________ @@ -356,24 +417,86 @@ bool MatchTOF::prepareTPCData() }; mRecoCont->createTracksVariadic(creator); +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNlanes) +#endif + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + if (mIsTPCused) { + propagateTPCTracks(sec); + } + if (mIsITSTPCused || mIsTPCTRDused || mIsITSTPCTRDused) { + propagateConstrTracks(sec); + } + } + + // re-assign tracks which change sector (no multi-threading) + std::array globalPos; + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + if (mIsTPCused) { + for (auto& it : mTracksSeed[trkType::UNCONS][sec]) { + auto& pair = mTracksWork[sec][trkType::UNCONS][it]; + auto& trc = pair.first; + trc.getXYZGlo(globalPos); + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + int itnew = mTracksWork[sector][trkType::UNCONS].size(); + + mSideTPC[sector].push_back(mSideTPC[sec][it]); + mExtraTPCFwdTime[sector].push_back(mExtraTPCFwdTime[sec][it]); + mTracksWork[sector][trkType::UNCONS].emplace_back(pair); + mTrackGid[sector][trkType::UNCONS].emplace_back(mTrackGid[sec][trkType::UNCONS][it]); + + if (mMCTruthON) { + mTracksLblWork[sector][trkType::UNCONS].emplace_back(mTracksLblWork[sec][trkType::UNCONS][it]); + } + mLTinfos[sector][trkType::UNCONS].emplace_back(mLTinfos[sec][trkType::UNCONS][it]); + mVZtpcOnly[sector].push_back(mVZtpcOnly[sec][it]); + mTracksSectIndexCache[trkType::UNCONS][sector].push_back(itnew); + } + } + + for (auto& it : mTracksSeed[trkType::CONSTR][sec]) { + auto& pair = mTracksWork[sec][trkType::CONSTR][it]; + auto& trc = pair.first; + trc.getXYZGlo(globalPos); + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + int itnew = mTracksWork[sector][trkType::CONSTR].size(); + + mTracksWork[sector][trkType::CONSTR].emplace_back(pair); + mTrackGid[sector][trkType::CONSTR].emplace_back(mTrackGid[sec][trkType::CONSTR][it]); + + if (mMCTruthON) { + mTracksLblWork[sector][trkType::CONSTR].emplace_back(mTracksLblWork[sec][trkType::CONSTR][it]); + } + mLTinfos[sector][trkType::CONSTR].emplace_back(mLTinfos[sec][trkType::CONSTR][it]); + mTracksSectIndexCache[trkType::CONSTR][sector].push_back(itnew); + } + } + for (int it = 0; it < trkType::SIZE; it++) { - mMatchedTracksIndex[it].resize(mTracksWork[it].size()); - std::fill(mMatchedTracksIndex[it].begin(), mMatchedTracksIndex[it].end(), -1); // initializing all to -1 + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + mMatchedTracksIndex[sec][it].resize(mTracksWork[sec][it].size()); + std::fill(mMatchedTracksIndex[sec][it].begin(), mMatchedTracksIndex[sec][it].end(), -1); // initializing all to -1 + } } if (mIsTPCused) { LOG(debug) << "Number of UNCONSTRAINED tracks that failed to be propagated to TOF = " << mNotPropagatedToTOF[trkType::UNCONS]; // sort tracks in each sector according to their time (increasing in time) - for (int sec = o2::constants::math::NSectors; sec--;) { +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNlanes) +#endif + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { auto& indexCache = mTracksSectIndexCache[trkType::UNCONS][sec]; LOG(debug) << "Sorting sector" << sec << " | " << indexCache.size() << " tracks"; if (!indexCache.size()) { continue; } - std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { - auto& trcA = mTracksWork[trkType::UNCONS][a].second; - auto& trcB = mTracksWork[trkType::UNCONS][b].second; + std::sort(indexCache.begin(), indexCache.end(), [this, sec](int a, int b) { + auto& trcA = mTracksWork[sec][trkType::UNCONS][a].second; + auto& trcB = mTracksWork[sec][trkType::UNCONS][b].second; return ((trcA.getTimeStamp() - trcA.getTimeStampError()) - (trcB.getTimeStamp() - trcB.getTimeStampError()) < 0.); }); } // loop over tracks of single sector @@ -382,22 +505,121 @@ bool MatchTOF::prepareTPCData() LOG(debug) << "Number of CONSTRAINED tracks that failed to be propagated to TOF = " << mNotPropagatedToTOF[trkType::CONSTR]; // sort tracks in each sector according to their time (increasing in time) - for (int sec = o2::constants::math::NSectors; sec--;) { +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNlanes) +#endif + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { auto& indexCache = mTracksSectIndexCache[trkType::CONSTR][sec]; LOG(debug) << "Sorting sector" << sec << " | " << indexCache.size() << " tracks"; if (!indexCache.size()) { continue; } - std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { - auto& trcA = mTracksWork[trkType::CONSTR][a].second; - auto& trcB = mTracksWork[trkType::CONSTR][b].second; + std::sort(indexCache.begin(), indexCache.end(), [this, sec](int a, int b) { + auto& trcA = mTracksWork[sec][trkType::CONSTR][a].second; + auto& trcB = mTracksWork[sec][trkType::CONSTR][b].second; return ((trcA.getTimeStamp() - mSigmaTimeCut * trcA.getTimeStampError()) - (trcB.getTimeStamp() - mSigmaTimeCut * trcB.getTimeStampError()) < 0.); }); } // loop over tracks of single sector } + if (mRecoCont->inputsTPCclusters) { + mTPCClusterIdxStruct = &mRecoCont->inputsTPCclusters->clusterIndex; + mTPCTracksArray = mRecoCont->getTPCTracks(); + mTPCTrackClusIdx = mRecoCont->getTPCTracksClusterRefs(); + mTPCRefitterShMap = mRecoCont->clusterShMapTPC; + mTPCRefitterOccMap = mRecoCont->occupancyMapTPC; + } + return true; } + +//______________________________________________ +void MatchTOF::propagateTPCTracks(int sec) +{ + auto& trkWork = mTracksWork[sec][trkType::UNCONS]; + + for (int it = 0; it < trkWork.size(); it++) { + o2::track::TrackParCov& trc = trkWork[it].first; + + o2::track::TrackLTIntegral& intLT0 = mLTinfos[sec][trkType::UNCONS][it]; + + const auto& trackTune = TrackTuneParams::Instance(); + if (!trackTune.sourceLevelTPC) { // correct only if TPC track was not corrected at the source level + if (trackTune.useTPCOuterCorr) { + trc.updateParams(trackTune.tpcParOuter); + } + if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) { // only TRD-refitted track have cov.matrix already man> + trc.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations); + } + } + if (!propagateToRefXWithoutCov(trc, mXRef, 10, mBz)) { // we first propagate to 371 cm without considering the covariance matri + mNotPropagatedToTOF[trkType::UNCONS]++; + continue; + } + + if (trc.getX() < o2::constants::geom::XTPCOuterRef - 1.) { + if (!propagateToRefXWithoutCov(trc, o2::constants::geom::XTPCOuterRef, 10, mBz) || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagat> + mNotPropagatedToTOF[trkType::UNCONS]++; + continue; + } + } + + o2::base::Propagator::Instance()->estimateLTFast(intLT0, trc); + + // the "rough" propagation worked; now we can propagate considering also the cov matrix + if (!propagateToRefX(trc, mXRef, 2, intLT0)) { // || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix w> + mNotPropagatedToTOF[trkType::UNCONS]++; + continue; + } + + // printf("time0 %f -> %f (diff = %f, err = %f)\n",trackTime0, time0, trackTime0 - time0, terr); + // printf("time errors %f,%f -> %f,%f\n",(_tr.getDeltaTBwd() + 5) * mTPCTBinMUS,(_tr.getDeltaTFwd() + 5) * mTPCTBinMUS,trackTime0 - time0 + terr, ti> + + std::array globalPos; + trc.getXYZGlo(globalPos); + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + if (sector == sec) { + mTracksSectIndexCache[trkType::UNCONS][sector].push_back(it); + } else { + mTracksSeed[trkType::UNCONS][sec].push_back(it); // to be moved to another sector + } + } +} +//______________________________________________ +void MatchTOF::propagateConstrTracks(int sec) +{ + std::array globalPos; + + auto& trkWork = mTracksWork[sec][trkType::CONSTR]; + + for (int it = 0; it < trkWork.size(); it++) { + o2::track::TrackParCov& trc = trkWork[it].first; + o2::track::TrackLTIntegral& intLT0 = mLTinfos[sec][trkType::CONSTR][it]; + + // propagate to matching Xref + if (!propagateToRefXWithoutCov(trc, mXRef, 2, mBz)) { // we first propagate to 371 cm without considering the covariance matrix + mNotPropagatedToTOF[trkType::CONSTR]++; + continue; + } + + // the "rough" propagation worked; now we can propagate considering also the cov matrix + if (!propagateToRefX(trc, mXRef, 2, intLT0) || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked;> + mNotPropagatedToTOF[trkType::CONSTR]++; + continue; + } + + trc.getXYZGlo(globalPos); + + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + if (sector == sec) { + mTracksSectIndexCache[trkType::CONSTR][sector].push_back(it); + } else { + mTracksSeed[trkType::CONSTR][sec].push_back(it); // to be moved to another sector + } + } +} //______________________________________________ void MatchTOF::addITSTPCSeed(const o2::dataformats::TrackTPCITS& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) { @@ -413,44 +635,19 @@ void MatchTOF::addITSTPCSeed(const o2::dataformats::TrackTPCITS& _tr, o2::datafo void MatchTOF::addConstrainedSeed(o2::track::TrackParCov& trc, o2::dataformats::GlobalTrackID srcGID, o2::track::TrackLTIntegral intLT0, timeEst timeMUS) { std::array globalPos; - // current track index - int it = mTracksWork[trkType::CONSTR].size(); - - // propagate to matching Xref - trc.getXYZGlo(globalPos); - LOG(debug) << "Global coordinates Before propagating to 371 cm: globalPos[0] = " << globalPos[0] << ", globalPos[1] = " << globalPos[1] << ", globalPos[2] = " << globalPos[2]; - LOG(debug) << "Radius xy Before propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1]); - LOG(debug) << "Radius xyz Before propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1] + globalPos[2] * globalPos[2]); - if (!propagateToRefXWithoutCov(trc, mXRef, 2, mBz)) { // we first propagate to 371 cm without considering the covariance matrix - mNotPropagatedToTOF[trkType::CONSTR]++; - return; - } - - // the "rough" propagation worked; now we can propagate considering also the cov matrix - if (!propagateToRefX(trc, mXRef, 2, intLT0) || TMath::Abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked; CHECK: can it happen that it does not if the prop> - mNotPropagatedToTOF[trkType::CONSTR]++; - return; - } - trc.getXYZGlo(globalPos); - LOG(debug) << "Global coordinates After propagating to 371 cm: globalPos[0] = " << globalPos[0] << ", globalPos[1] = " << globalPos[1] << ", globalPos[2] = " << globalPos[2]; - LOG(debug) << "Radius xy After propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1]); - LOG(debug) << "Radius xyz After propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1] + globalPos[2] * globalPos[2]); - LOG(debug) << "The track will go to sector " << o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); - int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); // create working copy of track param - mTracksWork[trkType::CONSTR].emplace_back(std::make_pair(trc, timeMUS)); - mTrackGid[trkType::CONSTR].emplace_back(srcGID); - mLTinfos[trkType::CONSTR].emplace_back(intLT0); + mTracksWork[sector][trkType::CONSTR].emplace_back(std::make_pair(trc, timeMUS)); + mTrackGid[sector][trkType::CONSTR].emplace_back(srcGID); + mLTinfos[sector][trkType::CONSTR].emplace_back(intLT0); if (mMCTruthON) { - mTracksLblWork[trkType::CONSTR].emplace_back(mRecoCont->getTPCITSTrackMCLabel(srcGID)); + mTracksLblWork[sector][trkType::CONSTR].emplace_back(mRecoCont->getTPCITSTrackMCLabel(srcGID)); } - mTracksSectIndexCache[trkType::CONSTR][sector].push_back(it); //delete trc; // Check: is this needed? } //______________________________________________ void MatchTOF::addTRDSeed(const o2::trd::TrackTRD& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) @@ -466,7 +663,6 @@ void MatchTOF::addTRDSeed(const o2::trd::TrackTRD& _tr, o2::dataformats::GlobalT auto trc = _tr.getOuterParam(); o2::track::TrackLTIntegral intLT0 = _tr.getLTIntegralOut(); - ; // empty for the moment // o2::dataformats::TimeStampWithError timeEst ts(time0, terr + mExtraTimeToleranceTRD); @@ -480,9 +676,6 @@ void MatchTOF::addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalT std::array globalPos; - // current track index - int it = mTracksWork[trkType::UNCONS].size(); - // create working copy of track param timeEst timeInfo; // set @@ -491,65 +684,71 @@ void MatchTOF::addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalT extraErr = 100; } + float trackTime0 = _tr.getTime0() * mTPCTBinMUS; + timeInfo.setTimeStampError((_tr.getDeltaTBwd() + 5) * mTPCTBinMUS + extraErr); + // timeInfo.setTimeStampError(trackTime0 - time0 + terr + extraErr); + timeInfo.setTimeStamp(trackTime0); + auto trc = _tr.getOuterParam(); - const auto& trackTune = o2::globaltracking::TrackTuneParams::Instance(); - if (!trackTune.sourceLevelTPC) { // correct only if TPC track was not corrected at the source level - if (trackTune.useTPCOuterCorr) { - trc.updateParams(trackTune.tpcParOuter); - } - if (trackTune.tpcCovOuterType != o2::globaltracking::TrackTuneParams::AddCovType::Disable) { // only TRD-refitted track have cov.matrix already manipulated - trc.updateCov(trackTune.tpcCovOuter, trackTune.tpcCovOuterType == o2::globaltracking::TrackTuneParams::AddCovType::WithCorrelations); - } - } - if (!propagateToRefXWithoutCov(trc, mXRef, 10, mBz)) { // we first propagate to 371 cm without considering the covariance matri - mNotPropagatedToTOF[trkType::UNCONS]++; - return; - } + trc.getXYZGlo(globalPos); - o2::track::TrackLTIntegral intLT0; //mTPCTracksWork.back().getLTIntegralOut(); // we get the integrated length from TPC-ITC outward propagation + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + mSideTPC[sector].push_back(_tr.hasASideClustersOnly() ? 1 : (_tr.hasCSideClustersOnly() ? -1 : 0)); + mExtraTPCFwdTime[sector].push_back((_tr.getDeltaTFwd() + 5) * mTPCTBinMUS + extraErr); + // mExtraTPCFwdTime[sector].push_back(time0 + terr - trackTime0 + extraErr); + mTracksWork[sector][trkType::UNCONS].emplace_back(std::make_pair(trc, timeInfo)); + mTrackGid[sector][trkType::UNCONS].emplace_back(srcGID); + + if (mMCTruthON) { + mTracksLblWork[sector][trkType::UNCONS].emplace_back(mRecoCont->getTPCTrackMCLabel(srcGID)); + } + o2::track::TrackLTIntegral intLT0; // mTPCTracksWork.back().getLTIntegralOut(); // we get the integrated length from TPC-ITC outward propagation // compute track length up to now - o2::base::Propagator::Instance()->estimateLTFast(intLT0, trc); + mLTinfos[sector][trkType::UNCONS].emplace_back(intLT0); + float vz0 = _tr.getZAt(0, mBz); + if (std::abs(vz0) > 9000) { + vz0 = _tr.getZ() - _tr.getX() * _tr.getTgl(); + } + mVZtpcOnly[sector].push_back(vz0); - if (trc.getX() < o2::constants::geom::XTPCOuterRef - 1.) { - if (!propagateToRefX(trc, o2::constants::geom::XTPCOuterRef, 10, intLT0) || TMath::Abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked; CHECK: can it happ + /* + const auto& trackTune = TrackTuneParams::Instance(); + if (!trackTune.sourceLevelTPC) { // correct only if TPC track was not corrected at the source level + if (trackTune.useTPCOuterCorr) { + trc.updateParams(trackTune.tpcParOuter); + } + if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) { // only TRD-refitted track have cov.matrix already manipulated + trc.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations); + } + } + if (!propagateToRefXWithoutCov(trc, mXRef, 10, mBz)) { // we first propagate to 371 cm without considering the covariance matri mNotPropagatedToTOF[trkType::UNCONS]++; return; } - } - - // the "rough" propagation worked; now we can propagate considering also the cov matrix - if (!propagateToRefX(trc, mXRef, 2, intLT0)) { // || TMath::Abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked; CHECK: can it happen that it does not if the prop> - mNotPropagatedToTOF[trkType::UNCONS]++; - return; - } - - float trackTime0 = _tr.getTime0() * mTPCTBinMUS - mTPCDriftTimeOffset; - - timeInfo.setTimeStampError((_tr.getDeltaTBwd() + 5) * mTPCTBinMUS + extraErr); - mExtraTPCFwdTime.push_back((_tr.getDeltaTFwd() + 5) * mTPCTBinMUS + extraErr); - - // timeInfo.setTimeStampError(trackTime0 - time0 + terr + extraErr); - // mExtraTPCFwdTime.push_back(time0 + terr - trackTime0 + extraErr); - timeInfo.setTimeStamp(trackTime0); - mSideTPC.push_back(_tr.hasASideClustersOnly() ? 1 : (_tr.hasCSideClustersOnly() ? -1 : 0)); - - // printf("time0 %f -> %f (diff = %f, err = %f)\n",trackTime0, time0, trackTime0 - time0, terr); - // printf("time errors %f,%f -> %f,%f\n",(_tr.getDeltaTBwd() + 5) * mTPCTBinMUS,(_tr.getDeltaTFwd() + 5) * mTPCTBinMUS,trackTime0 - time0 + terr, time0 + terr - trackTime0); + if (trc.getX() < o2::constants::geom::XTPCOuterRef - 1.) { + if (!propagateToRefX(trc, o2::constants::geom::XTPCOuterRef, 10, intLT0) || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked; CHECK: can it happ + mNotPropagatedToTOF[trkType::UNCONS]++; + return; + } + } - trc.getXYZGlo(globalPos); - int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + // the "rough" propagation worked; now we can propagate considering also the cov matrix + if (!propagateToRefX(trc, mXRef, 2, intLT0)) { // || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked; CHECK: can it happen that it does not if the prop> + mNotPropagatedToTOF[trkType::UNCONS]++; + return; + } - mTracksWork[trkType::UNCONS].emplace_back(std::make_pair(trc, timeInfo)); - mTrackGid[trkType::UNCONS].emplace_back(srcGID); + // printf("time0 %f -> %f (diff = %f, err = %f)\n",trackTime0, time0, trackTime0 - time0, terr); + // printf("time errors %f,%f -> %f,%f\n",(_tr.getDeltaTBwd() + 5) * mTPCTBinMUS,(_tr.getDeltaTFwd() + 5) * mTPCTBinMUS,trackTime0 - time0 + terr, time0 + terr - trackTime0); - if (mMCTruthON) { - mTracksLblWork[trkType::UNCONS].emplace_back(mRecoCont->getTPCTrackMCLabel(srcGID)); - } - mLTinfos[trkType::UNCONS].emplace_back(intLT0); + trc.getXYZGlo(globalPos); + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); - mTracksSectIndexCache[trkType::UNCONS][sector].push_back(it); - //delete trc; // Check: is this needed? + mTracksSectIndexCache[trkType::UNCONS][sector].push_back(it); + */ + // delete trc; // Check: is this needed? } //______________________________________________ bool MatchTOF::prepareTOFClusters() @@ -568,7 +767,7 @@ bool MatchTOF::prepareTOFClusters() // mTOFClusLblWork.reserve(mNumOfClusters); // } - for (int sec = o2::constants::math::NSectors; sec--;) { + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { mTOFClusSectIndexCache[sec].clear(); //mTOFClusSectIndexCache[sec].reserve(100 + 1.2 * mNumOfClusters / o2::constants::math::NSectors); // we cannot do this, we don't have mNumOfClusters yet } @@ -589,7 +788,7 @@ bool MatchTOF::prepareTOFClusters() } // sort clusters in each sector according to their time (increasing in time) - for (int sec = o2::constants::math::NSectors; sec--;) { + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { auto& indexCache = mTOFClusSectIndexCache[sec]; LOG(debug) << "Sorting sector" << sec << " | " << indexCache.size() << " TOF clusters"; if (!indexCache.size()) { @@ -642,15 +841,18 @@ void MatchTOF::doMatching(int sec) nStepsInsideSameStrip[ii] = 0; } int nStripsCrossedInPropagation = 0; // how many strips were hit during the propagation - auto& trackWork = mTracksWork[type][cacheTrk[itrk]]; + auto& trackWork = mTracksWork[sec][type][cacheTrk[itrk]]; auto& trefTrk = trackWork.first; - auto& intLT = mLTinfos[type][cacheTrk[itrk]]; + float pt = trefTrk.getPt(); + auto& intLT = mLTinfos[sec][type][cacheTrk[itrk]]; float timeShift = intLT.getL() * 33.35641; // integrated time for 0.75 beta particles in ps, to take into account the t.o.f. delay with respect the interaction BC // using beta=0.75 to cover beta range [0.59 , 1.04] also for a 8 m track lenght with a 10 ns track resolution (TRD) // Printf("intLT (before doing anything): length = %f, time (Pion) = %f", intLT.getL(), intLT.getTOF(o2::track::PID::Pion)); float minTrkTime = (trackWork.second.getTimeStamp() - mSigmaTimeCut * trackWork.second.getTimeStampError()) * 1.E6 + timeShift; // minimum time in ps float maxTrkTime = (trackWork.second.getTimeStamp() + mSigmaTimeCut * trackWork.second.getTimeStampError()) * 1.E6 + timeShift + 100E3; // maximum time in ps + 100 ns for slow tracks (beta->0.2) + const float sqrt12inv = 1. / sqrt(12.); + float resT = (trackWork.second.getTimeStampError() + 100E-3) * sqrt12inv; int istep = 1; // number of steps float step = 1.0; // step size in cm @@ -731,6 +933,44 @@ void MatchTOF::doMatching(int sec) } //Printf("nStepsInsideSameStrip[nStripsCrossedInPropagation-1] = %d", nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]); if (nStepsInsideSameStrip[nStripsCrossedInPropagation - 1] == 0) { + // fine propagation inside the strip -> 1 mm step + trkLTInt[nStripsCrossedInPropagation - 1] = intLT; + // temporary variables since propagation can fail + int detIdTemp2[5] = {0, 0, 0, 0, 0}; + float deltaPosTemp2[3] = {deltaPosTemp[0], deltaPosTemp[1], deltaPosTemp[2]}; + int nstep = 0; + const int maxnstep = 50; + float xStart = trefTrk.getX(); + float xStop = xStart; + trefTrk.getXYZGlo(pos); + for (int ii = 0; ii < 3; ii++) { // we need to change the type... + posFloat[ii] = pos[ii]; + } + while (deltaPosTemp2[1] < -0.05 && detIdTemp2[2] != -1 && nstep < maxnstep) { // continuing propagation if dy is negative and we are still inside the strip volume + nstep++; + xStop += 0.1; + propagateToRefXWithoutCov(trefTrk, xStop, 0.1, mBz, posFloat); + + Geo::getPadDxDyDz(posFloat, detIdTemp2, deltaPosTemp2, sec); + if (detIdTemp2[2] != -1) { // if propation was succesful -> update params + float dx = deltaPosTemp2[0] - deltaPosTemp[0]; + float dy = deltaPosTemp2[1] - deltaPosTemp[1]; + float dz = deltaPosTemp2[2] - deltaPosTemp[2]; + updateTL(trkLTInt[nStripsCrossedInPropagation - 1], sqrt(dx * dx + dy * dy + dz * dz)); + detIdTemp[0] = detIdTemp2[0]; + detIdTemp[1] = detIdTemp2[1]; + detIdTemp[2] = detIdTemp2[2]; + detIdTemp[3] = detIdTemp2[3]; + detIdTemp[4] = detIdTemp2[4]; + deltaPosTemp[0] = deltaPosTemp2[0]; + deltaPosTemp[1] = deltaPosTemp2[1]; + deltaPosTemp[2] = deltaPosTemp2[2]; + } + } + + // adjust accordingly to DeltaY + updateTL(trkLTInt[nStripsCrossedInPropagation - 1], -deltaPosTemp[1]); + detId[nStripsCrossedInPropagation - 1][0] = detIdTemp[0]; detId[nStripsCrossedInPropagation - 1][1] = detIdTemp[1]; detId[nStripsCrossedInPropagation - 1][2] = detIdTemp[2]; @@ -739,16 +979,18 @@ void MatchTOF::doMatching(int sec) deltaPos[nStripsCrossedInPropagation - 1][0] = deltaPosTemp[0]; deltaPos[nStripsCrossedInPropagation - 1][1] = deltaPosTemp[1]; deltaPos[nStripsCrossedInPropagation - 1][2] = deltaPosTemp[2]; - trkLTInt[nStripsCrossedInPropagation - 1] = intLT; // Printf("intLT (after matching to strip %d): length = %f, time (Pion) = %f", nStripsCrossedInPropagation - 1, trkLTInt[nStripsCrossedInPropagation - 1].getL(), trkLTInt[nStripsCrossedInPropagation - 1].getTOF(o2::track::PID::Pion)); nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]++; - } else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) + } + /* // obsolete + else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) // N.B. the integrated length and time are taken (at least for now) from the first time we crossed the strip, so here we do nothing with those deltaPos[nStripsCrossedInPropagation - 1][0] += deltaPosTemp[0] + (detIdTemp[4] - detId[nStripsCrossedInPropagation - 1][4]) * Geo::XPAD; // residual in x deltaPos[nStripsCrossedInPropagation - 1][1] += deltaPosTemp[1]; // residual in y deltaPos[nStripsCrossedInPropagation - 1][2] += deltaPosTemp[2] + (detIdTemp[3] - detId[nStripsCrossedInPropagation - 1][3]) * Geo::ZPAD; // residual in z nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]++; } + */ } for (Int_t imatch = 0; imatch < nStripsCrossedInPropagation; imatch++) { @@ -822,12 +1064,52 @@ void MatchTOF::doMatching(int sec) int eventIdTOF; int sourceIdTOF; for (auto iPropagation = 0; iPropagation < nStripsCrossedInPropagation; iPropagation++) { + int bct0 = int((mTOFClusWork[cacheTOF[itof]].getTime() - trkLTInt[iPropagation].getTOF(0) + 5000) * Geo::BC_TIME_INPS_INV); // bc taken assuming speed of light (el) and 5 ns of margin + if (bct0 < 0) { // if negative time (it can happen at the beginng of the TF int was truncated per excess... adjusting) + bct0--; + } + float tof = mTOFClusWork[cacheTOF[itof]].getTime() - bct0 * Geo::BC_TIME_INPS; + if (tof - trkLTInt[iPropagation].getTOF(6) > 2000) { // reject tracks slower than triton + continue; + } + float cosangle = TMath::Cos(Geo::getAngles(indices[1], indices[2]) * TMath::DegToRad()); + const float errXinvMin = 1. / (mMatchParams->maxResX * mMatchParams->maxResX); + const float errZinvMin = 1. / (mMatchParams->maxResZ * mMatchParams->maxResZ); + float errXinv2 = 1. / (trefTrk.getSigmaY2()); + float errZinv2 = 1. / (trefTrk.getSigmaZ2() * cosangle); // should be valid only at eta=0 + + if (errXinv2 < errXinvMin) { + errXinv2 = errXinvMin; + } + if (errZinv2 < errZinvMin) { + errZinv2 = errZinvMin; + } + LOG(debug) << "TOF Cluster [" << itof << ", " << cacheTOF[itof] << "]: indices = " << indices[0] << ", " << indices[1] << ", " << indices[2] << ", " << indices[3] << ", " << indices[4]; LOG(debug) << "Propagated Track [" << itrk << "]: detId[" << iPropagation << "] = " << detId[iPropagation][0] << ", " << detId[iPropagation][1] << ", " << detId[iPropagation][2] << ", " << detId[iPropagation][3] << ", " << detId[iPropagation][4]; float resX = deltaPos[iPropagation][0] - (indices[4] - detId[iPropagation][4]) * Geo::XPAD + posCorr[0]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster float resZ = deltaPos[iPropagation][2] - (indices[3] - detId[iPropagation][3]) * Geo::ZPAD + posCorr[2]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster + float resY = deltaPos[iPropagation][1]; + float resXor = resX; + float resZor = resZ; float res = TMath::Sqrt(resX * resX + resZ * resZ); + if (resX < -1.25) { // distance from closest border + resX += 1.25; + } else if (resX > 1.25) { + resX -= 1.25; + } else { + resX = 1E-3 / (pt + 1E-3); // high-pt should be favoured + } + + if (resZ < -1.75) { // distance from closest border + resZ += 1.75; + } else if (resZ > 1.75) { + resZ -= 1.75; + } else { + resZ = 1E-3 / (pt + 1E-3); // high-pt should be favoured + } + LOG(debug) << "resX = " << resX << ", resZ = " << resZ << ", res = " << res; if (indices[0] != detId[iPropagation][0]) { continue; @@ -838,14 +1120,20 @@ void MatchTOF::doMatching(int sec) if (indices[2] != detId[iPropagation][2]) { continue; } - float chi2 = res; // TODO: take into account also the time! + float chi2 = 0.5 * (resX * resX * errXinv2 + resZ * resZ * errZinv2); // TODO: take into account also the time! - if (res < mSpaceTolerance) { // matching ok! + if (res < mSpaceTolerance && chi2 < mMatchParams->maxChi2) { // matching ok! LOG(debug) << "MATCHING FOUND: We have a match! between track " << mTracksSectIndexCache[type][sec][itrk] << " and TOF cluster " << mTOFClusSectIndexCache[indices[0]][itof]; foundCluster = true; // set event indexes (to be checked) int eventIndexTOFCluster = mTOFClusSectIndexCache[indices[0]][itof]; - mMatchedTracksPairs.emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[iPropagation], mTrackGid[type][cacheTrk[itrk]], type, (trefTOF.getTime() - (minTrkTime + maxTrkTime - 100E3) * 0.5) * 1E-6, 0., resX, resZ); // subracting 100 ns to max track which was artificially added + mMatchedTracksPairsSec[sec].emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[iPropagation], mTrackGid[sec][type][cacheTrk[itrk]], type, (trefTOF.getTime() - (minTrkTime + maxTrkTime - 100E3) * 0.5) * 1E-6, trefTOF.getZ(), resXor, resZor, resY); // subracting 100 ns to max track which was artificially added + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setPt(pt); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResX(sqrt(1. / errXinv2)); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResZ(sqrt(1. / errZinv2)); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResT(resT); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setVz(0.0); // not needed for constrained tracks + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setChannel(mainChannel); } } } @@ -885,13 +1173,15 @@ void MatchTOF::doMatchingForTPC(int sec) std::vector> trkLTInt; std::vector, 2>> deltaPos; std::vector> nStepsInsideSameStrip; + std::vector> Zshift; LOG(debug) << "Trying to match %d tracks" << cacheTrk.size(); for (int itrk = 0; itrk < cacheTrk.size(); itrk++) { - auto& trackWork = mTracksWork[trkType::UNCONS][cacheTrk[itrk]]; + auto& trackWork = mTracksWork[sec][trkType::UNCONS][cacheTrk[itrk]]; auto& trefTrk = trackWork.first; - auto& intLT = mLTinfos[trkType::UNCONS][cacheTrk[itrk]]; + float pt = trefTrk.getPt(); + auto& intLT = mLTinfos[sec][trkType::UNCONS][cacheTrk[itrk]]; float timeShift = intLT.getL() * 33.35641; // integrated time for 0.75 beta particles in ps, to take into account the t.o.f. delay with respect the interaction BC // using beta=0.75 to cover beta range [0.59 , 1.04] also for a 8 m track lenght with a 10 ns track resolution (TRD) @@ -899,11 +1189,14 @@ void MatchTOF::doMatchingForTPC(int sec) BCcand.clear(); nStripsCrossedInPropagation.clear(); - int side = mSideTPC[cacheTrk[itrk]]; + int side = mSideTPC[sec][cacheTrk[itrk]]; // look at BC candidates for the track - double minTrkTime = (trackWork.second.getTimeStamp() - trackWork.second.getTimeStampError()) * 1.E6 + timeShift; // minimum time in ps + double tpctime = trackWork.second.getTimeStamp(); // in mus + double minTrkTime = (tpctime - trackWork.second.getTimeStampError()) * 1.E6 + timeShift; // minimum time in ps minTrkTime = int(minTrkTime / BCgranularity) * BCgranularity; // align min to a BC - double maxTrkTime = (trackWork.second.getTimeStamp() + mExtraTPCFwdTime[cacheTrk[itrk]]) * 1.E6 + timeShift; // maximum time in ps + double maxTrkTime = (tpctime + mExtraTPCFwdTime[sec][cacheTrk[itrk]]) * 1.E6 + timeShift; // maximum time in ps + const float sqrt12inv = 1. / sqrt(12.); + float resT = (maxTrkTime - minTrkTime) * sqrt12inv; if (mIsCosmics) { for (double tBC = minTrkTime; tBC < maxTrkTime; tBC += BCgranularity) { @@ -954,6 +1247,8 @@ void MatchTOF::doMatchingForTPC(int sec) detId.reserve(BCcand.size()); trkLTInt.clear(); trkLTInt.reserve(BCcand.size()); + Zshift.clear(); + Zshift.reserve(BCcand.size()); deltaPos.clear(); deltaPos.reserve(BCcand.size()); nStepsInsideSameStrip.clear(); @@ -1016,6 +1311,8 @@ void MatchTOF::doMatchingForTPC(int sec) posFloat[2] = pos[2]; } + float ZshiftCurrent = posFloat[2] - pos[2]; + Geo::getPadDxDyDz(posFloat, detIdTemp, deltaPosTemp, sec); if (detIdTemp[2] == -1) { @@ -1035,6 +1332,46 @@ void MatchTOF::doMatchingForTPC(int sec) //Printf("nStepsInsideSameStrip[nStripsCrossedInPropagation-1] = %d", nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]); if (nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1] == 0) { + trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1] = intLT; + // temporary variables since propagation can fail + int detIdTemp2[5] = {0, 0, 0, 0, 0}; + float deltaPosTemp2[3] = {deltaPosTemp[0], deltaPosTemp[1], deltaPosTemp[2]}; + int nstep = 0; + const int maxnstep = 50; + float xStart = trefTrk.getX(); + float xStop = xStart; + trefTrk.getXYZGlo(pos); + for (int ii = 0; ii < 3; ii++) { // we need to change the type... + posFloat[ii] = pos[ii]; + } + + while (deltaPosTemp2[1] < -0.05 && detIdTemp2[2] != -1 && nstep < maxnstep) { // continuing propagation if dy is negative and we are still inside the strip volume + nstep++; + xStop += 0.1; + propagateToRefXWithoutCov(trefTrk, xStop, 0.1, mBz, posFloat); + + posFloat[2] += ZshiftCurrent; + + Geo::getPadDxDyDz(posFloat, detIdTemp2, deltaPosTemp2, sec); + if (detIdTemp2[2] != -1) { // if propation was succesful -> update params + float dx = deltaPosTemp2[0] - deltaPosTemp[0]; + float dy = deltaPosTemp2[1] - deltaPosTemp[1]; + float dz = deltaPosTemp2[2] - deltaPosTemp[2]; + updateTL(trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1], sqrt(dx * dx + dy * dy + dz * dz)); + detIdTemp[0] = detIdTemp2[0]; + detIdTemp[1] = detIdTemp2[1]; + detIdTemp[2] = detIdTemp2[2]; + detIdTemp[3] = detIdTemp2[3]; + detIdTemp[4] = detIdTemp2[4]; + deltaPosTemp[0] = deltaPosTemp2[0]; + deltaPosTemp[1] = deltaPosTemp2[1]; + deltaPosTemp[2] = deltaPosTemp2[2]; + } + } + + // adjust accordingly to DeltaY + updateTL(trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1], -deltaPosTemp[1]); + detId[ibc][nStripsCrossedInPropagation[ibc] - 1][0] = detIdTemp[0]; detId[ibc][nStripsCrossedInPropagation[ibc] - 1][1] = detIdTemp[1]; detId[ibc][nStripsCrossedInPropagation[ibc] - 1][2] = detIdTemp[2]; @@ -1044,16 +1381,19 @@ void MatchTOF::doMatchingForTPC(int sec) deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][1] = deltaPosTemp[1]; deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][2] = deltaPosTemp[2]; - trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1] = intLT; + Zshift[ibc][nStripsCrossedInPropagation[ibc] - 1] = ZshiftCurrent; // Printf("intLT (after matching to strip %d): length = %f, time (Pion) = %f", nStripsCrossedInPropagation - 1, trkLTInt[nStripsCrossedInPropagation - 1].getL(), trkLTInt[nStripsCrossedInPropagation - 1].getTOF(o2::track::PID::Pion)); nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1]++; - } else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) - // N.B. the integrated length and time are taken (at least for now) from the first time we crossed the strip, so here we do nothing with those - deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][0] += deltaPosTemp[0] + (detIdTemp[4] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][4]) * Geo::XPAD; // residual in x - deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][1] += deltaPosTemp[1]; // residual in y - deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][2] += deltaPosTemp[2] + (detIdTemp[3] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][3]) * Geo::ZPAD; // residual in z - nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1]++; } + /* // obsolete + else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) + // N.B. the integrated length and time are taken (at least for now) from the first time we crossed the strip, so here we do nothing with those + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][0] += deltaPosTemp[0] + (detIdTemp[4] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][4]) * Geo::XPAD; // residual in x + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][1] += deltaPosTemp[1]; // residual in y + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][2] += deltaPosTemp[2] + (detIdTemp[3] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][3]) * Geo::ZPAD; // residual in z + nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1]++; + } + */ } } for (int ibc = 0; ibc < BCcand.size(); ibc++) { @@ -1145,17 +1485,67 @@ void MatchTOF::doMatchingForTPC(int sec) continue; } + int bct0 = int((mTOFClusWork[cacheTOF[itof]].getTime() - trkLTInt[ibc][iPropagation].getTOF(0) + 5000) * Geo::BC_TIME_INPS_INV); // bc taken assuming speed of light (el) and 5 ns of margin + if (bct0 < 0) { // if negative time (it can happen at the beginng of the TF int was truncated per excess... adjusting) + bct0--; + } + float tof = mTOFClusWork[cacheTOF[itof]].getTime() - bct0 * Geo::BC_TIME_INPS; + if (tof - trkLTInt[ibc][iPropagation].getTOF(6) > 2000) { // reject tracks slower than triton + continue; + } + + if (mMatchParams->applyPIDcutTPConly) { // for TPC only tracks allowing possibility to apply a PID cut + if (std::abs(tof - trkLTInt[ibc][iPropagation].getTOF(2)) < 2000) { // pion hypotesis + } else if (std::abs(tof - trkLTInt[ibc][iPropagation].getTOF(3)) < 2000) { // kaon hypoteis + } else if (std::abs(tof - trkLTInt[ibc][iPropagation].getTOF(4)) < 2000) { // proton hypotesis + } else { // reject matching + continue; + } + } + + float cosangle = TMath::Cos(Geo::getAngles(indices[1], indices[2]) * TMath::DegToRad()); + const float errXinvMin = 1. / (mMatchParams->maxResX * mMatchParams->maxResX); + const float errZinvMin = 1. / (mMatchParams->maxResZ * mMatchParams->maxResZ); + float errXinv2 = 1. / (trefTrk.getSigmaY2()); + float errZinv2 = 1. / (trefTrk.getSigmaZ2() * cosangle); // should be valid only at eta=0 + + if (errXinv2 < errXinvMin) { + errXinv2 = errXinvMin; + } + if (errZinv2 < errZinvMin) { + errZinv2 = errZinvMin; + } + LOG(debug) << "TOF Cluster [" << itof << ", " << cacheTOF[itof] << "]: indices = " << indices[0] << ", " << indices[1] << ", " << indices[2] << ", " << indices[3] << ", " << indices[4]; LOG(debug) << "Propagated Track [" << itrk << "]: detId[" << iPropagation << "] = " << detId[ibc][iPropagation][0] << ", " << detId[ibc][iPropagation][1] << ", " << detId[ibc][iPropagation][2] << ", " << detId[ibc][iPropagation][3] << ", " << detId[ibc][iPropagation][4]; float resX = deltaPos[ibc][iPropagation][0] - (indices[4] - detId[ibc][iPropagation][4]) * Geo::XPAD + posCorr[0]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster float resZ = deltaPos[ibc][iPropagation][2] - (indices[3] - detId[ibc][iPropagation][3]) * Geo::ZPAD + posCorr[2]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster + float resY = deltaPos[ibc][iPropagation][1]; if (BCcand[ibc] > bcClus) { resZ += (BCcand[ibc] - bcClus) * vdriftInBC * side; // add bc correction } else { resZ -= (bcClus - BCcand[ibc]) * vdriftInBC * side; } + float resXor = resX; + float resZor = resZ; float res = TMath::Sqrt(resX * resX + resZ * resZ); + if (resX < -1.25) { // distance from closest border + resX += 1.25; + } else if (resX > 1.25) { + resX -= 1.25; + } else { + resX = 1E-3 / (pt + 1E-3); // high-pt should be favoured + } + + if (resZ < -1.75) { // distance from closest border + resZ += 1.75; + } else if (resZ > 1.75) { + resZ -= 1.75; + } else { + resZ = 1E-3 / (pt + 1E-3); // high-pt should be favoured + } + if (indices[0] != detId[ibc][iPropagation][0]) { continue; } @@ -1167,14 +1557,21 @@ void MatchTOF::doMatchingForTPC(int sec) } LOG(debug) << "resX = " << resX << ", resZ = " << resZ << ", res = " << res; - float chi2 = mIsCosmics ? resX : res; // TODO: take into account also the time! + float chi2 = mIsCosmics ? resX : 0.5 * (resX * resX * errXinv2 + resZ * resZ * errZinv2); // TODO: take into account also the time! - if (res < mSpaceTolerance) { // matching ok! + if (res < mSpaceTolerance && chi2 < mMatchParams->maxChi2) { // matching ok! LOG(debug) << "MATCHING FOUND: We have a match! between track " << mTracksSectIndexCache[trkType::UNCONS][sec][itrk] << " and TOF cluster " << mTOFClusSectIndexCache[indices[0]][itof]; foundCluster = true; // set event indexes (to be checked) + int eventIndexTOFCluster = mTOFClusSectIndexCache[indices[0]][itof]; - mMatchedTracksPairs.emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[ibc][iPropagation], mTrackGid[trkType::UNCONS][cacheTrk[itrk]], trkType::UNCONS, resZ / mTPCVDrift * side, trefTOF.getZ(), resX, resZ); // TODO: check if this is correct! + mMatchedTracksPairsSec[sec].emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[ibc][iPropagation], mTrackGid[sec][trkType::UNCONS][cacheTrk[itrk]], trkType::UNCONS, trefTOF.getTime() * 1E-6 - tpctime, trefTOF.getZ(), resXor, resZor, resY); // TODO: check if this is correct! + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setPt(pt); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResX(sqrt(1. / errXinv2)); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResZ(sqrt(1. / errZinv2)); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResT(resT); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setVz(mVZtpcOnly[sec][itrk] + Zshift[ibc][iPropagation]); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setChannel(mainChannel); } } } @@ -1183,24 +1580,48 @@ void MatchTOF::doMatchingForTPC(int sec) return; } //______________________________________________ -int MatchTOF::findFITIndex(int bc, const gsl::span& FITRecPoints) +int MatchTOF::findFITIndex(int bc, const gsl::span& FITRecPoints, unsigned long firstOrbit) { + const auto& FT0Params = o2::ft0::InteractionTag::Instance(); + + if ((!mHasFillScheme) && o2::tof::Utils::hasFillScheme()) { + mHasFillScheme = true; + for (int ibc = 0; ibc < o2::tof::Utils::getNinteractionBC(); ibc++) { + mFillScheme[o2::tof::Utils::getInteractionBC(ibc)] = true; + } + } + if (FITRecPoints.size() == 0) { return -1; } int index = -1; - int distMax = 5; // require bc distance below 5 + int distMax = 10; + bool bestQuality = false; // prioritize FT0 BC with good quality (FT0-AC + vertex) to remove umbiguity in Pb-Pb (not a strict cut because inefficient in pp) + const int distThr = 8; for (unsigned int i = 0; i < FITRecPoints.size(); i++) { + const auto& ft = FITRecPoints[i]; + if (!FT0Params.isSelected(ft)) { + continue; + } const o2::InteractionRecord ir = FITRecPoints[i].getInteractionRecord(); - int bct0 = ir.orbit * o2::constants::lhc::LHCMaxBunches + ir.bc; + if (mHasFillScheme && !mFillScheme[ir.bc]) { + continue; + } + bool quality = (std::abs(FITRecPoints[i].getCollisionTime(0)) < 1000 && std::abs(FITRecPoints[i].getVertex()) < 1000); + if (bestQuality && !quality) { // if current has no good quality and the one previoulsy selected has -> discard the current one + continue; + } + int bct0 = (ir.orbit - firstOrbit) * o2::constants::lhc::LHCMaxBunches + ir.bc; int dist = bc - bct0; - if (dist < 0 || dist > distMax) { + bool worseDistance = dist < 0 || dist > distThr || dist > distMax; + if (worseDistance) { // discard if BC is not in the proper range or it is worse than the one already found continue; } + bestQuality = quality; distMax = dist; index = i; } @@ -1208,13 +1629,13 @@ int MatchTOF::findFITIndex(int bc, const gsl::span& FI return index; } //______________________________________________ -void MatchTOF::selectBestMatches() +void MatchTOF::selectBestMatches(int sec) { if (mSetHighPurity) { - BestMatchesHP(mMatchedTracksPairs, mMatchedTracks, mMatchedTracksIndex, mMatchedClustersIndex, mFITRecPoints, mTOFClusWork, mCalibInfoTOF, mTimestamp, mMCTruthON, mTOFClusLabels, mTracksLblWork, mOutTOFLabels); + BestMatchesHP(mMatchedTracksPairsSec[sec], mMatchedTracks, mMatchedTracksIndex[sec], mMatchedClustersIndex, mFITRecPoints, mTOFClusWork, mCalibInfoTOF, mTimestamp, mMCTruthON, mTOFClusLabels, mTracksLblWork[sec], mOutTOFLabels); return; } - BestMatches(mMatchedTracksPairs, mMatchedTracks, mMatchedTracksIndex, mMatchedClustersIndex, mFITRecPoints, mTOFClusWork, mTracksWork, mCalibInfoTOF, mTimestamp, mMCTruthON, mTOFClusLabels, mTracksLblWork, mOutTOFLabels, mMatchParams->calibMaxChi2); + BestMatches(mMatchedTracksPairsSec[sec], mMatchedTracks, mMatchedTracksIndex[sec], mMatchedClustersIndex, mFITRecPoints, mTOFClusWork, mTracksWork[sec], mCalibInfoTOF, mTimestamp, mMCTruthON, mTOFClusLabels, mTracksLblWork[sec], mOutTOFLabels, mMatchParams->calibMaxChi2); } //______________________________________________ void MatchTOF::BestMatches(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, const std::vector* TracksWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels, float calibMaxChi2) @@ -1226,20 +1647,11 @@ void MatchTOF::BestMatches(std::vector& match int i = 0; // then we take discard the pairs if their track or cluster was already matched (since they are ordered in chi2, we will take the best matching) - for (const o2::dataformats::MatchInfoTOFReco& matchingPair : matchedTracksPairs) { + for (o2::dataformats::MatchInfoTOFReco& matchingPair : matchedTracksPairs) { int trkType = (int)matchingPair.getTrackType(); int itrk = matchingPair.getIdLocal(); - if (matchedTracksIndex[trkType][itrk] != -1) { // the track was already filled - continue; - } - if (matchedClustersIndex[matchingPair.getTOFClIndex()] != -1) { // the cluster was already filled - continue; - } - matchedTracksIndex[trkType][itrk] = matchedTracks[trkType].size(); // index of the MatchInfoTOF correspoding to this track - matchedClustersIndex[matchingPair.getTOFClIndex()] = matchedTracksIndex[trkType][itrk]; // index of the track that was matched to this cluster - int trkTypeSplitted = trkType; auto sourceID = matchingPair.getTrackRef().getSource(); if (sourceID == o2::dataformats::GlobalTrackID::TPCTRD) { @@ -1247,6 +1659,86 @@ void MatchTOF::BestMatches(std::vector& match } else if (sourceID == o2::dataformats::GlobalTrackID::ITSTPCTRD) { trkTypeSplitted = (int)trkType::ITSTPCTRD; } + + if (matchedClustersIndex[matchingPair.getTOFClIndex()] != -1) { // the cluster was already filled + continue; + } + if (matchedTracksIndex[trkType][itrk] != -1) { // the track was already filled + if (trkType) { // not applied for unconstrained tracks (trkType == 0) + // let's check if we can use both matching to improve TOF time + int pairInd = matchedTracksIndex[trkType][itrk]; + auto& prevMatching = matchedTracks[trkTypeSplitted][pairInd]; + + if (pairInd >= matchedTracks[trkTypeSplitted].size()) { + LOG(error) << "Pair index out of range when trying to update TOF time. This should not happen"; + continue; + } + + int istripOld = TOFClusWork[prevMatching.getTOFClIndex()].getMainContributingChannel() / o2::tof::Geo::NPADS; + int istripNew = TOFClusWork[matchingPair.getTOFClIndex()].getMainContributingChannel() / o2::tof::Geo::NPADS; + + if (istripOld != istripNew) { // we accept it only if in a different strip + const o2::track::TrackLTIntegral& intLTnew = matchingPair.getLTIntegralOut(); + const o2::track::TrackLTIntegral& intLTold = prevMatching.getLTIntegralOut(); + + const float cinv = 1.E+10 / TMath::C(); // cm/ps + float deltaT = (intLTnew.getL() - intLTold.getL()) * cinv; + + float timeNew = TOFClusWork[matchingPair.getTOFClIndex()].getTime() - deltaT; + float timeOld = TOFClusWork[prevMatching.getTOFClIndex()].getTime(); + + if (std::abs(timeNew - timeOld) < 200) { + // update time information averaging the two (the second one corrected for the difference in the track length) + prevMatching.setSignal((timeNew + timeOld) * 0.5); + float geanttime = (TOFClusWork[matchingPair.getTOFClIndex()].getTgeant() + TOFClusWork[prevMatching.getTOFClIndex()].getTgeant() - deltaT * 1E-3) * 0.5; + double t0 = (TOFClusWork[matchingPair.getTOFClIndex()].getT0true() + TOFClusWork[prevMatching.getTOFClIndex()].getT0true()) * 0.5; + prevMatching.setTgeant(geanttime); + prevMatching.setT0true(t0); + prevMatching.setChi2(0); // flag such cases with chi2 equal to zero + matchedClustersIndex[matchingPair.getTOFClIndex()] = matchedTracksIndex[trkType][itrk]; // flag also the second cluster as already used + } + } + } + + continue; + } + matchedTracksIndex[trkType][itrk] = matchedTracks[trkTypeSplitted].size(); // index of the MatchInfoTOF correspoding to this track + matchedClustersIndex[matchingPair.getTOFClIndex()] = matchedTracksIndex[trkType][itrk]; // index of the track that was matched to this cluster + + matchingPair.setTgeant(TOFClusWork[matchingPair.getTOFClIndex()].getTgeant()); + matchingPair.setT0true(TOFClusWork[matchingPair.getTOFClIndex()].getT0true()); + + // let's check if cluster has multiple-hits (noferini) + const auto& tofcl = TOFClusWork[matchingPair.getTOFClIndex()]; + if (tofcl.getNumOfContributingChannels() > 1) { + // has an additional hit Up or Down (Z-dir) + matchingPair.setHitPatternUpDown(tofcl.isAdditionalChannelSet(o2::tof::Cluster::kUp) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kUpLeft) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kUpRight) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kDown) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kDownLeft) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kDownRight)); + // has an additional hit Left or Right (X-dir) + matchingPair.setHitPatternLeftRight(tofcl.isAdditionalChannelSet(o2::tof::Cluster::kLeft) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kDownLeft) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kUpLeft) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kRight) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kDownRight) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kUpRight)); + } + + // estimate collision time using FT0 info if available + ULong64_t bclongtofCal = (matchingPair.getSignal() - 10000) * o2::tof::Geo::BC_TIME_INPS_INV; + double t0Best = bclongtofCal * o2::tof::Geo::BC_TIME_INPS; // here just BC + float t0BestRes = 200; + if (FITRecPoints.size() > 0) { + int index = findFITIndex(bclongtofCal, FITRecPoints, mFirstTForbit); + if (index > -1 && FITRecPoints[index].isValidTime(1) && FITRecPoints[index].isValidTime(2)) { // require A and C + t0Best += FITRecPoints[index].getCollisionTime(0); + t0BestRes = 15; + } + } + matchingPair.setFT0Best(t0Best, t0BestRes); matchedTracks[trkTypeSplitted].push_back(matchingPair); // array of MatchInfoTOF // get fit info @@ -1254,14 +1746,15 @@ void MatchTOF::BestMatches(std::vector& match const o2::track::TrackLTIntegral& intLT = matchingPair.getLTIntegralOut(); - if (FITRecPoints.size() > 0) { - int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC(), FITRecPoints); + if (FITRecPoints.size() > 0 && mIsFIT) { + int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC() - o2::tof::Geo::LATENCYWINDOW_IN_BC, FITRecPoints, mFirstTForbit); if (index > -1) { o2::InteractionRecord ir = FITRecPoints[index].getInteractionRecord(); - t0info = ir.bc2ns() * 1E3; + int bct0 = (ir.orbit - mFirstTForbit) * o2::constants::lhc::LHCMaxBunches + ir.bc; + t0info = bct0 * o2::tof::Geo::BC_TIME_INPS; } - } else { // move time to time in orbit to avoid loss of precision when truncating from double to float + } else if (!mIsFIT) { // move time to time in orbit to avoid loss of precision when truncating from double to float int bcStarOrbit = int((TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - intLT.getTOF(o2::track::PID::Pion)) * o2::tof::Geo::BC_TIME_INPS_INV); bcStarOrbit = (bcStarOrbit / o2::constants::lhc::LHCMaxBunches) * o2::constants::lhc::LHCMaxBunches; // truncation t0info = bcStarOrbit * o2::tof::Geo::BC_TIME_INPS; @@ -1280,7 +1773,14 @@ void MatchTOF::BestMatches(std::vector& match } int mask = 0; - float deltat = o2::tof::Utils::subtractInteractionBC(TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - t0info - intLT.getTOF(o2::track::PID::Pion), mask, true); + float deltat; + + if (t0info > 0 && mIsFIT) { // FT0 BC found + deltat = TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - t0info - intLT.getTOF(o2::track::PID::Pion) - o2::tof::Geo::LATENCYWINDOW_IN_BC * o2::tof::Geo::BC_TIME_INPS; // latency subtracted + mask = 1 << 8; // only FT0 BC allowed (-> BC=0 since t0info already subtracted and assumed to be aligned to the true IntBC) + } else if (!mIsFIT) { + deltat = o2::tof::Utils::subtractInteractionBC(TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - t0info - intLT.getTOF(o2::track::PID::Pion), mask, true); + } const o2::track::TrackParCov& trc = TracksWork[trkType][itrk].first; float pt = trc.getPt(); // from outer parameters! @@ -1301,7 +1801,7 @@ void MatchTOF::BestMatches(std::vector& match flags = flags | o2::dataformats::CalibInfoTOF::kMultiHit; } - if (matchingPair.getChi2() < calibMaxChi2) { // extra cut in ChiSquare for storing calib info + if (matchingPair.getChi2() < calibMaxChi2 && t0info > 0) { // extra cut in ChiSquare for storing calib info CalibInfoTOF.emplace_back(TOFClusWork[matchingPair.getTOFClIndex()].getMainContributingChannel(), Timestamp / 1000 + int(TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() * 1E-12), // add time stamp deltat, @@ -1394,12 +1894,13 @@ void MatchTOF::BestMatchesHP(std::vector& mat // get fit info double t0info = 0; - if (FITRecPoints.size() > 0) { - int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC(), FITRecPoints); + if (FITRecPoints.size() > 0 && mIsFIT) { + int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC() - o2::tof::Geo::LATENCYWINDOW_IN_BC, FITRecPoints, mFirstTForbit); if (index > -1) { o2::InteractionRecord ir = FITRecPoints[index].getInteractionRecord(); - t0info = ir.bc2ns() * 1E3; + int bct0 = (ir.orbit - mFirstTForbit) * o2::constants::lhc::LHCMaxBunches + ir.bc; + t0info = bct0 * o2::tof::Geo::BC_TIME_INPS; } } else { // move time to time in orbit to avoid loss of precision when truncating from double to float int bcStarOrbit = int((TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - intLT.getTOF(o2::track::PID::Pion)) * o2::tof::Geo::BC_TIME_INPS_INV); @@ -1448,7 +1949,7 @@ bool MatchTOF::propagateToRefX(o2::track::TrackParCov& trc, float xRef, float st refReached = true; // we reached the 371cm reference } istep++; - if (fabs(trc.getY()) > trc.getX() * tanHalfSector) { // we are still in the same sector + if (std::abs(trc.getY()) > trc.getX() * tanHalfSector) { // we are still in the same sector // we need to rotate the track to go to the new sector //Printf("propagateToRefX: changing sector"); auto alphaNew = o2::math_utils::angle2Alpha(trc.getPhiPos()); @@ -1467,9 +1968,8 @@ bool MatchTOF::propagateToRefX(o2::track::TrackParCov& trc, float xRef, float st //Printf("propagateToRefX: snp of teh track is %f (--> %f grad)", trc.getSnp(), TMath::ASin(trc.getSnp())*TMath::RadToDeg()); return refReached && std::abs(trc.getSnp()) < 0.95; // Here we need to put MAXSNP } - //______________________________________________ -bool MatchTOF::propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef, float stepInCm, float bzField) +bool MatchTOF::propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef, float stepInCm, float bzField) { // propagate track to matching reference X without using the covariance matrix // we create the copy of the track in a TrackPar object (no cov matrix) @@ -1488,7 +1988,7 @@ bool MatchTOF::propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef refReached = true; // we reached the 371cm reference } istep++; - if (fabs(trcNoCov.getY()) > trcNoCov.getX() * tanHalfSector) { // we are still in the same sector + if (std::abs(trcNoCov.getY()) > trcNoCov.getX() * tanHalfSector) { // we are still in the same sector // we need to rotate the track to go to the new sector //Printf("propagateToRefX: changing sector"); auto alphaNew = o2::math_utils::angle2Alpha(trcNoCov.getPhiPos()); @@ -1505,6 +2005,61 @@ bool MatchTOF::propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef // if (std::abs(trc.getSnp()) > MAXSNP) Printf("propagateToRefX: condition on snp not ok, returning false"); //Printf("propagateToRefX: snp of teh track is %f (--> %f grad)", trcNoCov.getSnp(), TMath::ASin(trcNoCov.getSnp())*TMath::RadToDeg()); + return refReached && std::abs(trcNoCov.getSnp()) < 0.95 && std::abs(trcNoCov.getZ()) < Geo::MAXHZTOF; // Here we need to put MAXSNP +} +//______________________________________________ +void MatchTOF::updateTL(o2::track::TrackLTIntegral& intLT, float deltal) +{ + for (int i = 0; i < intLT.getNTOFs(); i++) { + float betainv = intLT.getTOF(i) / intLT.getL(); + intLT.setTOF(intLT.getTOF(i) + deltal * betainv, i); + } + intLT.setL(intLT.getL() + deltal); +} + +//______________________________________________ +bool MatchTOF::propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef, float stepInCm, float bzField, float pos[3]) +{ + // propagate track to matching reference X without using the covariance matrix + // we create the copy of the track in a TrackPar object (no cov matrix) + o2::track::TrackPar trcNoCov(trc); + const float tanHalfSector = tan(o2::constants::math::SectorSpanRad / 2); + bool refReached = false; + float xStart = trcNoCov.getX(); + // the first propagation will be from 2m, if the track is not at least at 2m + if (xStart < 50.) { + xStart = 50.; + } + int istep = 1; + bool hasPropagated = trcNoCov.propagateParamTo(xStart + istep * stepInCm, bzField); + while (hasPropagated) { + if (trcNoCov.getX() > xRef) { + refReached = true; // we reached the 371cm reference + } + istep++; + if (fabs(trcNoCov.getY()) > trcNoCov.getX() * tanHalfSector) { // we are still in the same sector + // we need to rotate the track to go to the new sector + // Printf("propagateToRefX: changing sector"); + auto alphaNew = o2::math_utils::angle2Alpha(trcNoCov.getPhiPos()); + if (!trcNoCov.rotateParam(alphaNew) != 0) { + // Printf("propagateToRefX: failed to rotate"); + break; // failed (this line is taken from MatchTPCITS and the following comment too: RS: check effect on matching tracks to neighbouring sector) + } + } + if (refReached) { + break; + } + hasPropagated = trcNoCov.propagateParamTo(xStart + istep * stepInCm, bzField); + } + // if (std::abs(trc.getSnp()) > MAXSNP) Printf("propagateToRefX: condition on snp not ok, returning false"); + // Printf("propagateToRefX: snp of teh track is %f (--> %f grad)", trcNoCov.getSnp(), TMath::ASin(trcNoCov.getSnp())*TMath::RadToDeg()); + + o2::track::TrackParametrization::dim3_t xyz; + trcNoCov.getXYZGlo(xyz); + pos[0] = xyz[0]; + pos[1] = xyz[1]; + pos[2] = xyz[2]; + return refReached && std::abs(trcNoCov.getSnp()) < 0.95 && TMath::Abs(trcNoCov.getZ()) < Geo::MAXHZTOF; // Here we need to put MAXSNP } @@ -1528,7 +2083,16 @@ void MatchTOF::updateTimeDependentParams() mTPCBin2Z = mTPCTBinMUS * mTPCVDrift; mBz = o2::base::Propagator::Instance()->getNominalBz(); - mMaxInvPt = abs(mBz) > 0.1 ? 1. / (abs(mBz) * 0.05) : 999.; + mMaxInvPt = std::abs(mBz) > 0.1 ? 1. / (std::abs(mBz) * 0.05) : 999.; + + const auto& trackTune = TrackTuneParams::Instance(); + float scale = mCTPLumi; + if (scale < 0.f) { + LOGP(warning, "Negative scale factor for TPC covariance correction, setting it to zero"); + scale = 0.f; + } + mCovDiagInner = trackTune.getCovInnerTotal(scale); + mCovDiagOuter = trackTune.getCovOuterTotal(scale); } //_________________________________________________________ @@ -1541,7 +2105,7 @@ bool MatchTOF::makeConstrainedTPCTrack(int matchedID, o2::dataformats::TrackTPCT // correct the time of the track auto timeTOFMUS = (tofCl.getTime() - intLT.getTOF(tpcTrOrig.getPID())) * 1e-6; // tof time in \mus, FIXME: account for time of flight to R TOF - auto timeTOFTB = (timeTOFMUS + mTPCDriftTimeOffset) * mTPCTBinMUSInv; // TOF time in TPC timebins, including TPC time offset + auto timeTOFTB = timeTOFMUS * mTPCTBinMUSInv; // TOF time in TPC timebins, including TPC time offset auto deltaTBins = timeTOFTB - tpcTrOrig.getTime0(); // time shift in timeBins float timeErr = 0.010; // assume 10 ns error FIXME auto dzCorr = deltaTBins * mTPCBin2Z; @@ -1576,23 +2140,23 @@ bool MatchTOF::makeConstrainedTPCTrack(int matchedID, o2::dataformats::TrackTPCT if (mTPCClusterIdxStruct) { // refit was requested float chi2 = 0; mTPCRefitter->setTrackReferenceX(o2::constants::geom::XTPCInnerRef); - if (mTPCRefitter->RefitTrackAsTrackParCov(trConstr, tpcTrOrig.getClusterRef(), timeTOFTB, &chi2, false, true) < 0) { // outward refit after resetting cov.mat. - LOG(debug) << "Inward Refit failed"; + if (mTPCRefitter->RefitTrackAsTrackParCov(trConstr, tpcTrOrig.getClusterRef(), timeTOFTB, &chi2, false, true) < 0) { // inward refit after resetting cov.mat. + LOGP(debug, "Inward Refit failed {}", trConstr.asString()); return false; } - const auto& trackTune = o2::globaltracking::TrackTuneParams::Instance(); // if needed, correct TPC track after inwar refit + const auto& trackTune = TrackTuneParams::Instance(); // if needed, correct TPC track after inward refit if (!trackTune.useTPCInnerCorr) { trConstr.updateParams(trackTune.tpcParInner); } - if (trackTune.tpcCovInnerType != o2::globaltracking::TrackTuneParams::AddCovType::Disable) { - trConstr.updateCov(trackTune.tpcCovInner, trackTune.tpcCovInnerType == o2::globaltracking::TrackTuneParams::AddCovType::WithCorrelations); + if (trackTune.tpcCovInnerType != TrackTuneParams::AddCovType::Disable) { + trConstr.updateCov(mCovDiagInner, trackTune.tpcCovInnerType == TrackTuneParams::AddCovType::WithCorrelations); } trConstr.setChi2Refit(chi2); // mTPCRefitter->setTrackReferenceX(o2::constants::geom::XTPCOuterRef); if (mTPCRefitter->RefitTrackAsTrackParCov(trConstrOut, tpcTrOrig.getClusterRef(), timeTOFTB, &chi2, true, true) < 0) { // outward refit after resetting cov.mat. - LOG(debug) << "Outward refit failed"; + LOGP(debug, "Outward Refit failed {}", trConstrOut.asString()); return false; } } @@ -1604,8 +2168,8 @@ bool MatchTOF::makeConstrainedTPCTrack(int matchedID, o2::dataformats::TrackTPCT void MatchTOF::checkRefitter() { if (mTPCClusterIdxStruct) { - mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMapsHelper, mBz, - mTPCTrackClusIdx.data(), mTPCRefitterShMap.data(), - nullptr, o2::base::Propagator::Instance()); + mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMaps, mBz, + mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), + mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); } } diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index 8ce6df8bf8e1f..1457790c7c531 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -9,8 +9,16 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "GPUO2ExternalUser.h" // Needed for propper settings in GPUParam.h +#include "GPUParam.h" +#include "GPUParam.inc" +#ifdef WITH_OPENMP +#include +#endif + #include #include +#include #include #include "Field/MagneticField.h" @@ -27,12 +35,12 @@ #include "CommonConstants/PhysicsConstants.h" #include "CommonConstants/GeomConstants.h" #include "DetectorsBase/GeometryManager.h" +#include "DetectorsBase/GlobalParams.h" #include #include #include #include -#include "DataFormatsParameters/GRPObject.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "CommonUtils/NameConf.h" @@ -42,13 +50,11 @@ #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" #include "DataFormatsGlobalTracking/TrackTuneParams.h" #include "DataFormatsTPC/WorkflowHelper.h" - +#include "DetectorsBase/GRPGeomHelper.h" #include "ITStracking/IOUtils.h" -#include "GPUO2Interface.h" // Needed for propper settings in GPUParam.h - -#ifdef WITH_OPENMP -#include +#ifdef ENABLE_UPGRADES +#include "ITS3Reconstruction/IOUtils.h" #endif using namespace o2::globaltracking; @@ -57,8 +63,13 @@ using MatrixDSym4 = ROOT::Math::SMatrix>; using NAMES = o2::base::NameConf; using GTrackID = o2::dataformats::GlobalTrackID; +using TrackTunePar = o2::globaltracking::TrackTuneParams; constexpr float MatchTPCITS::Tan70, MatchTPCITS::Cos70I2, MatchTPCITS::MaxSnp, MatchTPCITS::MaxTgp; +LinksPoolMT* TPCABSeed::gLinksPool = nullptr; + +const o2::gpu::GPUTPCGeometry MatchTPCITS::TPCGeometry{}; + //______________________________________________ MatchTPCITS::MatchTPCITS() = default; @@ -66,7 +77,13 @@ MatchTPCITS::MatchTPCITS() = default; MatchTPCITS::~MatchTPCITS() = default; //______________________________________________ -void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) +void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp, + pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib) { ///< perform matching for provided input if (!mInitDone) { @@ -84,8 +101,8 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) break; } if (mVDriftCalibOn) { // in the beginning of the output vector we send the full and reference VDrift used for this TF - mTglITSTPC.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, -999.); - mTglITSTPC.emplace_back(mTPCDriftTimeOffset, mTPCDrift.refTimeOffset, -999.); + calib.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, mTPCDrift.refTP); + calib.emplace_back(mTPCDriftTimeOffset, mTPCDrift.refTimeOffset, -999.); } mTimer[SWDoMatching].Start(false); @@ -93,7 +110,7 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) doMatching(sec); } mTimer[SWDoMatching].Stop(); - if (0) { // enabling this creates very verbose output + if constexpr (false) { // enabling this creates very verbose output mTimer[SWTot].Stop(); printCandidatesTPC(); printCandidatesITS(); @@ -102,12 +119,16 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) selectBestMatches(); - refitWinners(); - - if (mUseFT0 && Params::Instance().runAfterBurner) { - runAfterBurner(); + bool fullMatchRefitDone = false; + if (mUseFT0 && mParams->runAfterBurner) { + fullMatchRefitDone = runAfterBurner(matchedTracks, matchLabels, ABTrackletLabels, ABTrackletClusterIDs, ABTrackletRefs, calib); + } + if (!fullMatchRefitDone) { + refitWinners(matchedTracks, matchLabels, calib); // it afterburner is active, full matches refit will be done by it + } + if (mParams->verbosity > 0) { + reportSizes(matchedTracks, ABTrackletRefs, ABTrackletClusterIDs, matchLabels, ABTrackletLabels, calib); } - #ifdef _ALLOW_DEBUG_TREES_ if (mDBGOut && isDebugFlag(WinnerMatchesTree)) { dumpWinnerMatches(); @@ -118,13 +139,19 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) mTimer[SWTot].Stop(); if (mParams->verbosity > 0) { - for (int i = 0; i < NStopWatches; i++) { - LOGF(info, "Timing for %15s: Cpu: %.3e Real: %.3e s in %d slots of TF#%d", TimerName[i], mTimer[i].CpuTime(), mTimer[i].RealTime(), mTimer[i].Counter() - 1, mTFCount); - } + reportTiming(); } mTFCount++; } +//______________________________________________ +void MatchTPCITS::reportTiming() +{ + for (int i = 0; i < NStopWatches; i++) { + LOGF(info, "Timing for %15s: Cpu: %.3e Real: %.3e s in %d slots of TF#%d", TimerName[i], mTimer[i].CpuTime(), mTimer[i].RealTime(), mTimer[i].Counter() - 1, mTFCount); + } +} + //______________________________________________ void MatchTPCITS::end() { @@ -140,21 +167,18 @@ void MatchTPCITS::clear() mMatchRecordsTPC.clear(); mMatchRecordsITS.clear(); mWinnerChi2Refit.clear(); - mMatchedTracks.clear(); mITSWork.clear(); mTPCWork.clear(); mInteractions.clear(); mITSROFTimes.clear(); mITSTrackROFContMapping.clear(); mITSClustersArray.clear(); + mITSClusterSizes.clear(); mTPCABSeeds.clear(); mTPCABIndexCache.clear(); mABWinnersIDs.clear(); mABClusterLinkIndex.clear(); - mABTrackletRefs.clear(); - mABTrackletClusterIDs.clear(); - mABTrackletLabels.clear(); - mTglITSTPC.clear(); + mNMatchesControl = 0; for (int sec = o2::constants::math::NSectors; sec--;) { mITSSectIndexCache[sec].clear(); @@ -164,9 +188,11 @@ void MatchTPCITS::clear() } if (mMCTruthON) { - mOutLabels.clear(); - mITSROFTimes.clear(); mTPCLblWork.clear(); + mITSLblWork.clear(); + } + for (int i = 0; i < mNThreads; i++) { + mABLinksPool.threadPool[i].clear(); } } @@ -179,9 +205,10 @@ void MatchTPCITS::setTPCVDrift(const o2::tpc::VDriftCorrFact& v) } //______________________________________________ -void MatchTPCITS::setTPCCorrMaps(o2::gpu::CorrectionMapsHelper* maph) +void MatchTPCITS::setTPCCorrMaps(const o2::gpu::TPCFastTransformPOD* maph, float lumi) { - mTPCCorrMapsHelper = maph; + mTPCCorrMaps = maph; + mLumiCTP = lumi; } //______________________________________________ @@ -210,7 +237,7 @@ void MatchTPCITS::init() // make sure T2GRot matrices are loaded into ITS geometry helper o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)); - mSectEdgeMargin2 = mParams->crudeAbsDiffCut[o2::track::kY] * mParams->crudeAbsDiffCut[o2::track::kY]; ///< precalculated ^2 + mSectEdgeMargin = mParams->crudeAbsDiffCut[o2::track::kY] / std::sqrt(Cos70I2); #ifdef _ALLOW_DEBUG_TREES_ // debug streamer @@ -219,7 +246,9 @@ void MatchTPCITS::init() } #endif - mRGHelper.init(); // prepare helper for TPC track / ITS clusters matching + if (mParams->runAfterBurner) { // only used in AfterBurner + mRGHelper.init(mParams->lowestLayerAB); // prepare helper for TPC track / ITS clusters matching + } clear(); @@ -256,6 +285,15 @@ void MatchTPCITS::updateTimeDependentParams() o2::math_utils::Point3D p0(90., 1., 1), p1(90., 100., 100.); auto matbd = o2::base::Propagator::Instance()->getMatBudget(mParams->matCorr, p0, p1); mTPCmeanX0Inv = matbd.meanX2X0 / matbd.length; + + const auto& trackTune = TrackTuneParams::Instance(); + float scale = mLumiCTP; + if (scale < 0.f) { + LOGP(warning, "Negative scale factor for TPC covariance correction, setting it to zero"); + scale = 0.f; + } + mCovDiagInner = trackTune.getCovInnerTotal(scale); + mCovDiagOuter = trackTune.getCovOuterTotal(scale); } //______________________________________________ @@ -263,8 +301,9 @@ void MatchTPCITS::selectBestMatches() { ///< loop over match records and select the ones with best chi2 mTimer[SWSelectBest].Start(false); - int nValidated = 0, iter = 0, nValidatedTotal = 0; - + int nValidated = 0, iter = 0; + mNMatches = 0; + mNCalibPrelim = 0; do { nValidated = 0; int ntpc = mTPCWork.size(), nremaining = 0; @@ -276,6 +315,9 @@ void MatchTPCITS::selectBestMatches() nremaining++; if (validateTPCMatch(it)) { nValidated++; + if (mVDriftCalibOn && (!mFieldON || std::abs(tTPC.getQ2Pt()) < mParams->maxVDriftTrackQ2Pt)) { + mNCalibPrelim++; + } continue; } } @@ -283,31 +325,45 @@ void MatchTPCITS::selectBestMatches() LOGP(info, "iter {}: Validated {} of {} remaining matches", iter, nValidated, nremaining); } iter++; - nValidatedTotal += nValidated; + mNMatches += nValidated; } while (nValidated); mTimer[SWSelectBest].Stop(); - LOGP(info, "Validated {} matches for {} TPC and {} ITS tracks in {} iterations", nValidatedTotal, mTPCWork.size(), mITSWork.size(), iter); + LOGP(info, "Validated {} matches out of {} for {} TPC and {} ITS tracks in {} iterations", mNMatches, mNMatchesControl, mTPCWork.size(), mITSWork.size(), iter); } //______________________________________________ bool MatchTPCITS::validateTPCMatch(int iTPC) { - const auto& tTPC = mTPCWork[iTPC]; + auto& tTPC = mTPCWork[iTPC]; auto& rcTPC = mMatchRecordsTPC[tTPC.matchID]; // best TPC->ITS match - /* // should never happen - if (rcTPC.nextRecID == Validated) { - LOG(warning) << "TPC->ITS was already validated"; - return false; // RS do we need this - } - */ // check if it is consistent with corresponding ITS->TPC match auto& tITS = mITSWork[rcTPC.partnerID]; // partner ITS track auto& rcITS = mMatchRecordsITS[tITS.matchID]; // best ITS->TPC match record if (rcITS.nextRecID == Validated) { return false; } - if (rcITS.partnerID == iTPC) { // is best matching TPC track for this ITS track actually iTPC? + if (rcITS.partnerID == iTPC) { // is best matching TPC track for this ITS track actually iTPC? + int cloneID = tITS.getCloneShift(); // check if there is a clone of tITS + while (cloneID) { + cloneID += rcTPC.partnerID; + auto& tITSClone = mITSWork[cloneID]; + if (isDisabledITS(tITSClone)) { // ignore clone + break; + } + int nextITSCloneMatchID = tITSClone.matchID; + if (rcITS.isBetter(mMatchRecordsITS[nextITSCloneMatchID])) { // best ITS->TPC match record for the clone is worse than the rcITS + LOGP(debug, "Suppressing clone cloneID={} of winner clone {} of source ITS {}", cloneID, rcTPC.partnerID, tITS.sourceID); + while (nextITSCloneMatchID > MinusOne) { + auto& rcITSClone = mMatchRecordsITS[nextITSCloneMatchID]; + removeITSfromTPC(cloneID, rcITSClone.partnerID); + nextITSCloneMatchID = rcITSClone.nextRecID; + } + tITSClone.matchID = MinusTen; // disable + break; + } + return false; // ignore match at this iteration + } // unlink winner TPC track from all ITS candidates except winning one int nextTPC = rcTPC.nextRecID; while (nextTPC > MinusOne) { @@ -326,6 +382,7 @@ bool MatchTPCITS::validateTPCMatch(int iTPC) nextITS = rcITSrem.nextRecID; } rcITS.nextRecID = Validated; + tTPC.gid.setBit(0); // Flag full match return true; } return false; @@ -357,24 +414,30 @@ int MatchTPCITS::getNMatchRecordsITS(const TrackLocITS& tTPC) const } //______________________________________________ -void MatchTPCITS::addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr, GTrackID srcGID, int tpcID) +int MatchTPCITS::addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr, GTrackID srcGID, int tpcID) { // account single TPC seed, can be from standalone TPC track or constrained track from match to TRD and/or TOF const float SQRT12DInv = 2. / sqrt(12.); if (_tr.getX() > o2::constants::geom::XTPCInnerRef + 0.1 || std::abs(_tr.getQ2Pt()) > mMinTPCTrackPtInv) { - return; + return -99; } const auto& tpcOrig = mTPCTracksArray[tpcID]; // discard tracks w/o certain number of total or innermost pads (last cluster is innermost one) if (tpcOrig.getNClusterReferences() < mParams->minTPCClusters) { - return; + return -89; } uint8_t clSect = 0, clRow = 0; uint32_t clIdx = 0; tpcOrig.getClusterReference(mTPCTrackClusIdx, tpcOrig.getNClusterReferences() - 1, clSect, clRow, clIdx); - if (clRow > mParams->askMinTPCRow) { - return; + if (clRow > mParams->askMinTPCRow[clSect]) { + return -9; + } + const auto& clus = mTPCClusterIdxStruct->clusters[clSect][clRow][clIdx]; + uint8_t padFromEdge = uint8_t(clus.getPad()); + if (padFromEdge > TPCGeometry.NPads(clRow) / 2) { + padFromEdge = TPCGeometry.NPads(clRow) - 1 - padFromEdge; } + // create working copy of track param bool extConstrained = srcGID.getSource() != GTrackID::TPC; if (extConstrained) { @@ -383,35 +446,37 @@ void MatchTPCITS::addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr += tpcTimeBin2MUS(tpcOrig.hasBothSidesClusters() ? mParams->safeMarginTPCITSTimeBin : mTPCTimeEdgeTSafeMargin); } auto& trc = mTPCWork.emplace_back( - TrackLocTPC{_tr, {t0 - terr, t0 + terr}, extConstrained ? t0 : tpcTimeBin2MUS(tpcOrig.getTime0()) - mTPCDriftTimeOffset, + TrackLocTPC{_tr, {t0 - terr, t0 + terr}, extConstrained ? t0 : tpcTimeBin2MUS(tpcOrig.getTime0()), // for A/C constrained tracks the terr is half-interval, for externally constrained tracks it is sigma*Nsigma terr * (extConstrained ? mTPCExtConstrainedNSigmaInv : SQRT12DInv), tpcID, srcGID, MinusOne, + clRow, + padFromEdge, (extConstrained || tpcOrig.hasBothSidesClusters()) ? TrackLocTPC::Constrained : (tpcOrig.hasASideClustersOnly() ? TrackLocTPC::ASide : TrackLocTPC::CSide)}); // propagate to matching Xref - const auto& trackTune = o2::globaltracking::TrackTuneParams::Instance(); + const auto& trackTune = TrackTuneParams::Instance(); // only TPC standalone need to be corrected on the input, provided they were not corrected at the source level, // other inputs are corrected in respective upstream matching processes if (srcGID.getSource() == GTrackID::TPC && !trackTune.sourceLevelTPC) { if (trackTune.useTPCInnerCorr) { trc.updateParams(trackTune.tpcParInner); } - if (trackTune.tpcCovInnerType != o2::globaltracking::TrackTuneParams::AddCovType::Disable) { - trc.updateCov(trackTune.tpcCovInner, trackTune.tpcCovInnerType == o2::globaltracking::TrackTuneParams::AddCovType::WithCorrelations); + if (trackTune.tpcCovInnerType != TrackTuneParams::AddCovType::Disable) { + trc.updateCov(mCovDiagInner, trackTune.tpcCovInnerType == TrackTuneParams::AddCovType::WithCorrelations); } } if (!propagateToRefX(trc)) { mTPCWork.pop_back(); // discard track whose propagation to XMatchingRef failed - return; + return -1; } if (mMCTruthON) { mTPCLblWork.emplace_back(mTPCTrkLabels[tpcID]); } - // cache work track index mTPCSectIndexCache[o2::math_utils::angle2Sector(trc.getAlpha())].push_back(mTPCWork.size() - 1); + return 0; } //______________________________________________ @@ -425,18 +490,45 @@ bool MatchTPCITS::prepareTPCData() mTPCTrackClusIdx = inp.getTPCTracksClusterRefs(); mTPCClusterIdxStruct = &inp.inputsTPCclusters->clusterIndex; mTPCRefitterShMap = inp.clusterShMapTPC; + mTPCRefitterOccMap = inp.occupancyMapTPC; + if (mMCTruthON) { mTPCTrkLabels = inp.getTPCTracksMCLabels(); } - int ntr = mTPCTracksArray.size(); - mMatchRecordsTPC.reserve(mParams->maxMatchCandidates * ntr); // number of records might be actually more than N tracks! - mTPCWork.reserve(ntr); + int ntr = mTPCTracksArray.size(), ntrW = 0.7 * ntr; + mMatchRecordsTPC.reserve(mParams->maxMatchCandidates * ntrW); // number of records might be actually more than N tracks! + mTPCWork.reserve(ntrW); if (mMCTruthON) { - mTPCLblWork.reserve(ntr); + mTPCLblWork.reserve(ntrW); } for (int sec = o2::constants::math::NSectors; sec--;) { - mTPCSectIndexCache[sec].reserve(100 + 1.2 * ntr / o2::constants::math::NSectors); + mTPCSectIndexCache[sec].reserve(100 + 1.2 * ntrW / o2::constants::math::NSectors); + } + + mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMaps, mBz, mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); + mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; + mTBinClOcc.clear(); + if (mNTPCOccBinLength > 1 && mTPCRefitterOccMap.size()) { + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + int nTPCBins = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0; + int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); + mTBinClOcc.resize(nTPCOccBins); + std::vector mltHistTB(nTPCOccBins); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + for (int i = 0; i < nTPCOccBins; i++) { + mltHistTB[i] = mTPCRefitter->getParam()->GetUnscaledMult(tb); + tb += mNTPCOccBinLength; + } + for (int i = nTPCOccBins; i--;) { + sm += mltHistTB[i]; + if (i + sumBins < nTPCOccBins) { + sm -= mltHistTB[i + sumBins]; + } + mTBinClOcc[i] = sm; + } + } else { + mTBinClOcc.resize(1); } auto creator = [this](auto& trk, GTrackID gid, float time0, float terr) { @@ -448,20 +540,27 @@ bool MatchTPCITS::prepareTPCData() } else if (std::abs(trk.getQ2Pt()) > mMinTPCTrackPtInv) { return true; } + int resAdd = -100; + int tpcIndex = -1; if constexpr (isTPCTrack()) { // unconstrained TPC track, with t0 = TrackTPC.getTime0+0.5*(DeltaFwd-DeltaBwd) and terr = 0.5*(DeltaFwd+DeltaBwd) in TimeBins - if (!this->mSkipTPCOnly) { - this->addTPCSeed(trk, this->tpcTimeBin2MUS(time0) - this->mTPCDriftTimeOffset, this->tpcTimeBin2MUS(terr), gid, gid.getIndex()); + if (!this->mSkipTPCOnly && trk.getNClusters() > 0) { + resAdd = this->addTPCSeed(trk, this->tpcTimeBin2MUS(time0), this->tpcTimeBin2MUS(terr), gid, (tpcIndex = gid.getIndex())); } } if constexpr (isTPCTOFTrack()) { // TPC track constrained by TOF time, time and its error in \mus - this->addTPCSeed(trk, time0, terr, gid, this->mRecoCont->getTPCContributorGID(gid)); + resAdd = this->addTPCSeed(trk, time0, terr, gid, (tpcIndex = this->mRecoCont->getTPCContributorGID(gid))); } if constexpr (isTRDTrack()) { // TPC track constrained by TRD trigger time, time and its error in \mus - this->addTPCSeed(trk, time0, terr, gid, this->mRecoCont->getTPCContributorGID(gid)); + resAdd = this->addTPCSeed(trk, time0, terr, gid, (tpcIndex = this->mRecoCont->getTPCContributorGID(gid))); + } +#ifdef _ALLOW_DEBUG_TREES_ + if (resAdd > -10 && mDBGOut && isDebugFlag(TPCOrigTree)) { + dumpTPCOrig(resAdd == 0, tpcIndex); } +#endif // note: TPCTRDTPF tracks are actually TRD track with extra TOF cluster return true; }; @@ -530,7 +629,6 @@ bool MatchTPCITS::prepareTPCData() mITSROFofTPCBin[ib] = itsROF; } */ - mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMapsHelper, mBz, mTPCTrackClusIdx.data(), mTPCRefitterShMap.data(), nullptr, o2::base::Propagator::Instance()); mInteractionMUSLUT.clear(); mInteractionMUSLUT.resize(maxTime + 3 * o2::constants::lhc::LHCOrbitMUS, -1); mTimer[SWPrepTPC].Stop(); @@ -556,7 +654,46 @@ bool MatchTPCITS::prepareITSData() const auto patterns = inp.getITSClustersPatterns(); auto pattIt = patterns.begin(); mITSClustersArray.reserve(clusITS.size()); +#ifdef ENABLE_UPGRADES + bool withITS3 = o2::GlobalParams::Instance().withITS3; + if (withITS3) { + o2::its3::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mIT3Dict); + } else { + o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict); + } +#else o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict); +#endif + + // ITS clusters sizes + mITSClusterSizes.reserve(clusITS.size()); + auto pattIt2 = patterns.begin(); + for (auto& clus : clusITS) { + auto pattID = clus.getPatternID(); + unsigned int npix; +#ifdef ENABLE_UPGRADES + auto ib = o2::its3::constants::detID::isDetITS3(clus.getChipID()); + if ((pattID == o2::itsmft::CompCluster::InvalidPatternID) || ((withITS3) ? mIT3Dict->isGroup(pattID, ib) : mITSDict->isGroup(pattID))) { // braces guarantee evaluation order +#else + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mITSDict->isGroup(pattID)) { +#endif + o2::itsmft::ClusterPattern patt; + patt.acquirePattern(pattIt2); + npix = patt.getNPixels(); + } else { +#ifdef ENABLE_UPGRADES + if (withITS3) { + npix = mIT3Dict->getNpixels(pattID, ib); + } else { + npix = mITSDict->getNpixels(pattID); + } +#else + npix = mITSDict->getNpixels(pattID); +#endif + } + mITSClusterSizes.push_back(std::clamp(npix, 0u, 255u)); + } + if (mMCTruthON) { mITSClsLabels = inp.mcITSClusters.get(); } @@ -572,21 +709,26 @@ bool MatchTPCITS::prepareITSData() mITSWork.reserve(mITSTracksArray.size()); // total N ITS clusters in TF - const auto& lastClROF = mITSClusterROFRec[nROFs - 1]; + const auto& lastClROF = mITSClusterROFRec.back(); int nITSClus = lastClROF.getFirstEntry() + lastClROF.getNEntries(); mABClusterLinkIndex.resize(nITSClus, MinusOne); for (int sec = o2::constants::math::NSectors; sec--;) { mITSTimeStart[sec].resize(nROFs, -1); // start of ITS work tracks in every sector } + long nHBF = o2::base::GRPGeomHelper::getNHBFPerTF(); + long maxBCs = nHBF * long(o2::constants::lhc::LHCMaxBunches); + o2::track::TrackLTIntegral trackLTInt; + trackLTInt.setTimeNotNeeded(); for (int irof = 0; irof < nROFs; irof++) { const auto& rofRec = mITSTrackROFRec[irof]; - auto nBC = rofRec.getBCData().differenceInBC(mStartIR); - if (uint64_t(nBC) > 256 * uint64_t(o2::constants::lhc::LHCMaxBunches)) { // RS: fixme: use real NHBFPerTF from GRP + long nBC = rofRec.getBCData().differenceInBC(mStartIR); + if (nBC > maxBCs || nBC < 0) { if (++errCount < MaxErrors2Report) { - LOG(alarm) << "ITS ROF start " << rofRec.getBCData() << " does not match to TF with 1st orbit " << mStartIR; + LOGP(alarm, "ITS ROF#{} start {} is not compatible with TF 1st orbit {} or TF length of {} HBFs", + irof, rofRec.getBCData().asString(), mStartIR.asString(), nHBF); } - return false; + break; } float tMin = nBC * o2::constants::lhc::LHCBunchSpacingMUS + mITSTimeBiasMUS; float tMax = (nBC + mITSROFrameLengthInBC) * o2::constants::lhc::LHCBunchSpacingMUS + mITSTimeBiasMUS; @@ -624,13 +766,18 @@ bool MatchTPCITS::prepareITSData() continue; } // make sure the track is at the ref. radius - if (!propagateToRefX(trc)) { + trackLTInt.clearFast(); + if (!propagateToRefX(trc, &trackLTInt)) { mITSWork.pop_back(); // discard failed track continue; // add to cache only those ITS tracks which reached ref.X and have reasonable snp } + trc.xrho = trackLTInt.getXRho(); // we collect seen x*rho and distance to the reference X for further PID correcrions + trc.dL = trackLTInt.getL(); + if (mMCTruthON) { mITSLblWork.emplace_back(mITSTrkLabels[it]); } + trc.setUserField(0); // cache work track index int sector = o2::math_utils::angle2Sector(trc.getAlpha()); mITSSectIndexCache[sector].push_back(nWorkTracks); @@ -640,19 +787,16 @@ bool MatchTPCITS::prepareITSData() // when propagated to Xr (in this neighbouring sector) and the edge will be (neglecting the curvature) // [(Xr*tg(10)-Yr)/(tgPhir+tg70)]^2 / cos(70)^2 // for the next sector // [(Xr*tg(10)+Yr)/(tgPhir-tg70)]^2 / cos(70)^2 // for the prev sector - // Distances to the sector edges in neighbourings sectors (at Xref in theit proper frames) + // Distances to the sector edges in neighbourings sectors (at Xref in their proper frames) float trcY = trc.getY(), tgp = trc.getSnp(); tgp /= std::sqrt((1.f - tgp) * (1.f + tgp)); // tan of track direction XY - // sector up - float dy2Up = (YMaxAtXMatchingRef - trcY) / (tgp + Tan70); - if ((dy2Up * dy2Up * Cos70I2) < mSectEdgeMargin2) { // need to check this track for matching in sector up - addLastTrackCloneForNeighbourSector(sector < (o2::constants::math::NSectors - 1) ? sector + 1 : 0); - } - // sector down - float dy2Dn = (YMaxAtXMatchingRef + trcY) / (tgp - Tan70); - if ((dy2Dn * dy2Dn * Cos70I2) < mSectEdgeMargin2) { // need to check this track for matching in sector down - addLastTrackCloneForNeighbourSector(sector > 1 ? sector - 1 : o2::constants::math::NSectors - 1); + float dyUpDn[2] = {std::abs((YMaxAtXMatchingRef - trcY) / (tgp + Tan70)), std::abs((YMaxAtXMatchingRef + trcY) / (tgp - Tan70))}; // sector up, down edge distances + // we do the cloning for closest edge only + int sel = dyUpDn[0] < dyUpDn[1] ? 0 : 1; + if (dyUpDn[sel] < mSectEdgeMargin) { // need to check this track for matching in sector up or down + int sectNeib = sel == 0 ? (sector < (o2::constants::math::NSectors - 1) ? sector + 1 : 0) : (sector > 1 ? sector - 1 : o2::constants::math::NSectors - 1); + addLastTrackCloneForNeighbourSector(sectNeib, &trackLTInt); } } } @@ -770,12 +914,22 @@ void MatchTPCITS::doMatching(int sec) } o2::math_utils::Bracketf_t trange(timeCorr - timeCorrErr, timeCorr + timeCorrErr); LOG(debug) << "TPC range: " << trange.asString() << " ITS bracket: " << trefITS.tBracket.asString() << " DZ: " << (trefITS.getZ() - trefTPC.getZ()) << " DT: " << timeCorr; - if (trefITS.tBracket.isOutside(trange)) { - continue; - } - if (timeCorr < 0) { // RS TODO: similar check will be needed to other TF edge - if (timeCorr + mParams->tfEdgeTimeToleranceMUS < 0) { - // continue; + // check if the assigned time is strictly within the ITS bracket + auto cmpITSBracket = trefITS.tBracket.isOutside(timeCorr); + if (cmpITSBracket) { // no, check if brackets are overlapping at all + if (trefITS.tBracket.isOutside(trange)) { + continue; + } + if (mParams->ITSTimeOutliersPolicy == MatchTPCITSParams::TimeOutliersPolicy::Adjust) { + if (cmpITSBracket == o2::math_utils::Bracketf_t::Below) { + timeCorr = trefITS.tBracket.getMin(); + trange.setMin(timeCorr); + } else { + timeCorr = trefITS.tBracket.getMax(); + trange.setMax(timeCorr); + } + } else if (mParams->ITSTimeOutliersPolicy == MatchTPCITSParams::TimeOutliersPolicy::Reject) { + continue; } } @@ -852,6 +1006,7 @@ void MatchTPCITS::doMatching(int sec) << " N TPC tracks checked: " << nCheckTPCControl << " (starting from " << idxMinTPC << "), checks: " << nCheckITSControl << ", matches:" << nMatchesControl; } + mNMatchesControl += nMatchesControl; } //______________________________________________ @@ -941,7 +1096,7 @@ bool MatchTPCITS::registerMatchRecordTPC(int iITS, int iTPC, float chi2, int can } //______________________________________________ -void MatchTPCITS::registerMatchRecordITS(int iITS, int iTPC, float chi2, int candIC) +void MatchTPCITS::registerMatchRecordITS(const int iITS, int iTPC, float chi2, int candIC) { ///< register TPC match in ITS tracks match records, ordering them in quality auto& tITS = mITSWork[iITS]; @@ -995,7 +1150,26 @@ int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kTgl], RejectOnTgl + NSigmaShift))) { return rejFlag; } - diff = tITS.getParam(o2::track::kY) - tTPC.getParam(o2::track::kY); + // do we need to account for different PID hypotheses used for ITS and TPC tracks propagation to ref. X? + bool testOtherPID = false; + float itsParam[5] = {tITS.getY(), tITS.getZ(), tITS.getSnp(), tITS.getTgl(), tITS.getQ2Pt()}; + if (tTPC.getPID() > tITS.getPID() && tITS.dL > 0.f && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) { + o2::track::TrackPar tPID(mITSTracksArray[tITS.sourceID].getParamOut()); // clone original ITS track at highest update point + tPID.setPID(tTPC.getPID(), true); + if (!tPID.correctForELoss(tITS.xrho)) { + return RejectoOnPIDCorr; + } + float dCurv = (tPID.getQ2Pt() - tITS.getQ2Pt()) * mBz * o2::constants::math::B2C, dLEff = tITS.dL * mParams->ITSStepEffFraction, dCurvL = dCurv * dLEff; + itsParam[o2::track::kQ2Pt] = tPID.getQ2Pt(); + itsParam[o2::track::kSnp] += dCurvL; + if (std::abs(itsParam[o2::track::kSnp]) >= 1.) { + itsParam[o2::track::kSnp] = std::copysign(0.99, itsParam[o2::track::kSnp]); + } + itsParam[o2::track::kY] += dCurvL * dLEff * 0.5; + testOtherPID = true; + } + + diff = itsParam[o2::track::kY] - tTPC.getParam(o2::track::kY); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kY], RejectOnY))) { return rejFlag; } @@ -1005,7 +1179,7 @@ int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& } if (tTPC.constraint == TrackLocTPC::Constrained) { // in continuous only constrained tracks can be compared in Z - diff = tITS.getParam(o2::track::kZ) - tTPC.getParam(o2::track::kZ); + diff = itsParam[o2::track::kZ] - tTPC.getParam(o2::track::kZ); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kZ], RejectOnZ))) { return rejFlag; } @@ -1015,7 +1189,7 @@ int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& } } - diff = tITS.getParam(o2::track::kSnp) - tTPC.getParam(o2::track::kSnp); + diff = itsParam[o2::track::kSnp] - tTPC.getParam(o2::track::kSnp); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kSnp], RejectOnSnp))) { return rejFlag; } @@ -1024,7 +1198,7 @@ int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& return rejFlag; } - diff = tITS.getParam(o2::track::kQ2Pt) - tTPC.getParam(o2::track::kQ2Pt); + diff = itsParam[o2::track::kQ2Pt] - tTPC.getParam(o2::track::kQ2Pt); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kQ2Pt], RejectOnQ2Pt))) { return rejFlag; } @@ -1032,9 +1206,18 @@ int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kQ2Pt], RejectOnQ2Pt + NSigmaShift))) { return rejFlag; } - // calculate mutual chi2 excluding Z in continuos mode - chi2 = getPredictedChi2NoZ(tITS, tTPC); - if (chi2 > mParams->cutMatchingChi2 || chi2 < 0.) { // sometime due to the numerical stability the chi2 is negative, reject it. + // calculate mutual chi2 excluding Z in continuous mode + if (testOtherPID) { // temporarily substitute pion params by alternative ones + auto tITSAlt = tITS; + tITSAlt.setPID(tTPC.getPID()); + tITSAlt.setParam(itsParam[o2::track::kY], o2::track::kY); + tITSAlt.setParam(itsParam[o2::track::kSnp], o2::track::kSnp); + tITSAlt.setParam(itsParam[o2::track::kQ2Pt], o2::track::kQ2Pt); + chi2 = getPredictedChi2NoZ(tITSAlt, tTPC); + } else { + chi2 = getPredictedChi2NoZ(tITS, tTPC); + } + if (chi2 > mParams->cutMatchingChi2 || chi2 < 0.) { // sometimes due to the numerical stability the chi2 is negative, reject it. return RejectOnChi2; } @@ -1133,7 +1316,7 @@ float MatchTPCITS::getPredictedChi2NoZ(const o2::track::TrackParCov& trITS, cons } //______________________________________________ -void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector) +void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector, o2::track::TrackLTIntegral* trackLTInt) { // add clone of the src ITS track cache, propagate it to ref.X in requested sector // and register its index in the sector cache. Used for ITS tracks which are so close @@ -1141,9 +1324,16 @@ void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector) mITSWork.push_back(mITSWork.back()); // clone the last track defined in given sector auto& trc = mITSWork.back(); if (trc.rotate(o2::math_utils::sector2Angle(sector)) && - o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., MatCorrType::USEMatCorrNONE)) { + o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag, trackLTInt)) { // TODO: use faster prop here, no 3d field, materials mITSSectIndexCache[sector].push_back(mITSWork.size() - 1); // register track CLONE + // flag clone + mITSWork.back().setCloneBefore(); + if (trackLTInt) { + mITSWork.back().xrho = trackLTInt->getXRho(); // we collect seen x*rho and distance to the reference X for further PID correcrions + mITSWork.back().dL = trackLTInt->getL(); + } + mITSWork[mITSWork.size() - 2].setCloneAfter(); if (mMCTruthON) { mITSLblWork.emplace_back(mITSTrkLabels[trc.sourceID]); } @@ -1153,19 +1343,20 @@ void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector) } //______________________________________________ -bool MatchTPCITS::propagateToRefX(o2::track::TrackParCov& trc) +bool MatchTPCITS::propagateToRefX(o2::track::TrackParCov& trc, o2::track::TrackLTIntegral* lti) { // propagate track to matching reference X, making sure its assigned alpha // is consistent with TPC sector + constexpr float TgHalfSector = 0.17632698f; bool refReached = false; refReached = mParams->XMatchingRef < 10.; // RS: tmp, to cover XMatchingRef~0 int trialsLeft = 2; - while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) { + while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag, lti)) { if (refReached) { break; } // make sure the track is indeed within the sector defined by alpha - if (fabs(trc.getY()) < mParams->XMatchingRef * tan(o2::constants::math::SectorSpanRad / 2)) { + if (fabs(trc.getY()) < mParams->XMatchingRef * TgHalfSector) { refReached = true; break; // ok, within } @@ -1227,56 +1418,205 @@ void MatchTPCITS::print() const } //______________________________________________ -void MatchTPCITS::refitWinners() +void MatchTPCITS::refitWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib) { ///< refit winning tracks - mTimer[SWRefit].Start(false); + matchedTracks.reserve(mNMatches + mABWinnersIDs.size()); + matchedTracks.resize(mNMatches); + if (mMCTruthON) { + matchLabels.reserve(mNMatches + mABWinnersIDs.size()); + matchLabels.resize(mNMatches); + } + if (mVDriftCalibOn) { + calib.reserve(mNCalibPrelim * 1.2 + 1); + } + std::vector tpcToFit; + tpcToFit.reserve(mNMatches); + for (int iTPC = 0; iTPC < (int)mTPCWork.size(); iTPC++) { + const auto& tTPC = mTPCWork[iTPC]; + if (!isDisabledTPC(tTPC) && tTPC.gid.testBit(0)) { + tpcToFit.push_back(iTPC); + } + } LOG(debug) << "Refitting winner matches"; mWinnerChi2Refit.resize(mITSWork.size(), -1.f); - int iITS; - for (int iTPC = 0; iTPC < (int)mTPCWork.size(); iTPC++) { - if (!refitTrackTPCITS(iTPC, iITS)) { - continue; + int nToFit = (int)tpcToFit.size(); + unsigned int nFailedRefit{0}; + +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) \ + reduction(+ : nFailedRefit) +#endif + for (int ifit = 0; ifit < nToFit; ifit++) { + int iTPC = tpcToFit[ifit], iITS; + const auto& tTPC = mTPCWork[iTPC]; + if (refitTrackTPCITS(ifit, iTPC, iITS, matchedTracks, matchLabels, calib)) { + mWinnerChi2Refit[iITS] = matchedTracks.back().getChi2Refit(); + } else { + ++nFailedRefit; + } + } + LOGP(info, "Failed {} TPC-ITS refits out of {}", nFailedRefit, nToFit); + + // suppress tracks failed on refit and fill calib/debug data (if needed) + int last = nToFit; + mNMatches = 0; + for (int ifit = 0; ifit < nToFit; ifit++) { + int itpc = tpcToFit[ifit]; + if (!matchedTracks[ifit].isValid()) { // move the last good track from the back to the slot to delete + while (--last > ifit && !matchedTracks[last].isValid()) { + } // find the highest valid track + if (last > ifit) { + matchedTracks[ifit] = matchedTracks[last]; + matchedTracks[last].invalidate(); + itpc = tpcToFit[last]; + if (mMCTruthON) { + matchLabels[ifit] = matchLabels[last]; + } + } else { + break; + } } - mWinnerChi2Refit[iITS] = mMatchedTracks.back().getChi2Refit(); + if (mDBGOut || mVDriftCalibOn) { + fillCalibDebug(ifit, itpc, matchedTracks[ifit], calib); + } + mNMatches++; + } + // adjust sizes + matchedTracks.resize(mNMatches); + if (mMCTruthON) { + matchLabels.resize(mNMatches); } mTimer[SWRefit].Stop(); } //______________________________________________ -bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) +void MatchTPCITS::fillCalibDebug(int ifit, int iTPC, const o2::dataformats::TrackTPCITS& match, pmr::vector>& calib) +{ + const auto& tTPC = mTPCWork[iTPC]; + int iITS = mMatchRecordsTPC[tTPC.matchID].partnerID; + const auto& tITS = mITSWork[iITS]; + float minDiffFT0 = -999., timeC = 0.f; + std::vector dtimes; + bool fillVDCalib = mVDriftCalibOn && (!mFieldON || std::abs(match.getQ2Pt()) < mParams->maxVDriftTrackQ2Pt); + if (fillVDCalib || mDBGOut) { + timeC = match.getTimeMUS().getTimeStamp(); // find closest FIT record + float minDiffA = mParams->maxVDritTimeOffset; + if (mInteractions.size()) { + int timeC0 = timeC - minDiffA; + if (timeC0 < 0) { + timeC0 = 0; + } + auto entStart = timeC0 < int(mInteractionMUSLUT.size()) ? mInteractionMUSLUT[timeC0] : (mInteractionMUSLUT.size() ? mInteractionMUSLUT.back() : 0); + for (int ent = entStart; ent < (int)mInteractions.size(); ent++) { + float diff = mInteractions[ent].tBracket.mean() - timeC; + if (diff > minDiffA) { + break; // all following will be the same + } + if (diff < -minDiffA) { + continue; + } + dtimes.push_back(diff); + minDiffFT0 = diff; + minDiffA = std::abs(minDiffFT0); + } + } + } + if (fillVDCalib) { + calib.emplace_back(tITS.getTgl(), tTPC.getTgl(), minDiffFT0); + } +#ifdef _ALLOW_DEBUG_TREES_ + if (mDBGOut) { + o2::track::TrackPar itsRefPIDCorr(tITS); // version ad hoc corrected for TPC PID being different from the pion, as it is done in the matching + o2::track::TrackParCov itsRefAltPID(tITS); // version with full propagation to account for TPC PID being different from the pion + itsRefPIDCorr.setX(0); + if (tTPC.getPID() > tITS.getPID() && tITS.dL > 0.f && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) { + itsRefAltPID = mITSTracksArray[tITS.sourceID].getParamOut(); // clone original ITS track at highest update point + itsRefPIDCorr.setPID(tTPC.getPID(), true); + itsRefPIDCorr = itsRefAltPID; + // fast correction + if (!itsRefPIDCorr.correctForELoss(tITS.xrho)) { + itsRefPIDCorr.setX(-10); + } else { + float q2ptPID = itsRefPIDCorr.getQ2Pt(); + float dCurv = (q2ptPID - tITS.getQ2Pt()) * mBz * o2::constants::math::B2C, dLEff = tITS.dL * mParams->ITSStepEffFraction, dCurvL = dCurv * dLEff; + itsRefPIDCorr = tITS; + itsRefPIDCorr.setPID(tTPC.getPID(), true); + itsRefPIDCorr.setQ2Pt(q2ptPID); + auto snp = tITS.getSnp() + dCurvL; + if (std::abs(snp) >= 1.) { + snp = std::copysign(0.99, snp); + } + itsRefPIDCorr.setSnp(snp); + itsRefPIDCorr.setY(tITS.getY() + dCurvL * dLEff * 0.5); + } + // full propagation + if (!itsRefAltPID.rotate(tTPC.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(itsRefAltPID, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) { + itsRefAltPID.setX(-10); + } + } + int tb = mTPCTracksArray[tTPC.sourceID].getTime0() * mNTPCOccBinLengthInv; + float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]); + (*mDBGOut) << "refit" + << "tpcOrig=" << mTPCTracksArray[tTPC.sourceID] << "itsOrig=" << mITSTracksArray[tITS.sourceID] << "itsRef=" << tITS << "tpcRef=" << tTPC << "matchRefit=" << match + << "timeCorr=" << timeC << "dTimeFT0=" << minDiffFT0 << "dTimes=" << dtimes + << "itsRefAltPID=" << itsRefAltPID << "itsRefPIDCorr=" << itsRefPIDCorr; + if (mMCTruthON) { + (*mDBGOut) << "refit" + << "itsLbl=" << mITSLblWork[iITS] << "tpcLbl=" << mTPCLblWork[iTPC]; + } + (*mDBGOut) << "refit" + << "multTPC=" << mltTPC + << "multITSTr=" << mITSTrackROFRec[tITS.roFrame].getNEntries() + << "multITSCl=" << mITSClusterROFRec[tITS.roFrame].getNEntries() + << "tf=" << mTFCount << "\n"; + } +#endif +} + +//______________________________________________ +bool MatchTPCITS::refitTrackTPCITS(int slot, int iTPC, int& iITS, pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib) { ///< refit in inward direction the pair of TPC and ITS tracks const float maxStep = 2.f; // max propagation step (TODO: tune) const auto& tTPC = mTPCWork[iTPC]; - if (isDisabledTPC(tTPC)) { - return false; // no match - } const auto& tpcMatchRec = mMatchRecordsTPC[tTPC.matchID]; iITS = tpcMatchRec.partnerID; const auto& tITS = mITSWork[iITS]; const auto& itsTrOrig = mITSTracksArray[tITS.sourceID]; - - mMatchedTracks.emplace_back(tTPC, tITS); // create a copy of TPC track at xRef - auto& trfit = mMatchedTracks.back(); + auto& trfit = matchedTracks[slot]; + ((o2::track::TrackParCov&)trfit) = (const o2::track::TrackParCov&)tTPC; + trfit.getParamOut() = (const o2::track::TrackParCov&)tITS; // create a copy of TPC track at xRef + trfit.getParamOut().setUserField(0); // reset eventual clones flag + trfit.setPID(tTPC.getPID(), true); + trfit.getParamOut().setPID(tTPC.getPID(), true); // in continuos mode the Z of TPC track is meaningless, unless it is CE crossing // track (currently absent, TODO) if (!mCompareTracksDZ) { trfit.setZ(tITS.getZ()); // fix the seed Z } - float deltaT = (trfit.getZ() - tTPC.getZ()) * mTPCVDriftInv; // time correction in \mus - float timeErr = tTPC.constraint == TrackLocTPC::Constrained ? tTPC.timeErr : std::sqrt(tITS.getSigmaZ2() + tTPC.getSigmaZ2()) * mTPCVDriftInv; // estimate the error on time + float deltaT = (trfit.getZ() - tTPC.getZ()) * mTPCVDriftInv; // time correction in \mus + float timeErr = tTPC.constraint == TrackLocTPC::Constrained ? tTPC.timeErr : std::sqrt(tITS.getSigmaZ2() + tTPC.getSigmaZ2()) * mTPCVDriftInv; // estimate the error on time if (timeErr > mITSTimeResMUS && tTPC.constraint != TrackLocTPC::Constrained) { timeErr = mITSTimeResMUS; // chose smallest error deltaT = tTPC.constraint == TrackLocTPC::ASide ? tITS.tBracket.mean() - tTPC.time0 : tTPC.time0 - tITS.tBracket.mean(); } timeErr += mParams->globalTimeExtraErrorMUS; - float timeC = tTPC.getCorrectedTime(deltaT) + mParams->globalTimeBiasMUS; /// precise time estimate, optionally corrected for bias - if (timeC < 0) { // RS TODO similar check is needed for other edge of TF + float timeC = tTPC.getCorrectedTime(deltaT) + mParams->globalTimeBiasMUS; /// precise time estimate, optionally corrected for bias + o2::math_utils::Bracketf_t::Relation relITS; + if (mParams->ITSTimeOutliersPolicy != MatchTPCITSParams::TimeOutliersPolicy::Tolerate && (relITS = tITS.tBracket.isOutside(timeC))) { /// track time is outside of the nominal ITS time + if (mParams->ITSTimeOutliersPolicy == MatchTPCITSParams::TimeOutliersPolicy::Adjust) { + timeC = relITS == o2::math_utils::Bracketf_t::Below ? tITS.tBracket.getMin() : tITS.tBracket.getMax(); /// assign ITS boundary + } else { // == MatchTPCITSParams::TimeOutliersPolicy::Reject + trfit.invalidate(); + return false; + } + } + if (timeC < 0) { // RS TODO similar check is needed for other edge of TF if (timeC + std::min(timeErr, mParams->tfEdgeTimeToleranceMUS * mTPCTBinMUSInv) < 0) { - mMatchedTracks.pop_back(); // destroy failed track + trfit.invalidate(); // destroy failed track return false; } timeC = 0.; @@ -1317,7 +1657,7 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) if (nclRefit != ncl) { LOGP(debug, "Refit in ITS failed after ncl={}, match between TPC track #{} and ITS track #{}", nclRefit, tTPC.sourceID, tITS.sourceID); LOGP(debug, "{:s}", trfit.asString()); - mMatchedTracks.pop_back(); // destroy failed track + trfit.invalidate(); // destroy failed track return false; } @@ -1330,16 +1670,28 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) maxStep, MatCorrType::USEMatCorrNONE, nullptr, &trfit.getLTIntegralOut())) { LOG(error) << "LTOF integral might be incorrect"; } + auto& tofL = trfit.getLTIntegralOut(); // this is TL integral calculated from the RefX to the DCA to the beamline, invert material integrals for outward propagation + tofL.setX2X0(-tofL.getX2X0()); + tofL.setXRho(-tofL.getXRho()); // outward refit auto& tracOut = trfit.getParamOut(); // this is a clone of ITS outward track already at the matching reference X - auto& tofL = trfit.getLTIntegralOut(); + if (tTPC.getPID() > tITS.getPID() && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) { + // in case the TPC track hypothesis is not pion, we redo the outward propagation to ref.x with TPC PID + tracOut = mITSTracksArray[tITS.sourceID].getParamOut(); + tracOut.setPID(tTPC.getPID(), true); + if (!tracOut.rotate(tTPC.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(tracOut, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) { + LOGP(debug, "Failed to rotate ITSouter with imposed PID to TPC alpha {} or propagate to X={}: {:s}", tTPC.getAlpha(), mParams->XMatchingRef, tracOut.asString()); + trfit.invalidate(); // destroy failed track + return false; + } + } { float xtogo = 0; if (!tracOut.getXatLabR(o2::constants::geom::XTPCInnerRef, xtogo, mBz, o2::track::DirOutward) || !propagator->PropagateToXBxByBz(tracOut, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) { LOG(debug) << "Propagation to inner TPC boundary X=" << xtogo << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp(); - mMatchedTracks.pop_back(); // destroy failed track + trfit.invalidate(); // destroy failed track return false; } if (mVDriftCalibOn) { @@ -1347,44 +1699,34 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) } float chi2Out = 0; auto posStart = tracOut.getXYZGlo(); - auto tImposed = (timeC + mTPCDriftTimeOffset) * mTPCTBinMUSInv; + auto tImposed = timeC * mTPCTBinMUSInv; if (std::abs(tImposed - mTPCTracksArray[tTPC.sourceID].getTime0()) > 550) { LOGP(alarm, "Impossible imposed timebin {} for TPC track time0:{}, dBwd:{} dFwd:{} TB | ZShift:{}, TShift:{}", tImposed, mTPCTracksArray[tTPC.sourceID].getTime0(), mTPCTracksArray[tTPC.sourceID].getDeltaTBwd(), mTPCTracksArray[tTPC.sourceID].getDeltaTFwd(), trfit.getZ() - tTPC.getZ(), deltaT); LOGP(info, "Trc: {}", mTPCTracksArray[tTPC.sourceID].asString()); - mMatchedTracks.pop_back(); // destroy failed track + trfit.invalidate(); // destroy failed track return false; } int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), tImposed, &chi2Out, true, false); // outward refit if (retVal < 0) { LOG(debug) << "Refit failed"; - mMatchedTracks.pop_back(); // destroy failed track + trfit.invalidate(); // destroy failed track return false; } auto posEnd = tracOut.getXYZGlo(); - // account path integrals - float dX = posEnd.x() - posStart.x(), dY = posEnd.y() - posStart.y(), dZ = posEnd.z() - posStart.z(), d2XY = dX * dX + dY * dY; - if (mFieldON) { // circular arc = 2*R*asin(dXY/2R) - float b[3]; - o2::math_utils::Point3D posAv(0.5 * (posEnd.x() + posStart.x()), 0.5 * (posEnd.y() + posStart.y()), 0.5 * (posEnd.z() + posStart.z())); - propagator->getFieldXYZ(posAv, b); - float curvH = std::abs(0.5f * tracOut.getCurvature(b[2])), arcXY = 1. / curvH * std::asin(curvH * std::sqrt(d2XY)); - d2XY = arcXY * arcXY; - } - auto lInt = std::sqrt(d2XY + dZ * dZ); - tofL.addStep(lInt, tracOut.getP2Inv()); + auto lInt = propagator->estimateLTIncrement(tracOut, posStart, posEnd); + tofL.addStep(lInt, tracOut.getQ2P2()); tofL.addX2X0(lInt * mTPCmeanX0Inv); propagator->PropagateToXBxByBz(tracOut, o2::constants::geom::XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL); - const auto& trackTune = o2::globaltracking::TrackTuneParams::Instance(); + const auto& trackTune = TrackTuneParams::Instance(); if (trackTune.useTPCOuterCorr) { tracOut.updateParams(trackTune.tpcParOuter); } - if (trackTune.tpcCovOuterType != o2::globaltracking::TrackTuneParams::AddCovType::Disable) { - tracOut.updateCov(trackTune.tpcCovOuter, trackTune.tpcCovOuterType == o2::globaltracking::TrackTuneParams::AddCovType::WithCorrelations); + if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) { + tracOut.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations); } } - trfit.setChi2Match(tpcMatchRec.chi2); trfit.setChi2Refit(chi2); trfit.setTimeMUS(timeC, timeErr); @@ -1392,85 +1734,42 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) trfit.setRefITS({unsigned(tITS.sourceID), o2::dataformats::GlobalTrackID::ITS}); if (mMCTruthON) { // store MC info: we assign TPC track label and declare the match fake if the ITS and TPC labels are different (their fake flag is ignored) - auto& lbl = mOutLabels.emplace_back(mTPCLblWork[iTPC]); - lbl.setFakeFlag(mITSLblWork[iITS] != mTPCLblWork[iTPC]); + matchLabels[slot] = mTPCLblWork[iTPC]; + matchLabels[slot].setFakeFlag(mITSLblWork[iITS] != mTPCLblWork[iTPC]); } - // if requested, fill the difference of ITS and TPC tracks tgl for vdrift calibation - float minDiffFT0 = -999.; - std::vector dtimes; - if (mVDriftCalibOn && (!mFieldON || std::abs(trfit.getQ2Pt()) < mParams->maxVDriftTrackQ2Pt)) { - // find closest FIT record - float minDiffA = mParams->maxVDritTimeOffset; - if (mInteractions.size()) { - int timeC0 = timeC - minDiffA; - if (timeC0 < 0) { - timeC0 = 0; - } - auto entStart = timeC0 < int(mInteractionMUSLUT.size()) ? mInteractionMUSLUT[timeC0] : (mInteractionMUSLUT.size() ? mInteractionMUSLUT.back() : 0); - for (int ent = entStart; ent < (int)mInteractions.size(); ent++) { - float diff = mInteractions[ent].tBracket.mean() - timeC; - if (diff > minDiffA) { - break; // all following will be the same - } - if (diff < -minDiffA) { - continue; - } - dtimes.push_back(diff); - minDiffFT0 = diff; - minDiffA = std::abs(minDiffFT0); - } - } - mTglITSTPC.emplace_back(tITS.getTgl(), tTPC.getTgl(), minDiffFT0); - } -#ifdef _ALLOW_DEBUG_TREES_ - if (mDBGOut) { - (*mDBGOut) << "refit" - << "tpcOrig=" << mTPCTracksArray[tTPC.sourceID] << "itsOrig=" << itsTrOrig << "itsRef=" << tITS << "tpcRef=" << tTPC << "matchRefit=" << trfit << "timeCorr=" << timeC << "dTimeFT0=" << minDiffFT0 << "dTimes=" << dtimes; - if (mMCTruthON) { - (*mDBGOut) << "refit" - << "itsLbl=" << mITSLblWork[iITS] << "tpcLbl=" << mTPCLblWork[iTPC]; - } - (*mDBGOut) << "refit" - << "\n"; - } -#endif - - // trfit.print(); // DBG - return true; } //______________________________________________ -bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) +bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vector& matchedTracks, pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs) { ///< refit AfterBurner track const float maxStep = 2.f; // max propagation step (TODO: tune) const auto& tTPC = mTPCWork[seed.tpcWID]; const auto& winLink = seed.getLink(seed.winLinkID); - auto& newtr = mMatchedTracks.emplace_back(winLink, winLink); // create a copy of winner param at innermost ITS cluster + auto& newtr = matchedTracks.emplace_back(winLink, winLink); // create a copy of winner param at innermost ITS cluster auto& tracOut = newtr.getParamOut(); auto& tofL = newtr.getLTIntegralOut(); auto geom = o2::its::GeometryTGeo::Instance(); auto propagator = o2::base::Propagator::Instance(); tracOut.resetCovariance(); + o2::track::TrackPar refLin(tracOut); propagator->estimateLTFast(tofL, winLink); // guess about initial value for the track integral from the origin - // refit track outward in the ITS - const auto& itsClRefs = mABTrackletRefs[iITSAB]; + const auto& itsClRefs = ABTrackletRefs[iITSAB]; int nclRefit = 0, ncl = itsClRefs.getNClusters(); - float chi2 = 0.f; // NOTE: the ITS cluster absolute indices are stored from inner to outer layers for (int icl = itsClRefs.getFirstEntry(); icl < itsClRefs.getEntriesBound(); icl++) { - const auto& clus = mITSClustersArray[mABTrackletClusterIDs[icl]]; + const auto& clus = mITSClustersArray[ABTrackletClusterIDs[icl]]; float alpha = geom->getSensorRefAlpha(clus.getSensorID()), x = clus.getX(); - if (!tracOut.rotate(alpha) || + if (!tracOut.rotate(alpha, refLin, propagator->getNominalBz()) || // note: here we also calculate the L,T integral // note: we should eventually use TPC pid in the refit (TODO) // note: since we are at small R, we can use field BZ component at origin rather than 3D field - !propagator->propagateToX(tracOut, x, propagator->getNominalBz(), MaxSnp, maxStep, mUseMatCorrFlag, &tofL)) { + !propagator->propagateToX(tracOut, refLin, x, propagator->getNominalBz(), MaxSnp, maxStep, mUseMatCorrFlag, &tofL)) { break; } chi2 += tracOut.getPredictedChi2(clus); @@ -1482,7 +1781,7 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) if (nclRefit != ncl) { LOGP(debug, "AfterBurner refit in ITS failed after ncl={}, match between TPC track #{} and ITS tracklet #{}", nclRefit, tTPC.sourceID, iITSAB); LOGP(debug, "{:s}", tracOut.asString()); - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } // perform TPC refit with interaction time constraint @@ -1491,40 +1790,30 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) { float xtogo = 0; if (!tracOut.getXatLabR(o2::constants::geom::XTPCInnerRef, xtogo, mBz, o2::track::DirOutward) || - !propagator->PropagateToXBxByBz(tracOut, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) { + !propagator->PropagateToXBxByBz(tracOut, refLin, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) { LOG(debug) << "Propagation to inner TPC boundary X=" << xtogo << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp(); - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } float chi2Out = 0; auto posStart = tracOut.getXYZGlo(); - int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), (timeC + mTPCDriftTimeOffset) * mTPCTBinMUSInv, &chi2Out, true, false); // outward refit + int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), timeC * mTPCTBinMUSInv, &chi2Out, true, false); // outward refit if (retVal < 0) { LOG(debug) << "Refit failed"; - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } auto posEnd = tracOut.getXYZGlo(); - // account path integrals - float dX = posEnd.x() - posStart.x(), dY = posEnd.y() - posStart.y(), dZ = posEnd.z() - posStart.z(), d2XY = dX * dX + dY * dY; - if (mFieldON) { // circular arc = 2*R*asin(dXY/2R) - float b[3]; - o2::math_utils::Point3D posAv(0.5 * (posEnd.x() + posStart.x()), 0.5 * (posEnd.y() + posStart.y()), 0.5 * (posEnd.z() + posStart.z())); - propagator->getFieldXYZ(posAv, b); - float curvH = std::abs(0.5f * tracOut.getCurvature(b[2])), arcXY = 1. / curvH * std::asin(curvH * std::sqrt(d2XY)); - d2XY = arcXY * arcXY; - } - auto lInt = std::sqrt(d2XY + dZ * dZ); - tofL.addStep(lInt, tracOut.getP2Inv()); + auto lInt = propagator->estimateLTIncrement(tracOut, posStart, posEnd); + tofL.addStep(lInt, tracOut.getQ2P2()); tofL.addX2X0(lInt * mTPCmeanX0Inv); propagator->PropagateToXBxByBz(tracOut, o2::constants::geom::XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL); - - const auto& trackTune = o2::globaltracking::TrackTuneParams::Instance(); + const auto& trackTune = TrackTuneParams::Instance(); if (trackTune.useTPCOuterCorr) { tracOut.updateParams(trackTune.tpcParOuter); } - if (trackTune.tpcCovOuterType != o2::globaltracking::TrackTuneParams::AddCovType::Disable) { - tracOut.updateCov(trackTune.tpcCovOuter, trackTune.tpcCovOuterType == o2::globaltracking::TrackTuneParams::AddCovType::WithCorrelations); + if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) { + tracOut.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations); } } @@ -1657,6 +1946,9 @@ int MatchTPCITS::prepareInteractionTimes() } break; } + if (rof >= nITSROFs) { + break; + } mInteractions.emplace_back(ft.getInteractionRecord(), fitTime, ft0Uncertainty, rof, o2::detectors::DetID::FT0); } } @@ -1672,18 +1964,29 @@ int MatchTPCITS::prepareInteractionTimes() } //______________________________________________ -void MatchTPCITS::runAfterBurner() +bool MatchTPCITS::runAfterBurner(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, + pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs, pmr::vector>& calib) { mTimer[SWABSeeds].Start(false); + mNABRefsClus = 0; prepareABSeeds(); int nIntCand = mInteractions.size(), nABSeeds = mTPCABSeeds.size(); LOGP(info, "AfterBurner will check {} seeds from {} TPC tracks and {} interaction candidates with {} threads", nABSeeds, mTPCABIndexCache.size(), nIntCand, mNThreads); // TMP mTimer[SWABSeeds].Stop(); if (!nIntCand || !mTPCABSeeds.size()) { - return; + return false; } mTimer[SWABMatch].Start(false); + std::vector itsChipClRefsBuff(mNThreads); +#ifdef ENABLE_UPGRADES + // with upgrades the datatype changed, hence we need to initialize + // each element individually + std::generate(itsChipClRefsBuff.begin(), itsChipClRefsBuff.end(), []() { + return ITSChipClustersRefs(o2::its::GeometryTGeo::Instance()->getNumberOfChips()); + }); +#endif + #ifdef WITH_OPENMP #pragma omp parallel for schedule(dynamic) num_threads(mNThreads) #endif @@ -1694,13 +1997,13 @@ void MatchTPCITS::runAfterBurner() continue; } #ifdef WITH_OPENMP - int tid = omp_get_thread_num(); + uint8_t tid = (uint8_t)omp_get_thread_num(); #else - int tid = 0; + uint8_t tid = 0; #endif fillClustersForAfterBurner(intCand.rofITS, 1, itsChipClRefsBuff[tid]); // RS FIXME account for possibility of filling 2 ROFs for (int is = intCand.seedsRef.getFirstEntry(); is < intCand.seedsRef.getEntriesBound(); is++) { // loop over all seeds of this interaction candidate - processABSeed(is, itsChipClRefsBuff[tid]); + processABSeed(is, itsChipClRefsBuff[tid], tid); } } mTimer[SWABMatch].Stop(); @@ -1720,16 +2023,7 @@ void MatchTPCITS::runAfterBurner() if (ABSeed.isDisabled()) { continue; } - if (ABSeed.lowestLayer > mParams->requireToReachLayerAB) { - ABSeed.disable(); - continue; - } - auto candID = ABSeed.getBestLinkID(); - if (candID < 0 || ABSeed.getLink(candID).nContLayers < mParams->minContributingLayersAB) { - ABSeed.disable(); - continue; - } - candAB.emplace_back(SID{i, ABSeed.getLink(candID).chi2Norm()}); + candAB.emplace_back(SID{i, ABSeed.getLink(ABSeed.getBestLinkID()).chi2Norm()}); } std::sort(candAB.begin(), candAB.end(), [](SID a, SID b) { return a.chi2 < b.chi2; }); for (int i = 0; i < (int)candAB.size(); i++) { @@ -1753,23 +2047,37 @@ void MatchTPCITS::runAfterBurner() ABSeed.validate(bestID); ABSeed.flagLinkUsedClusters(bestID, mABClusterLinkIndex); mABWinnersIDs.push_back(tTPC.matchID = candAB[i].seedID); + mNABRefsClus += ABSeed.getNLayers(); nwin++; // RSTMP LOG(info) << "Iter: " << iter << " validated seed " << i << "[" << candAB[i].seedID << "/" << candAB[i].chi2 << "] for TPC track " << ABSeed.tpcWID << " last lr: " << int(ABSeed.lowestLayer) << " Ncont: " << int(link.nContLayers); } mTimer[SWABWinners].Stop(); mTimer[SWABRefit].Start(false); - refitABWinners(); + refitABWinners(matchedTracks, matchLabels, ABTrackletLabels, ABTrackletClusterIDs, ABTrackletRefs, calib); mTimer[SWABRefit].Stop(); + return true; } //______________________________________________ -void MatchTPCITS::refitABWinners() +void MatchTPCITS::refitABWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, + pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs, pmr::vector>& calib) { - mABTrackletClusterIDs.reserve(mABWinnersIDs.size() * (o2::its::RecoGeomHelper::getNLayers() - mParams->lowestLayerAB)); - mABTrackletRefs.reserve(mABWinnersIDs.size()); + // refit normal matches + refitWinners(matchedTracks, matchLabels, calib); + + ABTrackletClusterIDs.reserve(mNABRefsClus); + ABTrackletRefs.reserve(mABWinnersIDs.size()); if (mMCTruthON) { - mABTrackletLabels.reserve(mABWinnersIDs.size()); + ABTrackletLabels.reserve(mABWinnersIDs.size()); } + if (matchedTracks.capacity() < mABWinnersIDs.size() + matchedTracks.size()) { + LOGP(warn, "need to expand matched tracks container from {} to {}", matchedTracks.capacity(), mABWinnersIDs.size() + matchedTracks.size()); + matchedTracks.reserve(mABWinnersIDs.size() + matchedTracks.size()); + if (mMCTruthON) { + matchLabels.reserve(mABWinnersIDs.size() + matchedTracks.size()); + } + } + std::map labelOccurence; auto accountClusterLabel = [&labelOccurence, itsClLabs = mITSClsLabels](int clID) { auto labels = itsClLabs->getLabels(clID); @@ -1782,30 +2090,31 @@ void MatchTPCITS::refitABWinners() for (auto wid : mABWinnersIDs) { const auto& ABSeed = mTPCABSeeds[wid]; - int start = mABTrackletClusterIDs.size(); + int start = ABTrackletClusterIDs.size(); int lID = ABSeed.winLinkID, ncl = 0; - auto& clref = mABTrackletRefs.emplace_back(start, ncl); + auto& clref = ABTrackletRefs.emplace_back(start, ncl); while (lID > MinusOne) { const auto& winL = ABSeed.getLink(lID); if (winL.clID > MinusOne) { - mABTrackletClusterIDs.push_back(winL.clID); + ABTrackletClusterIDs.push_back(winL.clID); ncl++; clref.pattern |= 0x1 << winL.layerID; + clref.setClusterSize(winL.layerID, mITSClusterSizes[winL.clID]); if (mMCTruthON) { accountClusterLabel(winL.clID); } } lID = winL.parentID; } - if (!refitABTrack(mABTrackletRefs.size() - 1, ABSeed)) { // on failure, destroy added tracklet reference - mABTrackletRefs.pop_back(); - mABTrackletClusterIDs.resize(start); + clref.setEntries(ncl); + if (!refitABTrack(ABTrackletRefs.size() - 1, ABSeed, matchedTracks, ABTrackletClusterIDs, ABTrackletRefs)) { // on failure, destroy added tracklet reference + ABTrackletRefs.pop_back(); + ABTrackletClusterIDs.resize(start); // RSS if (mMCTruthON) { labelOccurence.clear(); } continue; } - clref.setEntries(ncl); if (mMCTruthON) { o2::MCCompLabel lab; int maxL = 0; // find most encountered label @@ -1819,21 +2128,23 @@ void MatchTPCITS::refitABWinners() lab.setFakeFlag(); } labelOccurence.clear(); - mABTrackletLabels.push_back(lab); // ITSAB tracklet label - auto& lblGlo = mOutLabels.emplace_back(mTPCLblWork[ABSeed.tpcWID]); + ABTrackletLabels.push_back(lab); // ITSAB tracklet label + auto& lblGlo = matchLabels.emplace_back(mTPCLblWork[ABSeed.tpcWID]); lblGlo.setFakeFlag(lab != lblGlo); LOG(debug) << "ABWinner ncl=" << ncl << " mcLBAB " << lab << " mcLBGlo " << lblGlo << " chi2=" << ABSeed.getLink(ABSeed.winLinkID).chi2Norm() << " pT = " << ABSeed.track.getPt(); } // build MC label } - LOG(info) << "AfterBurner validated " << mABTrackletRefs.size() << " tracks"; + LOG(info) << "AfterBurner validated " << ABTrackletRefs.size() << " tracks"; } //______________________________________________ -void MatchTPCITS::processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs) +void MatchTPCITS::processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs, uint8_t tID) { // prepare matching hypothesis tree for given seed auto& ABSeed = mTPCABSeeds[sid]; + ABSeed.threadID = tID; + ABSeed.linksEntry = mABLinksPool.threadPool[tID].size(); followABSeed(ABSeed.track, itsChipClRefs, MinusTen, NITSLayers - 1, ABSeed); // check matches on outermost layer for (int ilr = NITSLayers - 1; ilr > mParams->lowestLayerAB; ilr--) { int nextLinkID = ABSeed.firstInLr[ilr]; @@ -1851,6 +2162,16 @@ void MatchTPCITS::processABSeed(int sid, const ITSChipClustersRefs& itsChipClRef nextLinkID = next2nextLinkID; } } + // is this seed has chance to be validated? + auto candID = ABSeed.getBestLinkID(); + if (ABSeed.isDisabled() || + ABSeed.lowestLayer > mParams->requireToReachLayerAB || + candID < 0 || + ABSeed.getLink(candID).nContLayers < mParams->minContributingLayersAB) { // free unused links + ABSeed.disable(); + mABLinksPool.threadPool[tID].resize(size_t(ABSeed.linksEntry)); + } + /* // RS FIXME remove on final clean-up auto bestLinkID = ABSeed.getBestLinkID(); if (bestLinkID>MinusOne) { @@ -2055,13 +2376,13 @@ int MatchTPCITS::registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackPa { // registers new ABLink on the layer, assigning provided kinematics. The link will be registered in a // way preserving the quality ordering of the links on the layer - int lnkID = ABSeed.trackLinks.size(), nextID = ABSeed.firstInLr[lr], nc = 1 + (parentID > MinusOne ? ABSeed.getLink(parentID).nContLayers : 0); + int lnkID = ABSeed.getNLinks(), nextID = ABSeed.firstInLr[lr], nc = 1 + (parentID > MinusOne ? ABSeed.getLink(parentID).nContLayers : 0); float chi2 = chi2Cl + (parentID > MinusOne ? ABSeed.getLink(parentID).chi2 : 0.); // LOG(info) << "Reg on lr " << lr << " nc = " << nc << " chi2cl=" << chi2Cl << " -> " << chi2; // RSTMP if (ABSeed.firstInLr[lr] == MinusOne) { // no links on this layer yet ABSeed.firstInLr[lr] = lnkID; - ABSeed.trackLinks.emplace_back(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2); + ABSeed.addLink(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2); return lnkID; } // add new link sorting links of this layer in quality @@ -2073,7 +2394,7 @@ int MatchTPCITS::registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackPa bool newIsBetter = parentID <= MinusOne ? isBetter(chi2, nextLink.chi2) : isBetter(ABSeed.getLink(parentID).chi2NormPredict(chi2Cl), nextLink.chi2Norm()); if (newIsBetter) { // need to insert new link before nextLink if (count < mParams->maxABLinksOnLayer) { // will insert in front of nextID - ABSeed.trackLinks.emplace_back(trc, clID, parentID, nextID, lr, nc, laddID, chi2); + ABSeed.addLink(trc, clID, parentID, nextID, lr, nc, laddID, chi2); if (topID == MinusOne) { // are we comparing new link with best link on the layer? ABSeed.firstInLr[lr] = lnkID; // flag as best on the layer } else { @@ -2090,7 +2411,7 @@ int MatchTPCITS::registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackPa } while (nextID > MinusOne); // new link is worse than all others, add it only if there is a room to expand if (count < mParams->maxABLinksOnLayer) { - ABSeed.trackLinks.emplace_back(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2); + ABSeed.addLink(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2); if (topID > MinusOne) { ABSeed.getLink(topID).nextOnLr = lnkID; // point from previous one } @@ -2231,11 +2552,12 @@ void MatchTPCITS::setBunchFilling(const o2::BunchFilling& bf) // find closest (from above) filled bunch int minBC = bf.getFirstFilledBC(), maxBC = bf.getLastFilledBC(); if (minBC < 0 && mUseBCFilling) { - mUseBCFilling = false; - LOG(warning) << "Disabling match validation by BunchFilling as no interacting bunches found"; + if (mUseBCFilling) { + mUseBCFilling = false; + LOG(warning) << "Disabling match validation by BunchFilling as no interacting bunches found"; + } return; } - mUseBCFilling = true; int bcAbove = minBC; for (int i = o2::constants::lhc::LHCMaxBunches; i--;) { if (bf.testBC(i)) { @@ -2366,6 +2688,168 @@ int MatchTPCITS::preselectChipClusters(std::vector& clVecOut, const ClusRan return clVecOut.size(); } +//__________________________________________________________ +void MatchTPCITS::reportSizes(pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib) +{ + size_t sizTotShm = 0, capTotShm = 0, sizTot = 0, capTot = 0, siz = 0, cap = 0, cnt = 0, cntCap = 0; + { + siz = matchedTracks.size() * sizeof(o2::dataformats::TrackTPCITS); + cap = matchedTracks.capacity() * sizeof(o2::dataformats::TrackTPCITS); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, matchedTracks : size {:9} cap {:9}", siz, cap); + // + siz = ABTrackletRefs.size() * sizeof(o2::itsmft::TrkClusRef); + cap = ABTrackletRefs.capacity() * sizeof(o2::itsmft::TrkClusRef); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, ABTrackletRefs : size {:9} cap {:9}", siz, cap); + // + siz = ABTrackletClusterIDs.size() * sizeof(int); + cap = ABTrackletClusterIDs.capacity() * sizeof(int); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, ABTrackletClusterIDs : size {:9} cap {:9}", siz, cap); + // + siz = matchLabels.size() * sizeof(o2::MCCompLabel); + cap = matchLabels.capacity() * sizeof(o2::MCCompLabel); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, matchLabels : size {:9} cap {:9}", siz, cap); + // + siz = ABTrackletLabels.size() * sizeof(o2::MCCompLabel); + cap = ABTrackletLabels.capacity() * sizeof(o2::MCCompLabel); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, ABTrackletLabels : size {:9} cap {:9}", siz, cap); + // + siz = calib.size() * sizeof(o2::dataformats::Triplet); + cap = calib.capacity() * sizeof(o2::dataformats::Triplet); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, calib : size {:9} cap {:9}", siz, cap); + } + { + siz = mITSClustersArray.size() * sizeof(ITSCluster); + cap = mITSClustersArray.capacity() * sizeof(ITSCluster); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSClustersArray : size {:9} cap {:9}", siz, cap); + // + siz = mMatchRecordsTPC.size() * sizeof(MatchRecord); + cap = mMatchRecordsTPC.capacity() * sizeof(MatchRecord); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mMatchRecordsTPC : size {:9} cap {:9}", siz, cap); + // + siz = mMatchRecordsITS.size() * sizeof(MatchRecord); + cap = mMatchRecordsITS.capacity() * sizeof(MatchRecord); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mMatchRecordsITS : size {:9} cap {:9}", siz, cap); + // + siz = mITSROFTimes.size() * sizeof(BracketF); + cap = mITSROFTimes.capacity() * sizeof(BracketF); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSROFTimes : size {:9} cap {:9}", siz, cap); + // + siz = mTPCWork.size() * sizeof(TrackLocTPC); + cap = mTPCWork.capacity() * sizeof(TrackLocTPC); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCWork : size {:9} cap {:9}", siz, cap); + // + siz = mITSWork.size() * sizeof(TrackLocITS); + cap = mITSWork.capacity() * sizeof(TrackLocITS); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSWork : size {:9} cap {:9}", siz, cap); + // + siz = mWinnerChi2Refit.size() * sizeof(float); + cap = mWinnerChi2Refit.capacity() * sizeof(float); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mWinnerChi2Refit : size {:9} cap {:9}", siz, cap); + // + siz = mTPCABSeeds.size() * sizeof(float); + cap = mTPCABSeeds.capacity() * sizeof(float); + cnt = 0; + cntCap = 0; + for (const auto& a : mTPCABSeeds) { + siz += a.sizeInternal(); + cap += a.capInternal(); + cnt += a.getNLinks(); + cntCap += a.getNLinks(); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCABSeeds : size {:9} cap {:9} | internals size:{}/capacity:{} for {} elements", siz, cap, cnt, cntCap, mTPCABSeeds.size()); + // + siz = mTPCABIndexCache.size() * sizeof(int); + cap = mTPCABIndexCache.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCABIndexCache : size {:9} cap {:9}", siz, cap); + // + siz = mABWinnersIDs.size() * sizeof(int); + cap = mABWinnersIDs.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mABWinnersIDs : size {:9} cap {:9}", siz, cap); + // + siz = mABClusterLinkIndex.size() * sizeof(int); + cap = mABClusterLinkIndex.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mABClusterLinkIndex : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mTPCSectIndexCache[is].size() * sizeof(int); + cap += mTPCSectIndexCache[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCSectIndexCache : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mITSSectIndexCache[is].size() * sizeof(int); + cap += mITSSectIndexCache[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSSectIndexCache : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mTPCTimeStart[is].size() * sizeof(int); + cap += mTPCTimeStart[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCTimeStart : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mITSTimeStart[is].size() * sizeof(int); + cap += mITSTimeStart[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSTimeStart : size {:9} cap {:9}", siz, cap); + // + siz = mITSTrackROFContMapping.size() * sizeof(int); + cap = mITSTrackROFContMapping.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, ITSTrackROFContMapping: size {:9} cap {:9}", siz, cap); + } + LOGP(info, "TotalSizes/Capacities: SHM: {}/{} Heap: {}/{}", sizTotShm, capTotShm, sizTot, capTot); +} + //__________________________________________________________ void MatchTPCITS::setNThreads(int n) { @@ -2375,6 +2859,8 @@ void MatchTPCITS::setNThreads(int n) LOG(warning) << "Multithreading is not supported, imposing single thread"; mNThreads = 1; #endif + mABLinksPool.threadPool.resize(mNThreads); + TPCABSeed::gLinksPool = &mABLinksPool; } //<<============================= AfterBurner for TPC-track / ITS cluster matching ===================<< @@ -2391,6 +2877,56 @@ void MatchTPCITS::setDebugFlag(UInt_t flag, bool on) } } +//_________________________________________________________ +void MatchTPCITS::dumpTPCOrig(bool acc, int tpcIndex) +{ + ///< fill debug tree for TPC original tracks (passing pT cut) + mTimer[SWDBG].Start(false); + const auto& tpcOrig = mTPCTracksArray[tpcIndex]; + uint8_t clSect = 0, clRow = 0, prevRow = 0xff, padFromEdge = -1; + uint32_t clIdx = 0; + int nshared = 0; + std::array shMap{}; + bool prevRawShared = false; + for (int i = 0; i < tpcOrig.getNClusterReferences(); i++) { + tpcOrig.getClusterReference(mTPCTrackClusIdx, i, clSect, clRow, clIdx); + unsigned int absoluteIndex = mTPCClusterIdxStruct->clusterOffset[clSect][clRow] + clIdx; + if (mTPCRefitterShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (!(prevRow == clRow && prevRawShared)) { + nshared++; + } + prevRow = clRow; + prevRawShared = true; + } + } + const auto& clus = mTPCClusterIdxStruct->clusters[clSect][clRow][clIdx]; + padFromEdge = uint8_t(clus.getPad()); + if (padFromEdge > TPCGeometry.NPads(clRow) / 2) { + padFromEdge = TPCGeometry.NPads(clRow) - 1 - padFromEdge; + } + int tb = tpcOrig.getTime0() * mNTPCOccBinLengthInv; + float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]); + (*mDBGOut) << "tpcOrig" + << "tf=" << mTFCount + << "index=" << tpcIndex + << "acc=" << acc + << "chi2TPC=" << tpcOrig.getChi2() + << "nClus=" << tpcOrig.getNClusters() + << "nShared=" << nshared + << "time0=" << tpcOrig.getTime0() + << "trc=" << ((o2::track::TrackParCov&)tpcOrig) + << "minRow=" << clRow + << "padFromEdge=" << padFromEdge + << "multTPC=" << mltTPC; + if (mMCTruthON) { + (*mDBGOut) << "tpcOrig" + << "tpcLbl=" << mTPCTrkLabels[tpcIndex]; + } + (*mDBGOut) << "tpcOrig" + << "\n"; + mTimer[SWDBG].Stop(); +} + //_________________________________________________________ void MatchTPCITS::fillTPCITSmatchTree(int itsID, int tpcID, int rejFlag, float chi2, float tCorr) { @@ -2403,17 +2939,20 @@ void MatchTPCITS::fillTPCITSmatchTree(int itsID, int tpcID, int rejFlag, float c if (chi2 < 0.) { // need to recalculate chi2 = getPredictedChi2NoZ(trackITS, trackTPC); } - o2::MCCompLabel lblITS, lblTPC; (*mDBGOut) << "match" << "tf=" << mTFCount << "chi2Match=" << chi2 << "its=" << trackITS << "tpc=" << trackTPC << "tcorr=" << tCorr; if (mMCTruthON) { - lblITS = mITSLblWork[itsID]; - lblTPC = mTPCLblWork[tpcID]; (*mDBGOut) << "match" - << "itsLbl=" << lblITS << "tpcLbl=" << lblTPC; + << "itsLbl=" << mITSLblWork[itsID] << "tpcLbl=" << mTPCLblWork[tpcID]; } + int tb = mTPCTracksArray[trackTPC.sourceID].getTime0() * mNTPCOccBinLengthInv; + float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]); (*mDBGOut) << "match" - << "rejFlag=" << rejFlag << "\n"; + << "rejFlag=" << rejFlag + << "multTPC=" << mltTPC + << "multITSTr=" << mITSTrackROFRec[trackITS.roFrame].getNEntries() + << "multITSCl=" << mITSClusterROFRec[trackITS.roFrame].getNEntries() + << "\n"; mTimer[SWDBG].Stop(); } @@ -2438,14 +2977,16 @@ void MatchTPCITS::dumpWinnerMatches() (*mDBGOut) << "matchWin" << "tf=" << mTFCount << "chi2Match=" << itsMatchRec.chi2 << "chi2Refit=" << mWinnerChi2Refit[iits] << "its=" << tITS << "tpc=" << tTPC; - o2::MCCompLabel lblITS, lblTPC; if (mMCTruthON) { - lblITS = mITSLblWork[iits]; - lblTPC = mTPCLblWork[itpc]; (*mDBGOut) << "matchWin" - << "itsLbl=" << lblITS << "tpcLbl=" << lblTPC; + << "itsLbl=" << mITSLblWork[iits] << "tpcLbl=" << mTPCLblWork[itpc]; } + int tb = mTPCTracksArray[tTPC.sourceID].getTime0() * mNTPCOccBinLengthInv; + float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]); (*mDBGOut) << "matchWin" + << "multTPC=" << mltTPC + << "multITSTr=" << mITSTrackROFRec[tITS.roFrame].getNEntries() + << "multITSCl=" << mITSClusterROFRec[tITS.roFrame].getNEntries() << "\n"; } mTimer[SWDBG].Stop(); diff --git a/Detectors/GlobalTrackingWorkflow/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/CMakeLists.txt index 03202a268b866..6f29ab1930f95 100644 --- a/Detectors/GlobalTrackingWorkflow/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/CMakeLists.txt @@ -11,6 +11,8 @@ # FIXME: do we actually need a library here, or is the executable enough ? +#add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) + o2_add_library(GlobalTrackingWorkflow SOURCES src/TrackWriterTPCITSSpec.cxx src/TPCITSMatchingSpec.cxx @@ -18,16 +20,20 @@ o2_add_library(GlobalTrackingWorkflow src/PrimaryVertexWriterSpec.cxx src/VertexTrackMatcherSpec.cxx src/SecondaryVertexingSpec.cxx + src/StrangenessTrackingSpec.cxx src/GlobalFwdMatchingSpec.cxx src/SecondaryVertexWriterSpec.cxx + src/StrangenessTrackingWriterSpec.cxx src/CosmicsMatchingSpec.cxx src/TrackCosmicsWriterSpec.cxx src/TOFMatcherSpec.cxx src/TOFMatchChecker.cxx + src/HMPMatcherSpec.cxx src/TOFEventTimeChecker.cxx src/GlobalFwdTrackWriterSpec.cxx src/GlobalFwdMatchingAssessmentSpec.cxx src/MatchedMFTMCHWriterSpec.cxx + src/ReaderDriverSpec.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTracking O2::GlobalTrackingWorkflowReaders O2::GlobalTrackingWorkflowHelpers @@ -36,11 +42,19 @@ o2_add_library(GlobalTrackingWorkflow O2::ITSWorkflow O2::MFTTracking O2::MFTWorkflow + O2::HMPIDWorkflow O2::TPCWorkflow O2::FT0Workflow O2::ITSMFTWorkflow O2::SimulationDataFormat - O2::DetectorsVertexing) + O2::DetectorsVertexing + O2::StrangenessTracking + $<$:O2::ITS3Reconstruction>) + +o2_add_executable(driver-workflow + COMPONENT_NAME reader + SOURCES src/reader-driver-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow ) o2_add_executable(match-workflow COMPONENT_NAME tpcits @@ -72,6 +86,10 @@ o2_add_executable(vertexing-workflow SOURCES src/secondary-vertexing-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow O2::TOFWorkflowIO) +o2_add_executable(strangeness-tracking-workflow + SOURCES src/strangeness-tracking-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow) + o2_add_executable(matcher-workflow COMPONENT_NAME tof SOURCES src/tof-matcher-workflow.cxx @@ -85,6 +103,11 @@ o2_add_executable(match-eventtime-workflow SOURCES src/tof-eventtime-checker-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow O2::TOFWorkflowIO) +o2_add_executable(matcher-workflow + COMPONENT_NAME hmpid + SOURCES src/hmp-matcher-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow O2::HMPIDWorkflow) + o2_add_executable(matcher-workflow COMPONENT_NAME globalfwd SOURCES src/globalfwd-matcher-workflow.cxx diff --git a/Detectors/GlobalTrackingWorkflow/README.md b/Detectors/GlobalTrackingWorkflow/README.md index 2042f89a582ed..95693115d932d 100644 --- a/Detectors/GlobalTrackingWorkflow/README.md +++ b/Detectors/GlobalTrackingWorkflow/README.md @@ -28,4 +28,23 @@ o2-cosmics-match-workflow --shm-segment-size 10000000000 --run | tee cosmics.log One can account contributions of a limited set of track sources (currently by default: ITS, TPC, ITS-TPC, TPC-TOF, ITS-TPC-TOF) by providing optiont `--track-sources`. +## Using TF throttling when reading root files from detectors processing (tracks, clusters etc.) +The workflows driven by the input from the root files produced by detectors (e.g. by the `o2-global-track-cluster-reader`), can be preceded by the +`o2-reader-driver-workflow` which will allow to have TF throttling via usual `--timeframes-rate-limit ` and `--timeframes-rate-limit-ipcid ` +options. +The `o2-reader-driver-workflow` as well as all reader workflows must be provided with the `--hbfutils-config ,upstream` option, with being the file produced by the +`o2-tfidinfo-writer-workflow` (usually o2_tfidinfo.root file). If this file is in the working directory, then the `--hbfutils-config` option can be shortened to `upstream` only. + +The `o2-reader-driver-workflow` is not supported for `o2simdigitizerworkflow_configuration.ini` version of the `--hbfutils-config`, since it is used only for MC, +where by construction there is only 1 TF, hence the throttling is meaningless. + +Option `--max-tf ` of the `o2-reader-driver-workflow` allows to inject only 1st TFs by the dowsntream readers. + +A typical invocation of the throttled workflow is: + +``` +GLOSET=" --shm-segment-size 24000000000 --timeframes-rate-limit 2 --timeframes-rate-limit-ipcid 0" +HBFSET=" --hbfutils-config upstream,o2_tfidinfo.root " +o2-reader-driver-workflow $GLOSET $HBFSET --max-tf 3 | o2-global-track-cluster-reader $GLOSET $HBFSET --disable-mc --track-types <...> --cluster-types <...> | [ $GLOSET $HBFSET ] | $GLOSET --run +``` diff --git a/Detectors/GlobalTrackingWorkflow/helpers/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/helpers/CMakeLists.txt index 9f65236f4cd2b..357f16bb0c21b 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/helpers/CMakeLists.txt @@ -24,6 +24,7 @@ o2_add_library(GlobalTrackingWorkflowHelpers O2::GlobalTrackingWorkflowReaders O2::TOFWorkflowIO O2::FT0Workflow + O2::HMPIDWorkflow O2::FV0Workflow O2::FDDWorkflow O2::ZDCWorkflow @@ -40,4 +41,4 @@ o2_add_executable(track-cluster-reader COMPONENT_NAME global TARGETVARNAME targetName SOURCES src/GlobalTrackClusterReader.cxx - PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflowHelpers O2::DetectorsRaw) + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflowHelpers O2::DetectorsRaw O2::DataFormatsITSMFT) diff --git a/Detectors/GlobalTrackingWorkflow/helpers/include/GlobalTrackingWorkflowHelpers/InputHelper.h b/Detectors/GlobalTrackingWorkflow/helpers/include/GlobalTrackingWorkflowHelpers/InputHelper.h index 2db4b9a2b4f71..39323ba95012e 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/include/GlobalTrackingWorkflowHelpers/InputHelper.h +++ b/Detectors/GlobalTrackingWorkflow/helpers/include/GlobalTrackingWorkflowHelpers/InputHelper.h @@ -38,6 +38,7 @@ class InputHelper bool subSpecStrict = false); static int addInputSpecsPVertex(const o2::framework::ConfigContext& configcontext, o2::framework::WorkflowSpec& specs, bool mc); static int addInputSpecsSVertex(const o2::framework::ConfigContext& configcontext, o2::framework::WorkflowSpec& specs); + static int addInputSpecsStrangeTrack(const o2::framework::ConfigContext& configcontext, o2::framework::WorkflowSpec& specs, bool mc); static int addInputSpecsCosmics(const o2::framework::ConfigContext& configcontext, o2::framework::WorkflowSpec& specs, bool mc); static int addInputSpecsIRFramesITS(const o2::framework::ConfigContext& configcontext, o2::framework::WorkflowSpec& specs); }; diff --git a/Detectors/GlobalTrackingWorkflow/helpers/src/GlobalTrackClusterReader.cxx b/Detectors/GlobalTrackingWorkflow/helpers/src/GlobalTrackClusterReader.cxx index 3fc8df70fdd84..a004432643763 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/src/GlobalTrackClusterReader.cxx +++ b/Detectors/GlobalTrackingWorkflow/helpers/src/GlobalTrackClusterReader.cxx @@ -11,6 +11,7 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -37,6 +38,7 @@ void customize(std::vector& workflowOptions) {"ir-frames-its", VariantType::Bool, false, {"read ITS IR frames"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable reading root files, essentially making this workflow void, but needed for compatibility"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx b/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx index 34148393eb19c..e4c1e40b3a4d3 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx +++ b/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx @@ -14,17 +14,22 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "Framework/ConfigParamRegistry.h" #include "ITSMFTWorkflow/ClusterReaderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "ITSWorkflow/TrackReaderSpec.h" #include "MFTWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/ClusterReaderSpec.h" +#include "TPCReaderWorkflow/TriggerReaderSpec.h" #include "TPCWorkflow/ClusterSharingMapSpec.h" +#include "HMPIDWorkflow/ClustersReaderSpec.h" +#include "HMPIDWorkflow/HMPMatchedReaderSpec.h" #include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" #include "GlobalTrackingWorkflowReaders/GlobalFwdTrackReaderSpec.h" #include "GlobalTrackingWorkflowReaders/MatchedMFTMCHReaderSpec.h" #include "GlobalTrackingWorkflowReaders/MatchedMCHMIDReaderSpec.h" #include "GlobalTrackingWorkflowReaders/PrimaryVertexReaderSpec.h" #include "GlobalTrackingWorkflowReaders/SecondaryVertexReaderSpec.h" +#include "GlobalTrackingWorkflowReaders/StrangenessTrackingReaderSpec.h" #include "GlobalTrackingWorkflowReaders/TrackCosmicsReaderSpec.h" #include "GlobalTrackingWorkflowReaders/IRFrameReaderSpec.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" @@ -39,9 +44,10 @@ #include "MCHIO/TrackReaderSpec.h" #include "MCHIO/ClusterReaderSpec.h" #include "MIDWorkflow/TrackReaderSpec.h" -#include "PHOSWorkflow/ReaderSpec.h" -#include "CPVWorkflow/ReaderSpec.h" +#include "PHOSWorkflow/CellReaderSpec.h" +#include "CPVWorkflow/ClusterReaderSpec.h" #include "EMCALWorkflow/PublisherSpec.h" +// #include "StrangenessTrackingWorkflow/StrangenessTrackingReaderSpec.h" using namespace o2::framework; using namespace o2::globaltracking; @@ -64,7 +70,7 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& maskTracksMC = GID::getSourcesMask(GID::NONE); } else { // some detectors do not support MC labels - if (maskClustersMC[GID::MCH]) { + if (maskClusters[GID::MCH] && maskClustersMC[GID::MCH]) { LOG(warn) << "MCH global clusters do not support MC lables, disabling"; maskClustersMC &= ~GID::getSourceMask(GID::MCH); } @@ -74,13 +80,15 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& specs.emplace_back(o2::its::getITSTrackReaderSpec(maskTracksMC[GID::ITS])); } if (maskClusters[GID::ITS]) { - specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(maskClustersMC[GID::ITS], true)); + bool doStag = itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(maskClustersMC[GID::ITS], doStag, true)); } if (maskTracks[GID::MFT]) { specs.emplace_back(o2::mft::getMFTTrackReaderSpec(maskTracksMC[GID::MFT])); } if (maskClusters[GID::MFT]) { - specs.emplace_back(o2::itsmft::getMFTClusterReaderSpec(maskClustersMC[GID::MFT], true)); + bool doStag = itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); + specs.emplace_back(o2::itsmft::getMFTClusterReaderSpec(maskClustersMC[GID::MFT], doStag, true)); } if (maskTracks[GID::MCH] || maskMatches[GID::MCHMID]) { specs.emplace_back(o2::mch::getTrackReaderSpec(maskTracksMC[GID::MCH] || maskTracksMC[GID::MCHMID])); @@ -93,6 +101,9 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& } if (maskClusters[GID::TPC]) { specs.emplace_back(o2::tpc::getClusterReaderSpec(maskClustersMC[GID::TPC])); + if (!getenv("DPL_DISABLE_TPC_TRIGGER_READER") || atoi(getenv("DPL_DISABLE_TPC_TRIGGER_READER")) != 1) { + specs.emplace_back(o2::tpc::getTPCTriggerReaderSpec()); + } } if (maskTracks[GID::TPC] && maskClusters[GID::TPC]) { specs.emplace_back(o2::tpc::getClusterSharingMapSpec()); @@ -120,9 +131,15 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& maskMatches[GID::ITSTPCTOF] || maskMatches[GID::ITSTPCTRDTOF] || maskMatches[GID::TPCTRDTOF]) { specs.emplace_back(o2::tof::getClusterReaderSpec(maskClustersMC[GID::TOF])); } + if (maskClusters[GID::HMP]) { + specs.emplace_back(o2::hmpid::getClusterReaderSpec()); + } if (maskMatches[GID::TPCTOF] || maskTracks[GID::TPCTOF]) { specs.emplace_back(o2::tof::getTOFMatchedReaderSpec(maskTracksMC[GID::TPCTOF], 0, maskTracks[GID::TPCTOF], subSpecStrict)); } + if (maskMatches[GID::HMP]) { + specs.emplace_back(o2::hmpid::getHMPMatchedReaderSpec(maskTracksMC[GID::HMP])); + } if (maskTracks[GID::FT0] || maskClusters[GID::FT0]) { specs.emplace_back(o2::ft0::getRecPointReaderSpec(maskTracksMC[GID::FT0] || maskClustersMC[GID::FT0])); } @@ -150,11 +167,11 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& } if (maskTracks[GID::PHS] || maskClusters[GID::PHS]) { - specs.emplace_back(o2::phos::getCellReaderSpec(maskTracksMC[GID::PHS] || maskClustersMC[GID::PHS])); + specs.emplace_back(o2::phos::getPHOSCellReaderSpec(maskTracksMC[GID::PHS] || maskClustersMC[GID::PHS])); } if (maskTracks[GID::CPV] || maskClusters[GID::CPV]) { - specs.emplace_back(o2::cpv::getClustersReaderSpec(maskTracksMC[GID::CPV] || maskClustersMC[GID::CPV])); + specs.emplace_back(o2::cpv::getCPVClusterReaderSpec(maskTracksMC[GID::CPV] || maskClustersMC[GID::CPV])); } if (maskTracks[GID::EMC] || maskClusters[GID::EMC]) { @@ -162,7 +179,7 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& } if (maskClusters[GID::MCH]) { - specs.emplace_back(o2::mch::getClusterReaderSpec(maskClustersMC[GID::MCH], "mch-global-cluster-reader", true, false)); + specs.emplace_back(o2::mch::getClusterReaderSpec(maskClustersMC[GID::MCH], "mch-cluster-reader", true, true)); } return 0; @@ -188,6 +205,16 @@ int InputHelper::addInputSpecsSVertex(const o2::framework::ConfigContext& config return 0; } +// attach strangeness tracking reader +int InputHelper::addInputSpecsStrangeTrack(const o2::framework::ConfigContext& configcontext, o2::framework::WorkflowSpec& specs, bool mc) +{ + if (configcontext.options().get("disable-root-input")) { + return 0; + } + specs.emplace_back(o2::strangeness_tracking::getStrangenessTrackingReaderSpec(mc)); + return 0; +} + // attach cosmic tracks reader int InputHelper::addInputSpecsCosmics(const o2::framework::ConfigContext& configcontext, o2::framework::WorkflowSpec& specs, bool mc) { diff --git a/Detectors/GlobalTrackingWorkflow/helpers/src/NoInpDummyOutSpec.cxx b/Detectors/GlobalTrackingWorkflow/helpers/src/NoInpDummyOutSpec.cxx index 34f445ed003da..17ad5f09027e1 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/src/NoInpDummyOutSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/helpers/src/NoInpDummyOutSpec.cxx @@ -47,7 +47,7 @@ void NoInpDummyOut::run(ProcessingContext& pc) { static int counter = 0; // send just once dummy output to trigger the ccdb-fetcher - pc.outputs().make>(Output{"GLO", "DUMMY_OUT", 0, Lifetime::Timeframe}); + pc.outputs().make>(Output{"GLO", "DUMMY_OUT", 0}); if (mLoops >= 0 && ++counter >= mLoops) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(framework::QuitRequest::Me); diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h index 25553c5d56d33..ed1b0aa1c105d 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h @@ -24,7 +24,7 @@ namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getCosmicsMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC); +framework::DataProcessorSpec getCosmicsMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool usePV, bool useMC); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/GlobalFwdMatchingAssessmentSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/GlobalFwdMatchingAssessmentSpec.h index d6803952c5f97..b1b9bf727c876 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/GlobalFwdMatchingAssessmentSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/GlobalFwdMatchingAssessmentSpec.h @@ -17,6 +17,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "GlobalTracking/MatchGlobalFwdAssessment.h" +#include "DetectorsBase/GRPGeomHelper.h" #include "TStopwatch.h" using namespace o2::framework; @@ -28,17 +29,21 @@ namespace globaltracking class GlobalFwdAssessmentSpec : public Task { public: - GlobalFwdAssessmentSpec(bool useMC, bool processGen, bool midFilterDisabled, bool finalizeAnalysis = false) : mUseMC(useMC), - mMIDFilterDisabled(midFilterDisabled), - mProcessGen(processGen), - mFinalizeAnalysis(finalizeAnalysis){}; + GlobalFwdAssessmentSpec(bool useMC, bool processGen, std::shared_ptr gr, bool midFilterDisabled, bool finalizeAnalysis = false) + : mUseMC(useMC), + mMIDFilterDisabled(midFilterDisabled), + mProcessGen(processGen), + mGGCCDBRequest(gr), + mFinalizeAnalysis(finalizeAnalysis){}; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj); private: void sendOutput(DataAllocator& output); std::unique_ptr mGloFwdAssessment; + std::shared_ptr mGGCCDBRequest; bool mUseMC = true; bool mProcessGen = false; bool mFinalizeAnalysis = false; @@ -64,4 +69,4 @@ DataProcessorSpec getGlobaFwdAssessmentSpec(bool useMC, bool processGen, bool mi } // namespace globaltracking } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/HMPMatcherSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/HMPMatcherSpec.h new file mode 100644 index 0000000000000..ca515b22859a9 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/HMPMatcherSpec.h @@ -0,0 +1,33 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file HMPMatcherSpec.h // ef ; change to hmp + +#ifndef O2_HMP_MATCHER_SPEC // hmp +#define O2_HMP_MATCHER_SPEC // + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace globaltracking +{ + +/// create a processor spec +framework::DataProcessorSpec getHMPMatcherSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, float extratolerancetrd, float extratolerancetof); + +} // namespace globaltracking +} // namespace o2 + +#endif /* O2_HMP_MATCHER_SPEC */ diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h index da56fdfe20a86..d5f5a8b2a0f06 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h @@ -24,7 +24,7 @@ namespace vertexing { /// create a processor spec -o2::framework::DataProcessorSpec getPrimaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool skip, bool validateWithFT0, bool useMC); +o2::framework::DataProcessorSpec getPrimaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool skip, bool validateWithFT0, bool useMC, bool useGeom); } // namespace vertexing } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/ReaderDriverSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/ReaderDriverSpec.h new file mode 100644 index 0000000000000..3cbf7e5d8f4fe --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/ReaderDriverSpec.h @@ -0,0 +1,32 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ReaderDriverSpec.h + +#ifndef O2_READER_DRIVER_ +#define O2_READER_DRIVER_ + +#include "Framework/DataProcessorSpec.h" +#include + +namespace o2 +{ +namespace globaltracking +{ + +/// create a processor spec +/// pushes an empty output to provide timing to downstream devices +framework::DataProcessorSpec getReaderDriverSpec(const std::string& metricChannel = "", size_t minSHM = 0); + +} // namespace globaltracking +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h index 3474b46bd11b4..9211a9cb0c264 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h @@ -20,11 +20,12 @@ namespace o2 { + namespace vertexing { /// create a processor spec -o2::framework::DataProcessorSpec getSecondaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool enableCasc, bool enable3body); +o2::framework::DataProcessorSpec getSecondaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool enableCCDBParams, bool useMC, bool useGeom); } // namespace vertexing } // namespace o2 diff --git a/Detectors/StrangenessTracking/workflow/include/StrangenessTrackingWorkflow/StrangenessTrackingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingSpec.h similarity index 93% rename from Detectors/StrangenessTracking/workflow/include/StrangenessTrackingWorkflow/StrangenessTrackingSpec.h rename to Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingSpec.h index 444248c3c65c6..bbc2cdc80995e 100644 --- a/Detectors/StrangenessTracking/workflow/include/StrangenessTrackingWorkflow/StrangenessTrackingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingSpec.h @@ -50,8 +50,9 @@ class StrangenessTrackerSpec : public framework::Task private: void updateTimeDependentParams(framework::ProcessingContext& pc); + void storeConfigs(framework::ProcessingContext& pc); - bool mIsMC = false; + bool mUseMC = false; TStopwatch mTimer; StrangenessTracker mTracker; std::shared_ptr mGGCCDBRequest; @@ -59,9 +60,9 @@ class StrangenessTrackerSpec : public framework::Task std::unique_ptr mGRP = nullptr; }; -o2::framework::DataProcessorSpec getStrangenessTrackerSpec(o2::dataformats::GlobalTrackID::mask_t src); +o2::framework::DataProcessorSpec getStrangenessTrackerSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useGeom); o2::framework::WorkflowSpec getWorkflow(bool upstreamClusters = false, bool upstreamV0s = false); } // namespace strangeness_tracking } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/StrangenessTracking/workflow/include/StrangenessTrackingWorkflow/StrangenessTrackingWriterSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingWriterSpec.h similarity index 98% rename from Detectors/StrangenessTracking/workflow/include/StrangenessTrackingWorkflow/StrangenessTrackingWriterSpec.h rename to Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingWriterSpec.h index 2cd3262370eb5..6924b8b5c8c30 100644 --- a/Detectors/StrangenessTracking/workflow/include/StrangenessTrackingWorkflow/StrangenessTrackingWriterSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingWriterSpec.h @@ -25,7 +25,7 @@ namespace strangeness_tracking /// create a processor spec /// write ITS tracks to ROOT file -o2::framework::DataProcessorSpec getStrangenessTrackingWriterSpec(); +o2::framework::DataProcessorSpec getStrangenessTrackingWriterSpec(bool useMC); } // namespace strangeness_tracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h index 46f58b088abd8..a1102af6a1f75 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h @@ -25,7 +25,7 @@ namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getTOFMatcherSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd = 0., bool pushMatchable = 0); +framework::DataProcessorSpec getTOFMatcherSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, bool requestCTPLumi, int nlanes = 1); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h index 7d8839e89ba2b..56240fd2c8f98 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h @@ -23,7 +23,7 @@ namespace o2 namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getTPCITSMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useMC); +framework::DataProcessorSpec getTPCITSMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC, bool requestCTPLumi); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/qc/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/qc/CMakeLists.txt index 4a72fb8cede77..cca69282d20cf 100644 --- a/Detectors/GlobalTrackingWorkflow/qc/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/qc/CMakeLists.txt @@ -14,9 +14,10 @@ o2_add_library(GlobalTrackingWorkflowQC PUBLIC_LINK_LIBRARIES O2::DataFormatsGlobalTracking O2::GlobalTracking ROOT::Core + O2::GLOQC ) -o2_add_executable(match-qc +o2_add_executable(matching-qc COMPONENT_NAME itstpc SOURCES src/itstpc-matching-qc-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflowQC diff --git a/Detectors/GlobalTrackingWorkflow/qc/include/GlobalTrackingWorkflowQC/ITSTPCMatchingQCSpec.h b/Detectors/GlobalTrackingWorkflow/qc/include/GlobalTrackingWorkflowQC/ITSTPCMatchingQCSpec.h index 0d8a01bbad253..6ffd38c8a1c1b 100644 --- a/Detectors/GlobalTrackingWorkflow/qc/include/GlobalTrackingWorkflowQC/ITSTPCMatchingQCSpec.h +++ b/Detectors/GlobalTrackingWorkflow/qc/include/GlobalTrackingWorkflowQC/ITSTPCMatchingQCSpec.h @@ -17,7 +17,8 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" -#include "GlobalTracking/MatchITSTPCQC.h" +#include "GLOQC/MatchITSTPCQC.h" +#include "DetectorsBase/GRPGeomHelper.h" using namespace o2::framework; @@ -28,23 +29,27 @@ namespace globaltracking class ITSTPCMatchingQCDevice : public Task { public: - ITSTPCMatchingQCDevice(std::shared_ptr dr, bool useMC) : mDataRequest(dr), mUseMC(useMC){}; + ITSTPCMatchingQCDevice(std::shared_ptr dr, std::shared_ptr req, bool useMC, bool doK0QC, std::string trkSources) : mDataRequest(dr), mCCDBRequest(req), mUseMC(useMC), mDoK0QC(doK0QC), mTrkSources(trkSources){}; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; private: void sendOutput(DataAllocator& output); - std::unique_ptr mMatchITSTPCQC; + std::unique_ptr mMatchITSTPCQC; std::shared_ptr mDataRequest; + std::shared_ptr mCCDBRequest; bool mUseMC = true; + bool mDoK0QC = true; + std::string mTrkSources = "ITS,TPC,ITS-TPC"; }; } // namespace globaltracking namespace framework { -DataProcessorSpec getITSTPCMatchingQCDevice(bool useMC); +DataProcessorSpec getITSTPCMatchingQCDevice(bool useMC, bool doK0QC, std::string trkSources); } // namespace framework } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx b/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx index 68f1bd39b3d38..db61300c4cf60 100644 --- a/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx @@ -15,8 +15,11 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/Logger.h" #include "GlobalTrackingWorkflowQC/ITSTPCMatchingQCSpec.h" -#include "GlobalTracking/ITSTPCMatchingQCParams.h" +#include "GLOQC/ITSTPCMatchingQCParams.h" #include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsVertexing/SVertexerParams.h" +#include "Framework/CCDBParamSpec.h" #include "CommonUtils/NameConf.h" #include @@ -27,30 +30,57 @@ namespace o2 namespace globaltracking { -void ITSTPCMatchingQCDevice::init(InitContext& ic) +void ITSTPCMatchingQCDevice::init(InitContext& /*ic*/) { - const o2::globaltracking::ITSTPCMatchingQCParams* params = &o2::globaltracking::ITSTPCMatchingQCParams::Instance(); + const o2::gloqc::ITSTPCMatchingQCParams& params = o2::gloqc::ITSTPCMatchingQCParams::Instance(); - mMatchITSTPCQC = std::make_unique(); - mMatchITSTPCQC->init(); + mMatchITSTPCQC = std::make_unique(); mMatchITSTPCQC->setDataRequest(mDataRequest); - mMatchITSTPCQC->setPtCut(params->minPtCut); - mMatchITSTPCQC->setEtaCut(params->etaCut); - mMatchITSTPCQC->setMinNTPCClustersCut(params->minNTPCClustersCut); - mMatchITSTPCQC->setMinDCAtoBeamPipeDistanceCut(params->minDCACut); - mMatchITSTPCQC->setMinDCAtoBeamPipeYCut(params->minDCACutY); + mMatchITSTPCQC->setTrkSources(o2::dataformats::GlobalTrackID::getSourcesMask(mTrkSources)); + mMatchITSTPCQC->setMinPtITSCut(params.minPtITSCut); + mMatchITSTPCQC->setEtaITSCut(params.etaITSCut); + mMatchITSTPCQC->setMinNClustersITS(params.minNITSClustersCut); + mMatchITSTPCQC->setMaxChi2PerClusterITS(params.maxChi2PerClusterITS); + mMatchITSTPCQC->setMinPtTPCCut(params.minPtTPCCut); + mMatchITSTPCQC->setEtaTPCCut(params.etaTPCCut); + mMatchITSTPCQC->setMinNTPCClustersCut(params.minNTPCClustersCut); + mMatchITSTPCQC->setMinDCAtoBeamPipeDistanceCut(params.minDCACut); + mMatchITSTPCQC->setMinDCAtoBeamPipeYCut(params.minDCACutY); + mMatchITSTPCQC->setPtCut(params.minPtCut); + mMatchITSTPCQC->setMaxPtCut(params.maxPtCut); + mMatchITSTPCQC->setEtaCut(params.etaCut); + mMatchITSTPCQC->setEtaNo0Cut(params.etaNo0Cut); + mMatchITSTPCQC->setCutK0Mass(params.cutK0Mass); + mMatchITSTPCQC->setMaxK0Eta(params.maxEtaK0); + mMatchITSTPCQC->setK0Scaling(params.K0Scaling); + mMatchITSTPCQC->setMinTPCOccpp(params.minTPCOccpp); + mMatchITSTPCQC->setMaxTPCOccpp(params.maxTPCOccpp); + mMatchITSTPCQC->setNBinsTPCOccpp(params.nBinsTPCOccpp); + mMatchITSTPCQC->setMinTPCOccPbPb(params.minTPCOccPbPb); + mMatchITSTPCQC->setMaxTPCOccPbPb(params.maxTPCOccPbPb); + mMatchITSTPCQC->setNBinsTPCOccPbPb(params.nBinsTPCOccPbPb); + mMatchITSTPCQC->setK0MaxDCA(params.maxK0DCA); + mMatchITSTPCQC->setK0MinCosPA(params.minK0CosPA); + o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); if (mUseMC) { mMatchITSTPCQC->setUseMC(mUseMC); } - mMatchITSTPCQC->setGRPFileName(o2::base::NameConf::getGRPFileName()); - mMatchITSTPCQC->setGeomFileName(o2::base::NameConf::getGeomFileName()); + if (mDoK0QC) { + mMatchITSTPCQC->setDoK0QC(mDoK0QC); + } + mMatchITSTPCQC->init(); } //_____________________________________________________________ void ITSTPCMatchingQCDevice::run(o2::framework::ProcessingContext& pc) { - + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool wasSVParamInitialized = false; + if (!wasSVParamInitialized) { + pc.inputs().get("SVParam"); + wasSVParamInitialized = true; + } mMatchITSTPCQC->run(pc); } @@ -70,32 +100,60 @@ void ITSTPCMatchingQCDevice::sendOutput(DataAllocator& output) TObjArray objar; mMatchITSTPCQC->getHistos(objar); - output.snapshot(Output{"GLO", "ITSTPCMATCHQC", 0, Lifetime::Sporadic}, objar); + output.snapshot(Output{"GLO", "ITSTPCMATCHQC", 0}, objar); TFile* f = new TFile(Form("outITSTPCmatchingQC.root"), "RECREATE"); + if (f == nullptr) { + LOGP(error, "Cannot write QC to file 'outITSTPCmatchingQC.root'"); + return; + } objar.Write("ObjArray", TObject::kSingleKey); f->Close(); } + +void ITSTPCMatchingQCDevice::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("GLO", "SVPARAM", 0)) { + return; + } +} } // namespace globaltracking namespace framework { using GID = o2::dataformats::GlobalTrackID; -DataProcessorSpec getITSTPCMatchingQCDevice(bool useMC) +DataProcessorSpec getITSTPCMatchingQCDevice(bool useMC, bool doK0QC, std::string trkSources) { std::vector outputs; outputs.emplace_back("GLO", "ITSTPCMATCHQC", 0, Lifetime::Sporadic); auto dataRequest = std::make_shared(); - GID::mask_t mSrc = GID::getSourcesMask("TPC,ITS-TPC"); - dataRequest->requestTracks(mSrc, useMC); + GID::mask_t srcMask = GID::getSourcesMask(trkSources); + dataRequest->requestTracks(srcMask, useMC); + if (doK0QC) { + dataRequest->requestPrimaryVertices(useMC); + dataRequest->requestSecondaryVertices(useMC); + dataRequest->requestTPCClusters(false); + } + auto ccdbRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + dataRequest->inputs); + + dataRequest->inputs.emplace_back("SVParam", "GLO", "SVPARAM", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/SVertexerParam")); return DataProcessorSpec{ - "itstpc-matching-qc", - dataRequest->inputs, - outputs, - AlgorithmSpec{adaptFromTask(dataRequest, useMC)}, - Options{{}}}; + .name = "itstpc-matching-qc", + .inputs = dataRequest->inputs, + .outputs = outputs, + .algorithm = AlgorithmSpec{adaptFromTask(dataRequest, ccdbRequest, useMC, doK0QC, trkSources)}, + }; } } // namespace framework diff --git a/Detectors/GlobalTrackingWorkflow/qc/src/itstpc-matching-qc-workflow.cxx b/Detectors/GlobalTrackingWorkflow/qc/src/itstpc-matching-qc-workflow.cxx index 16bd2d78d77e2..e3107bedbf1d4 100644 --- a/Detectors/GlobalTrackingWorkflow/qc/src/itstpc-matching-qc-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/qc/src/itstpc-matching-qc-workflow.cxx @@ -21,6 +21,8 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ {"disable-mc", o2::framework::VariantType::Bool, false, {"disable use of MC information even if available"}}, + {"disable-k0-qc", o2::framework::VariantType::Bool, false, {"disable K0 QC"}}, + {"track-sources", o2::framework::VariantType::String, "ITS,TPC,ITS-TPC", {"comma-separated list of track sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; std::swap(workflowOptions, options); } @@ -33,12 +35,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - // write the configuration used for the workflow - o2::conf::ConfigurableParam::writeINI("o2-itstpc-matching-qc.ini"); LOG(info) << "ITSTPC matching QC: disable-mc = " << configcontext.options().get("disable-mc"); auto useMC = !configcontext.options().get("disable-mc"); + LOG(info) << "ITSTPC matching QC: disable-k0-qc = " << configcontext.options().get("disable-k0-qc"); + auto doK0QC = !configcontext.options().get("disable-k0-qc"); + LOG(info) << "ITSTPC matching QC: track-sources = " << configcontext.options().get("track-sources"); + std::string trkSources = configcontext.options().get("track-sources"); WorkflowSpec specs; - specs.emplace_back(getITSTPCMatchingQCDevice(useMC)); + specs.emplace_back(getITSTPCMatchingQCDevice(useMC, doK0QC, trkSources)); return specs; } diff --git a/Detectors/GlobalTrackingWorkflow/readers/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/readers/CMakeLists.txt index dec98d82eb156..2db7019f67756 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/readers/CMakeLists.txt @@ -13,6 +13,7 @@ o2_add_library(GlobalTrackingWorkflowReaders SOURCES src/TrackCosmicsReaderSpec.cxx src/SecondaryVertexReaderSpec.cxx src/PrimaryVertexReaderSpec.cxx + src/StrangenessTrackingReaderSpec.cxx src/TrackTPCITSReaderSpec.cxx src/GlobalFwdTrackReaderSpec.cxx src/MatchedMFTMCHReaderSpec.cxx diff --git a/Detectors/GlobalTrackingWorkflow/readers/include/GlobalTrackingWorkflowReaders/StrangenessTrackingReaderSpec.h b/Detectors/GlobalTrackingWorkflow/readers/include/GlobalTrackingWorkflowReaders/StrangenessTrackingReaderSpec.h new file mode 100644 index 0000000000000..c93b719fe8ce2 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/readers/include/GlobalTrackingWorkflowReaders/StrangenessTrackingReaderSpec.h @@ -0,0 +1,32 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file StrangenessTrackingReaderSpec.h + +#ifndef O2_STRANGENESS_TRACKING_READERSPEC +#define O2_STRANGENESS_TRACKING_READERSPEC + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" + +namespace o2 +{ +namespace strangeness_tracking +{ + +/// create a processor spec +/// read secondary vertex data from a root file +o2::framework::DataProcessorSpec getStrangenessTrackingReaderSpec(bool useMC); + +} // namespace strangeness_tracking +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx index 1fdc7b49616c3..11fa58333f89b 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx @@ -65,9 +65,9 @@ void GlobalFwdTrackReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " Global Forward tracks at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "GLFWD", 0, Lifetime::Timeframe}, mTracks); + pc.outputs().snapshot(Output{"GLO", "GLFWD", 0}, mTracks); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "GLFWD_MC", 0, Lifetime::Timeframe}, mLabels); + pc.outputs().snapshot(Output{"GLO", "GLFWD_MC", 0}, mLabels); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx index 012542e448d19..c1810a1deb743 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx @@ -63,7 +63,7 @@ void IRFrameReaderSpec::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); LOG(debug) << "Pushing " << mIRF.size() << " IR-frames in at entry " << ent; - pc.outputs().snapshot(Output{mDataOrigin, "IRFRAMES", mSubSpec, Lifetime::Timeframe}, mIRF); + pc.outputs().snapshot(Output{mDataOrigin, "IRFRAMES", mSubSpec}, mIRF); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx index 08c5c5871db84..5f02beebd1746 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx @@ -65,7 +65,7 @@ void MatchMFTMCHReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " MFTMCH matches at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "MTC_MFTMCH", 0, Lifetime::Timeframe}, mTracks); + pc.outputs().snapshot(Output{"GLO", "MTC_MFTMCH", 0}, mTracks); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx index 06bdf8ff95a45..6e1aba8b2e1f3 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx @@ -84,12 +84,12 @@ void PrimaryVertexReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "Pushing " << mVerticesPtr->size() << " vertices at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "PVTX", 0, Lifetime::Timeframe}, mVertices); - pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe}, mPV2MatchIdx); - pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTCREFS", 0, Lifetime::Timeframe}, mPV2MatchIdxRef); + pc.outputs().snapshot(Output{"GLO", "PVTX", 0}, mVertices); + pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTC", 0}, mPV2MatchIdx); + pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTCREFS", 0}, mPV2MatchIdxRef); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "PVTX_MCTR", 0, Lifetime::Timeframe}, mLabels); + pc.outputs().snapshot(Output{"GLO", "PVTX_MCTR", 0}, mLabels); } if (mVerbose) { diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx index 0f01618681035..9f252616c9d55 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx @@ -21,7 +21,7 @@ #include "CommonDataFormat/RangeReference.h" #include "ReconstructionDataFormats/V0.h" #include "ReconstructionDataFormats/Cascade.h" -#include "ReconstructionDataFormats/DecayNbody.h" +#include "ReconstructionDataFormats/Decay3Body.h" #include "TFile.h" #include "TTree.h" @@ -35,9 +35,12 @@ namespace vertexing class SecondaryVertexReader : public o2::framework::Task { using RRef = o2::dataformats::RangeReference; + using V0Index = o2::dataformats::V0Index; using V0 = o2::dataformats::V0; + using CascadeIndex = o2::dataformats::CascadeIndex; using Cascade = o2::dataformats::Cascade; - using DecayNbody = o2::dataformats::DecayNbody; + using Decay3BodyIndex = o2::dataformats::Decay3BodyIndex; + using Decay3Body = o2::dataformats::Decay3Body; public: SecondaryVertexReader() = default; @@ -50,11 +53,14 @@ class SecondaryVertexReader : public o2::framework::Task bool mVerbose = false; + std::vector mV0sIdx, *mV0sIdxPtr = &mV0sIdx; std::vector mV0s, *mV0sPtr = &mV0s; std::vector mPV2V0Ref, *mPV2V0RefPtr = &mPV2V0Ref; + std::vector mCascsIdx, *mCascsIdxPtr = &mCascsIdx; std::vector mCascs, *mCascsPtr = &mCascs; std::vector mPV2CascRef, *mPV2CascRefPtr = &mPV2CascRef; - std::vector m3Bodys, *m3BodysPtr = &m3Bodys; + std::vector m3BodysIdx, *m3BodysIdxPtr = &m3BodysIdx; + std::vector m3Bodys, *m3BodysPtr = &m3Bodys; std::vector mPV23BodyRef, *mPV23BodyRefPtr = &mPV23BodyRef; std::unique_ptr mFile; @@ -62,10 +68,13 @@ class SecondaryVertexReader : public o2::framework::Task std::string mFileName = ""; std::string mFileNameMatches = ""; std::string mSVertexTreeName = "o2sim"; + std::string mV0IdxBranchName = "V0sID"; std::string mV0BranchName = "V0s"; std::string mPVertex2V0RefBranchName = "PV2V0Refs"; + std::string mCascIdxBranchName = "CascadesID"; std::string mCascBranchName = "Cascades"; std::string mPVertex2CascRefBranchName = "PV2CascRefs"; + std::string m3BodyIdxBranchName = "Decays3BodyID"; std::string m3BodyBranchName = "Decays3Body"; std::string mPVertex23BodyRefBranchName = "PV23BodyRefs"; }; @@ -82,14 +91,18 @@ void SecondaryVertexReader::run(ProcessingContext& pc) auto ent = mTree->GetReadEntry() + 1; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << "Pushing " << mV0s.size() << " V0s and " << mCascs.size() << " cascades at entry " << ent; - - pc.outputs().snapshot(Output{"GLO", "V0S", 0, Lifetime::Timeframe}, mV0s); - pc.outputs().snapshot(Output{"GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe}, mPV2V0Ref); - pc.outputs().snapshot(Output{"GLO", "CASCS", 0, Lifetime::Timeframe}, mCascs); - pc.outputs().snapshot(Output{"GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe}, mPV2CascRef); - pc.outputs().snapshot(Output{"GLO", "DECAYS3BODY", 0, Lifetime::Timeframe}, m3Bodys); - pc.outputs().snapshot(Output{"GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe}, mPV23BodyRef); + LOGP(info, "Pushing {} V0s ({} indices), {} cascades ({} indices) and {} 3-body ({} indices ) at entry {}", + mV0s.size(), mV0sIdx.size(), mCascs.size(), mCascsIdx.size(), m3Bodys.size(), m3BodysIdx.size(), ent); + + pc.outputs().snapshot(Output{"GLO", "V0S_IDX", 0}, mV0sIdx); + pc.outputs().snapshot(Output{"GLO", "V0S", 0}, mV0s); + pc.outputs().snapshot(Output{"GLO", "PVTX_V0REFS", 0}, mPV2V0Ref); + pc.outputs().snapshot(Output{"GLO", "CASCS_IDX", 0}, mCascsIdx); + pc.outputs().snapshot(Output{"GLO", "CASCS", 0}, mCascs); + pc.outputs().snapshot(Output{"GLO", "PVTX_CASCREFS", 0}, mPV2CascRef); + pc.outputs().snapshot(Output{"GLO", "DECAYS3BODY_IDX", 0}, m3BodysIdx); + pc.outputs().snapshot(Output{"GLO", "DECAYS3BODY", 0}, m3Bodys); + pc.outputs().snapshot(Output{"GLO", "PVTX_3BODYREFS", 0}, mPV23BodyRef); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); @@ -104,17 +117,23 @@ void SecondaryVertexReader::connectTree() assert(mFile && !mFile->IsZombie()); mTree.reset((TTree*)mFile->Get(mSVertexTreeName.c_str())); assert(mTree); + assert(mTree->GetBranch(mV0IdxBranchName.c_str())); assert(mTree->GetBranch(mV0BranchName.c_str())); assert(mTree->GetBranch(mPVertex2V0RefBranchName.c_str())); assert(mTree->GetBranch(mCascBranchName.c_str())); + assert(mTree->GetBranch(mCascIdxBranchName.c_str())); assert(mTree->GetBranch(mPVertex2CascRefBranchName.c_str())); + assert(mTree->GetBranch(m3BodyIdxBranchName.c_str())); assert(mTree->GetBranch(m3BodyBranchName.c_str())); assert(mTree->GetBranch(mPVertex23BodyRefBranchName.c_str())); + mTree->SetBranchAddress(mV0IdxBranchName.c_str(), &mV0sIdxPtr); mTree->SetBranchAddress(mV0BranchName.c_str(), &mV0sPtr); mTree->SetBranchAddress(mPVertex2V0RefBranchName.c_str(), &mPV2V0RefPtr); + mTree->SetBranchAddress(mCascIdxBranchName.c_str(), &mCascsIdxPtr); mTree->SetBranchAddress(mCascBranchName.c_str(), &mCascsPtr); mTree->SetBranchAddress(mPVertex2CascRefBranchName.c_str(), &mPV2CascRefPtr); + mTree->SetBranchAddress(m3BodyIdxBranchName.c_str(), &m3BodysIdxPtr); mTree->SetBranchAddress(m3BodyBranchName.c_str(), &m3BodysPtr); mTree->SetBranchAddress(mPVertex23BodyRefBranchName.c_str(), &mPV23BodyRefPtr); @@ -124,10 +143,13 @@ void SecondaryVertexReader::connectTree() DataProcessorSpec getSecondaryVertexReaderSpec() { std::vector outputs; + outputs.emplace_back("GLO", "V0S_IDX", 0, Lifetime::Timeframe); // found V0s indices outputs.emplace_back("GLO", "V0S", 0, Lifetime::Timeframe); // found V0s outputs.emplace_back("GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe); // prim.vertex -> V0s refs + outputs.emplace_back("GLO", "CASCS_IDX", 0, Lifetime::Timeframe); // found Cascades indices outputs.emplace_back("GLO", "CASCS", 0, Lifetime::Timeframe); // found Cascades outputs.emplace_back("GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe); // prim.vertex -> Cascades refs + outputs.emplace_back("GLO", "DECAYS3BODY_IDX", 0, Lifetime::Timeframe); // found 3 body vertices indices outputs.emplace_back("GLO", "DECAYS3BODY", 0, Lifetime::Timeframe); // found 3 body Decays outputs.emplace_back("GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe); // prim.vertex -> 3 body Decays refs diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx new file mode 100644 index 0000000000000..8c7f87a720925 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx @@ -0,0 +1,131 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file StrangenessTrackingReaderSpec.cxx + +#include "GlobalTrackingWorkflowReaders/StrangenessTrackingReaderSpec.h" + +#include + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "CommonUtils/NameConf.h" +#include "CommonDataFormat/RangeReference.h" +#include "ReconstructionDataFormats/StrangeTrack.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "TFile.h" +#include "TTree.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace strangeness_tracking +{ +// read strangeness tracking candidates +class StrangenessTrackingReader : public o2::framework::Task +{ + // using RRef = o2::dataformats::RangeReference; + using StrangeTrack = dataformats::StrangeTrack; + + public: + StrangenessTrackingReader(bool useMC) : mUseMC(useMC) {} + ~StrangenessTrackingReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(); + + bool mUseMC = false; + bool mVerbose = false; + + std::vector mStrangeTrack, *mStrangeTrackPtr = &mStrangeTrack; + std::vector mStrangeTrackMC, *mStrangeTrackMCPtr = &mStrangeTrackMC; + // std::vector mPV2V0Ref, *mPV2V0RefPtr = &mPV2V0Ref; + + std::unique_ptr mFile; + std::unique_ptr mTree; + std::string mFileName = ""; + std::string mFileNameMatches = ""; + std::string mSTrackingTreeName = "o2sim"; + std::string mStrackBranchName = "StrangeTracks"; + std::string mStrackMCBranchName = "StrangeTrackMCLab"; + // std::string mPVertex2V0RefBranchName = "PV2V0Refs"; +}; + +void StrangenessTrackingReader::init(InitContext& ic) +{ + mFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), + ic.options().get("strange-tracks-infile")); + connectTree(); +} + +void StrangenessTrackingReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "Pushing " << mStrangeTrack.size() << " strange tracks at entry " << ent; + pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS", 0}, mStrangeTrack); + + if (mUseMC) { + LOG(info) << "Pushing " << mStrangeTrackMC.size() << " strange tracks MC labels at entry " << ent; + pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS_MC", 0}, mStrangeTrackMC); + } + + // pc.outputs().snapshot(Output{"GLO", "PVTX_V0REFS", 0}, mPV2V0Ref); + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void StrangenessTrackingReader::connectTree() +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(mFileName.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mSTrackingTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mStrackBranchName.c_str())); + + mTree->SetBranchAddress(mStrackBranchName.c_str(), &mStrangeTrackPtr); + if (mUseMC) { + assert(mTree->GetBranch(mStrackMCBranchName.c_str())); + mTree->SetBranchAddress(mStrackMCBranchName.c_str(), &mStrangeTrackMCPtr); + } + + LOG(info) << "Loaded " << mSTrackingTreeName << " tree from " << mFileName << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getStrangenessTrackingReaderSpec(bool useMC) +{ + std::vector outputs; + outputs.emplace_back("GLO", "STRANGETRACKS", 0, Lifetime::Timeframe); // found strange tracks + if (useMC) { + outputs.emplace_back("GLO", "STRANGETRACKS_MC", 0, Lifetime::Timeframe); // MC labels + } + + return DataProcessorSpec{ + "strangeness-tracking-reader", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask(useMC)}, + Options{ + {"strange-tracks-infile", VariantType::String, "o2_strange_tracks.root", {"Name of the input strange tracks file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}}}; +} + +} // namespace strangeness_tracking +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx index 90db5d08acc58..7e3cdffd84a6d 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx @@ -41,9 +41,9 @@ void TrackCosmicsReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " Cosmic Tracks at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "COSMICTRC", 0, Lifetime::Timeframe}, mTracks); + pc.outputs().snapshot(Output{"GLO", "COSMICTRC", 0}, mTracks); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "COSMICTRC_MC", 0, Lifetime::Timeframe}, mLabels); + pc.outputs().snapshot(Output{"GLO", "COSMICTRC_MC", 0}, mLabels); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx index 8ae55e478603e..c7fd0d543ecf6 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx @@ -68,12 +68,12 @@ void TrackTPCITSReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " TPC-ITS matches at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "TPCITS", 0, Lifetime::Timeframe}, mTracks); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_REFS", 0, Lifetime::Timeframe}, mABTrkClusRefs); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_CLID", 0, Lifetime::Timeframe}, mABTrkClIDs); + pc.outputs().snapshot(Output{"GLO", "TPCITS", 0}, mTracks); + pc.outputs().snapshot(Output{"GLO", "TPCITSAB_REFS", 0}, mABTrkClusRefs); + pc.outputs().snapshot(Output{"GLO", "TPCITSAB_CLID", 0}, mABTrkClIDs); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "TPCITS_MC", 0, Lifetime::Timeframe}, mLabels); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_MC", 0, Lifetime::Timeframe}, mLabelsAB); + pc.outputs().snapshot(Output{"GLO", "TPCITS_MC", 0}, mLabels); + pc.outputs().snapshot(Output{"GLO", "TPCITSAB_MC", 0}, mLabelsAB); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { diff --git a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx index fd66a3adfa3ac..338bb86bd8033 100644 --- a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx @@ -11,14 +11,18 @@ /// @file CosmicsMatchingSpec.cxx +#include +#include #include #include #include "TStopwatch.h" #include "GlobalTracking/MatchCosmics.h" +#include "GlobalTracking/MatchCosmicsParams.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "DataFormatsTPC/Constants.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "Framework/ConfigParamRegistry.h" +#include "Framework/DeviceSpec.h" #include "GlobalTrackingWorkflow/CosmicsMatchingSpec.h" #include "ReconstructionDataFormats/GlobalTrackAccessor.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -40,14 +44,14 @@ #include "Headers/DataHeader.h" #include "CommonDataFormat/InteractionRecord.h" #include "ITSBase/GeometryTGeo.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "Framework/Task.h" #include "Framework/CCDBParamSpec.h" #include "ITSMFTReconstruction/ClustererParam.h" #include "DetectorsBase/GRPGeomHelper.h" #include "TPCCalibration/VDriftHelper.h" -#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCFastTransformPOD.h" using namespace o2::framework; using MCLabelsTr = gsl::span; @@ -62,7 +66,7 @@ namespace globaltracking class CosmicsMatchingSpec : public Task { public: - CosmicsMatchingSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC) {} + CosmicsMatchingSpec(std::shared_ptr dr, std::shared_ptr gr, bool usePV, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mUsePVInfo(usePV), mUseMC(useMC) {} ~CosmicsMatchingSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -71,12 +75,14 @@ class CosmicsMatchingSpec : public Task private: void updateTimeDependentParams(ProcessingContext& pc); + void storeConfigs(ProcessingContext& pc); std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; o2::tpc::VDriftHelper mTPCVDriftHelper{}; - o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; + const o2::gpu::TPCFastTransformPOD* mCorrMap{nullptr}; o2::globaltracking::MatchCosmics mMatching; // matching engine bool mUseMC = true; + bool mUsePVInfo = false; TStopwatch mTimer; }; @@ -87,6 +93,7 @@ void CosmicsMatchingSpec::init(InitContext& ic) o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mMatching.setDebugFlag(ic.options().get("debug-tree-flags")); mMatching.setUseMC(mUseMC); + mMatching.setUsePVInfo(mUsePVInfo); // } @@ -96,20 +103,37 @@ void CosmicsMatchingSpec::run(ProcessingContext& pc) RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions - + storeConfigs(pc); mMatching.process(recoData); - pc.outputs().snapshot(Output{"GLO", "COSMICTRC", 0, Lifetime::Timeframe}, mMatching.getCosmicTracks()); + pc.outputs().snapshot(Output{"GLO", "COSMICTRC", 0}, mMatching.getCosmicTracks()); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "COSMICTRC_MC", 0, Lifetime::Timeframe}, mMatching.getCosmicTracksLbl()); + pc.outputs().snapshot(Output{"GLO", "COSMICTRC_MC", 0}, mMatching.getCosmicTracksLbl()); } mTimer.Stop(); } +void CosmicsMatchingSpec::storeConfigs(ProcessingContext& pc) +{ + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + const auto& conf = MatchCosmicsParams::Instance(); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, conf.getName()), conf.getName()); + TMap md; + md.SetOwnerKeyValue(); + md.Add(new TObjString(conf.getName().c_str()), new TObjString(o2::conf::ConfigurableParam::asJSON(conf.getName()).c_str())); + pc.outputs().snapshot(Output{"META", "COSMICMATCHER", 0}, md); + } + } +} + void CosmicsMatchingSpec::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTPCVDriftHelper.extractCCDBInputs(pc); - o2::tpc::CorrectionMapsLoader::extractCCDBInputs(pc); + auto const& raw = pc.inputs().get("corrMap"); + mCorrMap = &gpu::TPCFastTransformPOD::get(raw); static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; @@ -126,12 +150,7 @@ void CosmicsMatchingSpec::updateTimeDependentParams(ProcessingContext& pc) } mMatching.init(); } - bool updateMaps = false; - if (mTPCCorrMapsLoader.isUpdated()) { - mMatching.setTPCCorrMaps(&mTPCCorrMapsLoader); - mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; - } + mMatching.setTPCCorrMaps(mCorrMap); if (mTPCVDriftHelper.isUpdated()) { LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, @@ -139,10 +158,6 @@ void CosmicsMatchingSpec::updateTimeDependentParams(ProcessingContext& pc) mTPCVDriftHelper.getSourceName()); mMatching.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } @@ -154,9 +169,6 @@ void CosmicsMatchingSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { LOG(info) << "cluster dictionary updated"; mMatching.setITSDict((const o2::itsmft::TopologyDictionary*)obj); @@ -171,13 +183,20 @@ void CosmicsMatchingSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC) +DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool usePV, bool useMC) { std::vector outputs; + Options opts{ + {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}, + {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}; + auto dataRequest = std::make_shared(); dataRequest->requestTracks(src, useMC); dataRequest->requestClusters(src, false); // no MC labels for clusters needed for refit only + if (usePV) { + dataRequest->requestPrimaryVertices(useMC); + } outputs.emplace_back("GLO", "COSMICTRC", 0, Lifetime::Timeframe); if (useMC) { @@ -193,16 +212,16 @@ DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC) dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs); + dataRequest->inputs.emplace_back("corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe); + + outputs.emplace_back("META", "COSMICMATCHER", 0, Lifetime::Sporadic); return DataProcessorSpec{ "cosmics-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, - Options{ - {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}, - {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}}; + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, usePV, useMC)}, + opts}; } } // namespace globaltracking diff --git a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingAssessmentSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingAssessmentSpec.cxx index 7ce95a1dfd3f8..e896c097cd03d 100644 --- a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingAssessmentSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingAssessmentSpec.cxx @@ -28,23 +28,27 @@ namespace o2 namespace globaltracking { +//_____________________________________________________________ +void GlobalFwdAssessmentSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + auto field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + const double centerMFT[3] = {0, 0, -61.4}; // Field at center of MFT + auto Bz = field->getBz(centerMFT); + mGloFwdAssessment->setBz(Bz); + return; + } + return; +} + //_____________________________________________________________ void GlobalFwdAssessmentSpec::init(InitContext& ic) { mGloFwdAssessment = std::make_unique(mUseMC); - + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); if (mMIDFilterDisabled) { mGloFwdAssessment->disableMIDFilter(); } - auto filename = o2::base::NameConf::getGRPFileName(); - const auto grp = o2::parameters::GRPObject::loadFrom(filename.c_str()); - if (grp) { - o2::base::Propagator::initFieldFromGRP(grp); - auto field = static_cast(TGeoGlobalMagField::Instance()->GetField()); - double centerMFT[3] = {0, 0, -61.4}; // Field at center of MFT - auto Bz = field->getBz(centerMFT); - mGloFwdAssessment->setBz(Bz); - } for (int sw = 0; sw < NStopWatches; sw++) { mTimer[sw].Stop(); @@ -58,6 +62,7 @@ void GlobalFwdAssessmentSpec::init(InitContext& ic) //_____________________________________________________________ void GlobalFwdAssessmentSpec::run(o2::framework::ProcessingContext& pc) { + o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTimer[SWQCAsync].Start(false); mGloFwdAssessment->runBasicQC(pc); @@ -104,7 +109,7 @@ void GlobalFwdAssessmentSpec::sendOutput(DataAllocator& output) TObjArray objar; mGloFwdAssessment->getHistos(objar); - output.snapshot(Output{"GLO", "FWDASSESSMENT", 0, Lifetime::Sporadic}, objar); + output.snapshot(Output{"GLO", "FWDASSESSMENT", 0}, objar); TFile* f = new TFile(Form("GlobalForwardAssessment.root"), "RECREATE"); objar.Write(); @@ -127,13 +132,22 @@ DataProcessorSpec getGlobaFwdAssessmentSpec(bool useMC, bool processGen, bool mi inputs.emplace_back("fwdtrklabels", "GLO", "GLFWD_MC", 0, Lifetime::Timeframe); } + auto ggRequest = std::make_shared(false, // orbitResetTime + false, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs, + true); // query only once all objects except mag.field + outputs.emplace_back("GLO", "FWDASSESSMENT", 0, Lifetime::Sporadic); return DataProcessorSpec{ "glofwd-assessment", inputs, outputs, - AlgorithmSpec{adaptFromTask(useMC, processGen, midFilterDisabled, finalizeAnalysis)}, + AlgorithmSpec{adaptFromTask(useMC, processGen, ggRequest, midFilterDisabled, finalizeAnalysis)}, Options{{}}}; } diff --git a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx index 683917d51b355..a43a1e8943739 100644 --- a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx @@ -20,7 +20,7 @@ #include "Framework/CCDBParamSpec.h" #include "CommonUtils/StringUtils.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "SimulationDataFormat/MCCompLabel.h" #include "DataFormatsMFT/TrackMFT.h" #include "DataFormatsITSMFT/Cluster.h" @@ -35,6 +35,7 @@ #include "TGeoGlobalMagField.h" #include "Field/MagneticField.h" #include "DetectorsBase/GRPGeomHelper.h" +#include "MCHTracking/TrackExtrap.h" using namespace o2::framework; using MCLabelsTr = gsl::span; @@ -91,18 +92,18 @@ void GlobalFwdMatchingDPL::run(ProcessingContext& pc) const auto& matchingParam = GlobalFwdMatchingParam::Instance(); if (matchingParam.saveMode == kSaveTrainingData) { - pc.outputs().snapshot(Output{"GLO", "GLFWDMFT", 0, Lifetime::Timeframe}, mMatching.getMFTMatchingPlaneParams()); - pc.outputs().snapshot(Output{"GLO", "GLFWDMCH", 0, Lifetime::Timeframe}, mMatching.getMCHMatchingPlaneParams()); - pc.outputs().snapshot(Output{"GLO", "GLFWDINF", 0, Lifetime::Timeframe}, mMatching.getMFTMCHMatchInfo()); + pc.outputs().snapshot(Output{"GLO", "GLFWDMFT", 0}, mMatching.getMFTMatchingPlaneParams()); + pc.outputs().snapshot(Output{"GLO", "GLFWDMCH", 0}, mMatching.getMCHMatchingPlaneParams()); + pc.outputs().snapshot(Output{"GLO", "GLFWDINF", 0}, mMatching.getMFTMCHMatchInfo()); } else { - pc.outputs().snapshot(Output{"GLO", "GLFWD", 0, Lifetime::Timeframe}, mMatching.getMatchedFwdTracks()); + pc.outputs().snapshot(Output{"GLO", "GLFWD", 0}, mMatching.getMatchedFwdTracks()); } if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "GLFWD_MC", 0, Lifetime::Timeframe}, mMatching.getMatchLabels()); + pc.outputs().snapshot(Output{"GLO", "GLFWD_MC", 0}, mMatching.getMatchLabels()); } if (mMatchRootOutput) { - pc.outputs().snapshot(Output{"GLO", "MTC_MFTMCH", 0, Lifetime::Timeframe}, mMatching.getMFTMCHMatchInfo()); + pc.outputs().snapshot(Output{"GLO", "MTC_MFTMCH", 0}, mMatching.getMFTMCHMatchInfo()); } mTimer.Stop(); } @@ -116,6 +117,9 @@ void GlobalFwdMatchingDPL::endOfStream(EndOfStreamContext& ec) void GlobalFwdMatchingDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + if (matcher == ConcreteDataMatcher("GLO", "GRPMAGFIELD", 0)) { + o2::mch::TrackExtrap::setField(); + } return; } if (matcher == ConcreteDataMatcher("MFT", "CLUSDICT", 0)) { @@ -153,6 +157,10 @@ void GlobalFwdMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) } else { mMatching.setMFTROFrameLengthInBC(alpParams.roFrameLengthInBC); // MFT ROFrame duration in \mus } + if (alpParams.roFrameBiasInBC != 0) { + mMatching.setMFTROFrameBiasInBC(alpParams.roFrameBiasInBC); // MFT ROFrame bias in BCs wrt orbit start + LOG(info) << "Setting MFT ROF bias to " << alpParams.roFrameBiasInBC << " BCs"; + } mMatching.init(); } diff --git a/Detectors/GlobalTrackingWorkflow/src/HMPMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/HMPMatcherSpec.cxx new file mode 100644 index 0000000000000..b0562fd4f3a1e --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/HMPMatcherSpec.cxx @@ -0,0 +1,181 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file HMPMatcherSpec.cxx + +#include +#include +#include "TStopwatch.h" +#include "Framework/ConfigParamRegistry.h" +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsBase/Propagator.h" +#include "CommonUtils/NameConf.h" +#include "DataFormatsParameters/GRPObject.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" +#include "DetectorsBase/GRPGeomHelper.h" + +// from Tracks +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/GlobalTrackAccessor.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsTRD/TrackTRD.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/TrackTPCTOF.h" + +// from HMPID +#include "DataFormatsHMP/Cluster.h" +#include "GlobalTracking/MatchHMP.h" +#include "GlobalTrackingWorkflow/HMPMatcherSpec.h" + +using namespace o2::framework; +// using MCLabelsTr = gsl::span; +// using GID = o2::dataformats::GlobalTrackID; +// using DetID = o2::detectors::DetID; + +using evIdx = o2::dataformats::EvIndex; +using MatchOutputType = std::vector; +using GID = o2::dataformats::GlobalTrackID; + +namespace o2 +{ +namespace globaltracking +{ + +class HMPMatcherSpec : public Task +{ + public: + HMPMatcherSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC) {} + ~HMPMatcherSpec() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(framework::EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + + private: + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + bool mUseMC = false; // true + bool mUseFIT = false; + bool mDoTPCRefit = false; + bool mStrict = false; + MatchHMP mMatcher; ///< Cluster finder + TStopwatch mTimer; +}; + +void HMPMatcherSpec::init(InitContext& ic) +{ + mTimer.Stop(); + mTimer.Reset(); + //-------- init geometry and field --------// + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); +} + +void HMPMatcherSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } +} + +void HMPMatcherSpec::run(ProcessingContext& pc) +{ + mTimer.Start(false); + + RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + + auto creationTime = DataRefUtils::getHeader(pc.inputs().getFirstValid(true))->creation; + + // LOG(debug) << "isTrackSourceLoaded: TPC -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPC); LOG(debug) << "isTrackSourceLoaded: ITSTPC -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPC); + + LOG(debug) << "isTrackSourceLoaded: TPC -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPC); + + //: + LOG(debug) << "isTrackSourceLoaded: TPCTOF -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTOF); + + LOG(debug) << "isTrackSourceLoaded: ITSTPC -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPC); + LOG(debug) << "isTrackSourceLoaded: TPCTRD -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTRD); + LOG(debug) << "isTrackSourceLoaded: ITSTPCTRD -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTRD); + LOG(debug) << "isTrackSourceLoaded: ITSTPCTOF -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTOF); + LOG(debug) << "isTrackSourceLoaded: TPCTRDTOF -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTRDTOF); + LOG(debug) << "isTrackSourceLoaded: ITSTPCTRDTOF -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTRDTOF); + + bool isTPCused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPC); + bool isITSTPCused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPC); + bool isTPCTRDused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTRD); + + bool isTPCTOFused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTOF); + + bool isITSTPCTRDused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTRD); + bool isITSTPCTOFused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTOF); + bool isTPCTRDTOFused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTRDTOF); + bool isITSTPCTRDTOFused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTRDTOF); + // uint32_t ss = o2::globaltracking::getSubSpec(mStrict ? o2::globaltracking::MatchingType::Strict : o2::globaltracking::MatchingType::Standard); + + mMatcher.run(recoData); + + pc.outputs().snapshot(Output{o2::header::gDataOriginHMP, "MATCHES", 0}, mMatcher.getMatchedTrackVector(o2::globaltracking::MatchHMP::trackType::CONSTR)); + if (mUseMC) { + pc.outputs().snapshot(Output{o2::header::gDataOriginHMP, "MCLABELS", 0}, mMatcher.getMatchedHMPLabelsVector(o2::globaltracking::MatchHMP::trackType::CONSTR)); + } + + mTimer.Stop(); +} + +void HMPMatcherSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(debug, "HMP matching total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getHMPMatcherSpec(GID::mask_t src, bool useMC, float extratolerancetrd, float extratolerancetof) +{ + // uint32_t ss = o2::globaltracking::getSubSpec(strict ? o2::globaltracking::MatchingType::Strict : o2::globaltracking::MatchingType::Standard); + auto dataRequest = std::make_shared(); + /* if (strict) { + dataRequest->setMatchingInputStrict(); + }*/ + + dataRequest->requestTracks(src, useMC); + dataRequest->requestClusters(GID::getSourceMask(GID::HMP), useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + + std::vector outputs; + + outputs.emplace_back(o2::header::gDataOriginHMP, "MATCHES", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back(o2::header::gDataOriginHMP, "MCLABELS", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "hmp-matcher", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, + Options{}}; +} + +} // namespace globaltracking +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx index 8608b9906529d..e92d5c35152e1 100644 --- a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx @@ -11,11 +11,15 @@ /// @file PrimaryVertexingSpec.cxx +#include +#include #include #include #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "DataFormatsITSMFT/TrkClusRef.h" #include "DataFormatsCalibration/MeanVertexObject.h" +#include "DataFormatsCalibration/MeanVertexBiasParam.h" #include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DetectorsBase/Propagator.h" @@ -29,7 +33,7 @@ #include "Framework/CCDBParamSpec.h" #include "Framework/DeviceSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsVertexing/PVertexer.h" #include "DetectorsBase/GRPGeomHelper.h" @@ -58,6 +62,7 @@ class PrimaryVertexingSpec : public Task private: void updateTimeDependentParams(ProcessingContext& pc); + void storeConfigs(ProcessingContext& pc); std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; o2::vertexing::PVertexer mVertexer; @@ -81,6 +86,7 @@ void PrimaryVertexingSpec::init(InitContext& ic) throw std::runtime_error(fmt::format("directory {} for raw data dumps does not exist", dumpDir)); } mVertexer.setPoolDumpDirectory(dumpDir); + mVertexer.setTrackSources(mTrackSrc); } void PrimaryVertexingSpec::run(ProcessingContext& pc) @@ -96,20 +102,26 @@ void PrimaryVertexingSpec::run(ProcessingContext& pc) o2::globaltracking::RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions - + storeConfigs(pc); std::vector tracks; std::vector tracksMCInfo; std::vector gids; auto maxTrackTimeError = PVertexerParams::Instance().maxTimeErrorMUS; auto trackMaxX = PVertexerParams::Instance().trackMaxX; + auto minIBHits = PVertexerParams::Instance().minIBHits; auto halfROFITS = 0.5 * mITSROFrameLengthMUS + mITSROFBiasMUS; auto hw2ErrITS = 2.f / std::sqrt(12.f) * mITSROFrameLengthMUS; // conversion from half-width to error for ITS - auto creator = [maxTrackTimeError, hw2ErrITS, halfROFITS, trackMaxX, &tracks, &gids](auto& _tr, GTrackID _origID, float t0, float terr) { + auto creator = [maxTrackTimeError, hw2ErrITS, halfROFITS, trackMaxX, minIBHits, &tracks, &gids, &recoData](auto& _tr, GTrackID _origID, float t0, float terr) { if constexpr (isBarrelTrack()) { if (!_origID.includesDet(DetID::ITS) || _tr.getX() > trackMaxX) { return true; // just in case this selection was not done on RecoContainer filling level } + auto itsID = recoData.getITSContributorGID(_origID); + if ((itsID.getSource() == GTrackID::ITS && o2::math_utils::numberOfBitsSet(recoData.getITSTrack(itsID).getPattern() & 7) < minIBHits) || + (itsID.getSource() == GTrackID::ITSAB && o2::math_utils::numberOfBitsSet(recoData.getITSABRef(itsID).pattern & 7) < minIBHits)) { // do not accept ITSAB tracklets + return true; + } if constexpr (isITSTrack()) { t0 += halfROFITS; // ITS time is supplied in \mus as beginning of ROF terr *= hw2ErrITS; // error is supplied as a half-ROF duration, convert to \mus @@ -129,33 +141,87 @@ void PrimaryVertexingSpec::run(ProcessingContext& pc) recoData.fillTrackMCLabels(gids, tracksMCInfo); } mVertexer.setStartIR(recoData.startIR); - std::vector ft0Data; + static std::vector ft0Data; if (mValidateWithIR) { // select BCs for validation + ft0Data.clear(); const o2::ft0::InteractionTag& ft0Params = o2::ft0::InteractionTag::Instance(); auto ft0all = recoData.getFT0RecPoints(); for (const auto& ftRP : ft0all) { if (ft0Params.isSelected(ftRP)) { - ft0Data.push_back(ftRP.getInteractionRecord()); + ft0Data.emplace_back(InteractionCandidate{ftRP.getInteractionRecord(), + float(ftRP.getInteractionRecord().differenceInBC(recoData.startIR) * o2::constants::lhc::LHCBunchSpacingMUS), + float(ftRP.getTrigger().getAmplA() + ftRP.getTrigger().getAmplC()), + GTrackID::FT0}); } } } mVertexer.process(tracks, gids, ft0Data, vertices, vertexTrackIDs, v2tRefs, tracksMCInfo, lblVtx); + + // flag vertices using UPC ITS mode + auto itsrofs = recoData.getITSTracksROFRecords(); + std::vector itsTrUPC(recoData.getITSTracks().size()); + for (auto& rof : itsrofs) { + if (rof.getFlag(o2::itsmft::ROFRecord::VtxUPCMode)) { + for (int i = rof.getFirstEntry(); i < rof.getFirstEntry() + rof.getNEntries(); i++) { + itsTrUPC[i] = true; + } + } + } + int nv = vertices.size(); + for (int iv = 0; iv < nv; iv++) { + int idMin = v2tRefs[iv].getFirstEntry(), idMax = idMin + v2tRefs[iv].getEntries(); + int nits = 0, nitsUPC = 0; + for (int id = idMin; id < idMax; id++) { + auto gid = recoData.getITSContributorGID(vertexTrackIDs[id]); + if (gid.getSource() == GIndex::ITS) { + nits++; + if (itsTrUPC[gid.getIndex()]) { + nitsUPC++; + } + } + } + if (nitsUPC > nits / 2) { + vertices[iv].setFlags(PVertex::UPCMode); + } + } + } else { + storeConfigs(pc); } - pc.outputs().snapshot(Output{"GLO", "PVTX", 0, Lifetime::Timeframe}, vertices); - pc.outputs().snapshot(Output{"GLO", "PVTX_CONTIDREFS", 0, Lifetime::Timeframe}, v2tRefs); - pc.outputs().snapshot(Output{"GLO", "PVTX_CONTID", 0, Lifetime::Timeframe}, vertexTrackIDs); + pc.outputs().snapshot(Output{"GLO", "PVTX", 0}, vertices); + pc.outputs().snapshot(Output{"GLO", "PVTX_CONTIDREFS"}, v2tRefs); + pc.outputs().snapshot(Output{"GLO", "PVTX_CONTID", 0}, vertexTrackIDs); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "PVTX_MCTR", 0, Lifetime::Timeframe}, lblVtx); + pc.outputs().snapshot(Output{"GLO", "PVTX_MCTR", 0}, lblVtx); } mTimer.Stop(); - LOGP(info, "Found {} PVs, Time CPU/Real:{:.3f}/{:.3f} (DBScan: {:.4f}, Finder:{:.4f}, Rej.Debris:{:.4f}, Reattach:{:.4f}) | {} trials for {} TZ-clusters, max.trials: {}, Slowest TZ-cluster: {} ms of mult {}", + LOGP(info, "Found {} PVs, Time CPU/Real:{:.3f}/{:.3f} (DBScan: {:.4f}, Finder:{:.4f}, MADSel:{:.4f}, Rej.Debris:{:.4f}, Reattach:{:.4f}) | {} trials for {} TZ-clusters, max.trials: {}, Slowest TZ-cluster: {} ms of mult {} | NInitial:{}, Rejections: NoFilledBC:{}, NoIntCand:{}, Debris:{}, Quality:{}, ITSOnly:{}", vertices.size(), mTimer.CpuTime() - timeCPU0, mTimer.RealTime() - timeReal0, - mVertexer.getTimeDBScan().CpuTime(), mVertexer.getTimeVertexing().CpuTime(), mVertexer.getTimeDebris().CpuTime(), mVertexer.getTimeReAttach().CpuTime(), - mVertexer.getTotTrials(), mVertexer.getNTZClusters(), mVertexer.getMaxTrialsPerCluster(), - mVertexer.getLongestClusterTimeMS(), mVertexer.getLongestClusterMult()); + mVertexer.getTimeDBScan().CpuTime(), mVertexer.getTimeVertexing().CpuTime(), mVertexer.getTimeMADSel().CpuTime(), mVertexer.getTimeDebris().CpuTime(), + mVertexer.getTimeReAttach().CpuTime(), mVertexer.getTotTrials(), mVertexer.getNTZClusters(), mVertexer.getMaxTrialsPerCluster(), + mVertexer.getLongestClusterTimeMS(), mVertexer.getLongestClusterMult(), mVertexer.getNIniFound(), + mVertexer.getNKilledBCValid(), mVertexer.getNKilledIntCand(), mVertexer.getNKilledDebris(), mVertexer.getNKilledQuality(), mVertexer.getNKilledITSOnly()); +} + +void PrimaryVertexingSpec::storeConfigs(ProcessingContext& pc) +{ + static bool first = true; + if (first) { + first = false; + const auto& confPV = PVertexerParams::Instance(); + const auto& confMV = o2::dataformats::MeanVertexBiasParam::Instance(); + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, confPV.getName()), confPV.getName()); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, confMV.getName()), confMV.getName()); + TMap md; + md.SetOwnerKeyValue(); + md.Add(new TObjString(confPV.getName().c_str()), new TObjString(o2::conf::ConfigurableParam::asJSON(confPV.getName()).c_str())); + md.Add(new TObjString(confMV.getName().c_str()), new TObjString(o2::conf::ConfigurableParam::asJSON(confMV.getName()).c_str())); + pc.outputs().snapshot(Output{"META", "PVERTEXER", 0}, md); + } + } } void PrimaryVertexingSpec::endOfStream(EndOfStreamContext& ec) @@ -178,6 +244,7 @@ void PrimaryVertexingSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) return; } if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); mVertexer.setMeanVertex((const o2::dataformats::MeanVertexObject*)obj); return; } @@ -211,7 +278,7 @@ void PrimaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) pc.inputs().get("meanvtx"); } -DataProcessorSpec getPrimaryVertexingSpec(GTrackID::mask_t src, bool skip, bool validateWithFT0, bool useMC) +DataProcessorSpec getPrimaryVertexingSpec(GTrackID::mask_t src, bool skip, bool validateWithFT0, bool useMC, bool useGeom) { std::vector outputs; auto dataRequest = std::make_shared(); @@ -229,16 +296,18 @@ DataProcessorSpec getPrimaryVertexingSpec(GTrackID::mask_t src, bool skip, bool outputs.emplace_back("GLO", "PVTX_MCTR", 0, Lifetime::Timeframe); } - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - true, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - o2::base::GRPGeomRequest::Aligned, // geometry + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // geometry dataRequest->inputs, true); dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + outputs.emplace_back("META", "PVERTEXER", 0, Lifetime::Sporadic); + return DataProcessorSpec{ "primary-vertexing", dataRequest->inputs, diff --git a/Detectors/GlobalTrackingWorkflow/src/ReaderDriverSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/ReaderDriverSpec.cxx new file mode 100644 index 0000000000000..d53bbf9a30690 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/ReaderDriverSpec.cxx @@ -0,0 +1,101 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ReaderDriverSpec.cxx + +#include +#include +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" +#include "Framework/RateLimiter.h" +#include "Framework/RawDeviceService.h" +#include "GlobalTrackingWorkflow/ReaderDriverSpec.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "CommonUtils/StringUtils.h" +#include "CommonDataFormat/IRFrame.h" +#include "TFile.h" +#include "TTree.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace globaltracking +{ +using HBFINI = o2::raw::HBFUtilsInitializer; + +class ReadeDriverSpec : public o2::framework::Task +{ + public: + ReadeDriverSpec(size_t minSHM) : mMinSHM(minSHM) {} + ~ReadeDriverSpec() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + int mTFRateLimit = -999; + int mNTF = 0; + size_t mMinSHM = 0; +}; + +void ReadeDriverSpec::init(InitContext& ic) +{ + mNTF = ic.options().get("max-tf"); +} + +void ReadeDriverSpec::run(ProcessingContext& pc) +{ + if (mTFRateLimit == -999) { + mTFRateLimit = std::stoi(pc.services().get().device()->fConfig->GetValue("timeframes-rate-limit")); + } + static RateLimiter limiter; + static int count = 0; + if (!count) { + if (HBFINI::NTFs < 0) { + LOGP(fatal, "Number of TFs to process was not initizalized in the HBFUtilsInitializer"); + } + mNTF = (mNTF > 0 && mNTF < HBFINI::NTFs) ? mNTF : HBFINI::NTFs; + } else { // check only for count > 0 + limiter.check(pc, mTFRateLimit, mMinSHM); + } + auto& v = pc.outputs().make>(Output{"GLO", "READER_DRIVER", 0}); + if (!HBFINI::IRFrameSel.getMin().isDummy() && !HBFINI::IRFrameSel.getMax().isDummy()) { + v.push_back(HBFINI::IRFrameSel); + LOGP(debug, "OUTVEC {}/{}", v.back().getMin().asString(), v.back().getMax().asString()); + } + count++; + if ((HBFINI::LastIRFrameIndex == -1 && count >= mNTF) || (v.size() && v.back().isLast())) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +DataProcessorSpec getReaderDriverSpec(const std::string& metricChannel, size_t minSHM) +{ + std::vector outputSpec; + std::vector options; + options.emplace_back(ConfigParamSpec{"max-tf", o2::framework::VariantType::Int, -1, {"max TFs to process (<1 : no limits)"}}); + if (!metricChannel.empty()) { + options.emplace_back(ConfigParamSpec{"channel-config", VariantType::String, metricChannel, {"Out-of-band channel config for TF throttling"}}); + } + return DataProcessorSpec{ + HBFINI::ReaderDriverDevice, + Inputs{}, + Outputs{{"GLO", "READER_DRIVER", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask(minSHM)}, + options}; +} + +} // namespace globaltracking +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx index 5a5821ffd4a9c..7f961536cf1f2 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx @@ -19,7 +19,7 @@ #include "CommonDataFormat/RangeReference.h" #include "ReconstructionDataFormats/V0.h" #include "ReconstructionDataFormats/Cascade.h" -#include "ReconstructionDataFormats/DecayNbody.h" +#include "ReconstructionDataFormats/Decay3Body.h" using namespace o2::framework; @@ -29,8 +29,11 @@ namespace vertexing { using RRef = o2::dataformats::RangeReference; using V0 = o2::dataformats::V0; +using V0Index = o2::dataformats::V0Index; using Cascade = o2::dataformats::Cascade; -using DecayNbody = o2::dataformats::DecayNbody; +using CascadeIndex = o2::dataformats::CascadeIndex; +using Decay3Body = o2::dataformats::Decay3Body; +using Decay3BodyIndex = o2::dataformats::Decay3BodyIndex; template using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; @@ -39,34 +42,42 @@ using namespace o2::header; DataProcessorSpec getSecondaryVertexWriterSpec() { - auto loggerV = [](std::vector const& v) { + auto loggerV = [](std::vector const& v) { LOG(info) << "SecondaryVertexWriter pulled " << v.size() << " v0s"; }; - auto loggerC = [](std::vector const& v) { + auto loggerC = [](std::vector const& v) { LOG(info) << "SecondaryVertexWriter pulled " << v.size() << " cascades"; }; - auto loggerD = [](std::vector const& v) { + auto loggerD = [](std::vector const& v) { LOG(info) << "SecondaryVertexWriter pulled " << v.size() << " decays3body"; }; - auto inpV0ID = InputSpec{"v0s", "GLO", "V0S", 0}; - auto inpV0RefID = InputSpec{"pv2v0ref", "GLO", "PVTX_V0REFS", 0}; - auto inpCascID = InputSpec{"cascs", "GLO", "CASCS", 0}; - auto inpCascRefID = InputSpec{"pv2cascref", "GLO", "PVTX_CASCREFS", 0}; - auto inp3BodyID = InputSpec{"decays3body", "GLO", "DECAYS3BODY", 0}; - auto inp3BodyRefID = InputSpec{"pv23bodyref", "GLO", "PVTX_3BODYREFS", 0}; + auto inpV0 = InputSpec{"v0s", "GLO", "V0S", 0}; + auto inpV0ID = InputSpec{"v0sIdx", "GLO", "V0S_IDX", 0}; + auto inpV0Ref = InputSpec{"pv2v0ref", "GLO", "PVTX_V0REFS", 0}; + auto inpCasc = InputSpec{"cascs", "GLO", "CASCS", 0}; + auto inpCascID = InputSpec{"cascsIdx", "GLO", "CASCS_IDX", 0}; + auto inpCascRef = InputSpec{"pv2cascref", "GLO", "PVTX_CASCREFS", 0}; + auto inp3Body = InputSpec{"decays3body", "GLO", "DECAYS3BODY", 0}; + auto inp3BodyID = InputSpec{"decays3bodyIdx", "GLO", "DECAYS3BODY_IDX", 0}; + auto inp3BodyRef = InputSpec{"pv23bodyref", "GLO", "PVTX_3BODYREFS", 0}; return MakeRootTreeWriterSpec("secondary-vertex-writer", "o2_secondary_vertex.root", MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with Secondary Vertices"}, - BranchDefinition>{inpV0ID, "V0s", loggerV}, - BranchDefinition>{inpV0RefID, "PV2V0Refs"}, - BranchDefinition>{inpCascID, "Cascades", loggerC}, - BranchDefinition>{inpCascRefID, "PV2CascRefs"}, - BranchDefinition>{inp3BodyID, "Decays3Body", loggerD}, - BranchDefinition>{inp3BodyRefID, "PV23BodyRefs"})(); + BranchDefinition>{inpV0ID, "V0sID", loggerV}, + BranchDefinition>{inpV0, "V0s"}, + BranchDefinition>{inpV0Ref, "PV2V0Refs"}, + + BranchDefinition>{inpCascID, "CascadesID", loggerC}, + BranchDefinition>{inpCasc, "Cascades"}, + BranchDefinition>{inpCascRef, "PV2CascRefs"}, + + BranchDefinition>{inp3BodyID, "Decays3BodyID", loggerD}, + BranchDefinition>{inp3Body, "Decays3Body"}, + BranchDefinition>{inp3BodyRef, "PV23BodyRefs"})(); } } // namespace vertexing diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index d82bc5c320577..3a9de8662f4b5 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -11,8 +11,12 @@ /// @file SecondaryVertexingSpec.cxx +#include +#include #include -#include "ReconstructionDataFormats/DecayNbody.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "Framework/CCDBParamSpec.h" +#include "ReconstructionDataFormats/Decay3Body.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -24,10 +28,12 @@ #include "SimulationDataFormat/MCEventLabel.h" #include "CommonUtils/NameConf.h" #include "DetectorsVertexing/SVertexer.h" +#include "StrangenessTracking/StrangenessTracker.h" #include "DetectorsBase/GRPGeomHelper.h" +#include "DetectorsBase/GlobalParams.h" #include "TStopwatch.h" #include "TPCCalibration/VDriftHelper.h" -#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCFastTransformPOD.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/DeviceSpec.h" @@ -39,7 +45,7 @@ using VRef = o2::dataformats::VtxTrackRef; using PVertex = const o2::dataformats::PrimaryVertex; using V0 = o2::dataformats::V0; using Cascade = o2::dataformats::Cascade; -using DecayNbody = o2::dataformats::DecayNbody; +using Decay3Body = o2::dataformats::Decay3Body; using RRef = o2::dataformats::RangeReference; using DataRequest = o2::globaltracking::DataRequest; @@ -53,7 +59,7 @@ namespace o2d = o2::dataformats; class SecondaryVertexingSpec : public Task { public: - SecondaryVertexingSpec(std::shared_ptr dr, std::shared_ptr gr, bool enabCasc, bool enable3body) : mDataRequest(dr), mGGCCDBRequest(gr), mEnableCascades(enabCasc), mEnable3BodyVertices(enable3body) {} + SecondaryVertexingSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool enabCasc, bool enable3body, bool enableStrangenessTracking, bool enableCCDBParams, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mSrc(src), mEnableCascades(enabCasc), mEnable3BodyVertices(enable3body), mEnableStrangenessTracking(enableStrangenessTracking), mEnableCCDBParams(enableCCDBParams), mUseMC(useMC) {} ~SecondaryVertexingSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -61,14 +67,20 @@ class SecondaryVertexingSpec : public Task void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; private: + void storeConfigs(ProcessingContext& pc); void updateTimeDependentParams(ProcessingContext& pc); std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; o2::tpc::VDriftHelper mTPCVDriftHelper{}; - o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps{nullptr}; + GTrackID::mask_t mSrc{}; + bool mEnableCCDBParams = false; bool mEnableCascades = false; bool mEnable3BodyVertices = false; + bool mEnableStrangenessTracking = false; + bool mUseMC = false; o2::vertexing::SVertexer mVertexer; + o2::strangeness_tracking::StrangenessTracker mStrTracker; TStopwatch mTimer; }; @@ -81,30 +93,56 @@ void SecondaryVertexingSpec::init(InitContext& ic) mVertexer.setEnableCascades(mEnableCascades); mVertexer.setEnable3BodyDecays(mEnable3BodyVertices); mVertexer.setNThreads(ic.options().get("threads")); + mVertexer.setUseMC(mUseMC); + if (mEnableStrangenessTracking) { + mStrTracker.setCorrType(o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT); + mStrTracker.setConfigParams(&o2::strangeness_tracking::StrangenessTrackingParamConfig::Instance()); + mStrTracker.setupThreads(ic.options().get("threads")); + mStrTracker.setupFitters(); + mStrTracker.setMCTruthOn(mUseMC); + mVertexer.setStrangenessTracker(&mStrTracker); + } } void SecondaryVertexingSpec::run(ProcessingContext& pc) { double timeCPU0 = mTimer.CpuTime(), timeReal0 = mTimer.RealTime(); mTimer.Start(false); + static std::array fitCalls{}; o2::globaltracking::RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); updateTimeDependentParams(pc); - - auto& v0s = pc.outputs().make>(Output{"GLO", "V0S", 0, Lifetime::Timeframe}); - auto& v0Refs = pc.outputs().make>(Output{"GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe}); - auto& cascs = pc.outputs().make>(Output{"GLO", "CASCS", 0, Lifetime::Timeframe}); - auto& cascRefs = pc.outputs().make>(Output{"GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe}); - auto& vtx3body = pc.outputs().make>(Output{"GLO", "DECAYS3BODY", 0, Lifetime::Timeframe}); - auto& vtx3bodyRefs = pc.outputs().make>(Output{"GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe}); - - mVertexer.process(recoData); - mVertexer.extractSecondaryVertices(v0s, v0Refs, cascs, cascRefs, vtx3body, vtx3bodyRefs); + storeConfigs(pc); + mVertexer.process(recoData, pc); mTimer.Stop(); - LOG(info) << "Found " << v0s.size() << " V0s, " << cascs.size() << " cascades, and " << vtx3body.size() << " 3-body decays; timing: CPU: " - << mTimer.CpuTime() - timeCPU0 << " Real: " << mTimer.RealTime() - timeReal0 << " s"; + auto calls = mVertexer.getNFitterCalls(); + LOGP(info, "Found {} V0s ({} fits), {} cascades ({} fits), {} 3-body decays ({} fits), {} strange tracks. Timing: CPU: {:.2f} Real: {:.2f} s", + mVertexer.getNV0s(), calls[0] - fitCalls[0], mVertexer.getNCascades(), calls[1] - fitCalls[1], mVertexer.getN3Bodies(), calls[2] - fitCalls[2], mVertexer.getNStrangeTracks(), + mTimer.CpuTime() - timeCPU0, mTimer.RealTime() - timeReal0); + fitCalls = calls; +} + +void SecondaryVertexingSpec::storeConfigs(ProcessingContext& pc) +{ + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + const auto& confSV = SVertexerParams::Instance(); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, confSV.getName()), confSV.getName()); + TMap md; + md.SetOwnerKeyValue(); + md.Add(new TObjString(confSV.getName().c_str()), new TObjString(o2::conf::ConfigurableParam::asJSON(confSV.getName()).c_str())); + if (mEnableStrangenessTracking) { + const auto& confST = o2::strangeness_tracking::StrangenessTrackingParamConfig::Instance(); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, confST.getName()), confST.getName()); + md.Add(new TObjString(confST.getName().c_str()), new TObjString(o2::conf::ConfigurableParam::asJSON(confST.getName()).c_str())); + } + pc.outputs().snapshot(Output{"META", "SVERTEXER", 0}, md); + } + } } void SecondaryVertexingSpec::endOfStream(EndOfStreamContext& ec) @@ -121,78 +159,166 @@ void SecondaryVertexingSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mStrTracker.setClusterDictionaryITS((const o2::itsmft::TopologyDictionary*)obj); + return; + } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mVertexer.setMeanVertex((const o2::dataformats::MeanVertexObject*)obj); return; } + if (matcher == ConcreteDataMatcher("GLO", "SVPARAM", 0)) { + LOG(info) << "SVertexer Params updated from ccdb"; + return; + } + if (matcher == ConcreteDataMatcher("ITS", "GEOMTGEO", 0)) { + LOG(info) << "ITS GeomtetryTGeo loaded from ccdb"; + o2::its::GeometryTGeo::adopt((o2::its::GeometryTGeo*)obj); + return; + } +#ifdef ENABLE_UPGRADES + if (matcher == ConcreteDataMatcher("IT3", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mStrTracker.setClusterDictionaryIT3((const o2::its3::TopologyDictionary*)obj); + return; + } +#endif } void SecondaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); - mTPCVDriftHelper.extractCCDBInputs(pc); - o2::tpc::CorrectionMapsLoader::extractCCDBInputs(pc); + if (mSrc[GTrackID::TPC]) { + mTPCVDriftHelper.extractCCDBInputs(pc); + auto const& raw = pc.inputs().get("corrMap"); + mTPCCorrMaps = &gpu::TPCFastTransformPOD::get(raw); + } static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; mVertexer.init(); + if (mEnableCCDBParams) { + // for reading the calib objects from the CCDB + pc.inputs().get("SVParam"); + } if (pc.services().get().inputTimesliceId == 0) { - SVertexerParams::Instance().printKeyValues(); + // setting and or overwriting the configurable params + SVertexerParams::Instance().printKeyValues(true, true); + } + if (pc.inputs().getPos("itsTGeo") >= 0) { + pc.inputs().get("itsTGeo"); + } + if (mEnableStrangenessTracking) { + o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); } + +#ifdef ENABLE_UPGRADES + if (o2::GlobalParams::Instance().withITS3) { // hack to trigger loading dictionary + pc.inputs().get("cldict"); + } +#endif } // we may have other params which need to be queried regularly - bool updateMaps = false; - if (mTPCCorrMapsLoader.isUpdated()) { - mVertexer.setTPCCorrMaps(&mTPCCorrMapsLoader); - mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; - } - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, - mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, - mTPCVDriftHelper.getSourceName()); - mVertexer.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); - mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); + if (mSrc[GTrackID::TPC]) { + mVertexer.setTPCCorrMaps(mTPCCorrMaps); + + if (mTPCVDriftHelper.isUpdated()) { + LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, + mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, + mTPCVDriftHelper.getSourceName()); + mVertexer.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); + mTPCVDriftHelper.acknowledgeUpdate(); + } + } + if (mEnableStrangenessTracking) { + if (o2::base::Propagator::Instance()->getNominalBz() != mStrTracker.getBz()) { + mStrTracker.setBz(o2::base::Propagator::Instance()->getNominalBz()); + mStrTracker.setupFitters(); + } } + + pc.inputs().get("meanvtx"); } -DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCasc, bool enable3body) +DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool enableCCDBParams, + bool useMC, bool useGeom) { std::vector outputs; + Options opts{ + {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}, + {"threads", VariantType::Int, 1, {"Number of threads"}}}; auto dataRequest = std::make_shared(); - - bool useMC = false; + if (enableCCDBParams) { + dataRequest->inputs.emplace_back("SVParam", "GLO", "SVPARAM", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/SVertexerParam")); + } + GTrackID::mask_t srcClus{}; + if (enableStrangenesTracking) { + src |= (srcClus = GTrackID::getSourceMask(GTrackID::ITS)); + } + if (GTrackID::includesDet(o2::detectors::DetID::TPC, src) && !src[GTrackID::TPC]) { + LOGP(warn, "Tracks involving TPC were requested w/o requesting TPC-only tracks, simplified selection will be applied"); + } + if (src[GTrackID::TPC]) { + srcClus |= GTrackID::getSourceMask(GTrackID::TPC); + } + if (srcClus.any()) { + dataRequest->requestClusters(srcClus, useMC); + } +#ifdef ENABLE_UPGRADES + if (o2::GlobalParams::Instance().withITS3) { // hack to trigger loading dictionary + dataRequest->inputs.emplace_back("cldict", "IT3", "CLUSDICT", Lifetime::Condition, ccdbParamSpec("IT3/Calib/ClusterDictionary")); + } +#endif dataRequest->requestTracks(src, useMC); - dataRequest->requestPrimaryVertertices(useMC); - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry + dataRequest->requestPrimaryVertices(useMC); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + useGeom || enableStrangenesTracking ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // geometry dataRequest->inputs, true); - o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs); + if (!useGeom && enableStrangenesTracking) { + ggRequest->addInput({"itsTGeo", "ITS", "GEOMTGEO", 0, Lifetime::Condition, framework::ccdbParamSpec("ITS/Config/Geometry")}, dataRequest->inputs); + } + if (src[GTrackID::TPC]) { + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + dataRequest->inputs.emplace_back("corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe); + } + outputs.emplace_back("GLO", "V0S_IDX", 0, Lifetime::Timeframe); // found V0s indices + outputs.emplace_back("GLO", "V0S", 0, Lifetime::Timeframe); // found V0s + outputs.emplace_back("GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe); // prim.vertex -> V0s refs + + outputs.emplace_back("GLO", "CASCS_IDX", 0, Lifetime::Timeframe); // found Cascades indices + outputs.emplace_back("GLO", "CASCS", 0, Lifetime::Timeframe); // found Cascades + outputs.emplace_back("GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe); // prim.vertex -> Cascades refs + + outputs.emplace_back("GLO", "DECAYS3BODY_IDX", 0, Lifetime::Timeframe); // found 3 body vertices indices + outputs.emplace_back("GLO", "DECAYS3BODY", 0, Lifetime::Timeframe); // found 3 body vertices + outputs.emplace_back("GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe); // prim.vertex -> 3 body vertices refs - outputs.emplace_back("GLO", "V0S", 0, Lifetime::Timeframe); // found V0s - outputs.emplace_back("GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe); // prim.vertex -> V0s refs - outputs.emplace_back("GLO", "CASCS", 0, Lifetime::Timeframe); // found Cascades - outputs.emplace_back("GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe); // prim.vertex -> Cascades refs - outputs.emplace_back("GLO", "DECAYS3BODY", 0, Lifetime::Timeframe); // found 3 body vertices - outputs.emplace_back("GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe); // prim.vertex -> 3 body vertices refs + if (enableStrangenesTracking) { + outputs.emplace_back("GLO", "STRANGETRACKS", 0, Lifetime::Timeframe); // found strange track + outputs.emplace_back("GLO", "CLUSUPDATES", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back("GLO", "STRANGETRACKS_MC", 0, Lifetime::Timeframe); + LOG(info) << "Strangeness tracker will use MC"; + } + } + outputs.emplace_back("META", "SVERTEXER", 0, Lifetime::Sporadic); return DataProcessorSpec{ "secondary-vertexing", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, enableCasc, enable3body)}, - Options{{"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}, - {"threads", VariantType::Int, 1, {"Number of threads"}}}}; + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, src, enableCasc, enable3body, enableStrangenesTracking, enableCCDBParams, useMC)}, + opts}; } } // namespace vertexing diff --git a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx new file mode 100644 index 0000000000000..c438f869773a5 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx @@ -0,0 +1,195 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// \file StrangenessTrackingSpec.cxx +/// \brief + +#include +#include +#include "TGeoGlobalMagField.h" +#include "Framework/ConfigParamRegistry.h" +#include "Field/MagneticField.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "StrangenessTracking/StrangenessTrackingConfigParam.h" +#include "GlobalTrackingWorkflow/StrangenessTrackingSpec.h" +#include "ITSWorkflow/TrackerSpec.h" +#include "ITSWorkflow/TrackReaderSpec.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" +#include "DataFormatsParameters/GRPObject.h" + +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITS/TrackITS.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "ITStracking/IOUtils.h" +#include "DetectorsCommonDataFormats/DetectorNameConf.h" + +#include +namespace o2 +{ +using namespace o2::framework; +namespace strangeness_tracking +{ + +using StrangeTrack = o2::dataformats::StrangeTrack; +using DataRequest = o2::globaltracking::DataRequest; + +StrangenessTrackerSpec::StrangenessTrackerSpec(std::shared_ptr dr, std::shared_ptr gr, bool isMC) : mDataRequest{dr}, mGGCCDBRequest(gr), mUseMC{isMC} +{ + // no ops +} + +void StrangenessTrackerSpec::init(framework::InitContext& ic) +{ + mTimer.Stop(); + mTimer.Reset(); + + // load propagator + + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mTracker.setCorrType(o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT); + mTracker.setConfigParams(&StrangenessTrackingParamConfig::Instance()); + mTracker.setupThreads(1); + mTracker.setupFitters(); + + LOG(info) << "Initialized strangeness tracker..."; +} + +void StrangenessTrackerSpec::run(framework::ProcessingContext& pc) +{ + mTimer.Start(false); + LOG(debug) << "Running strangeness tracker..."; + + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); + storeConfigs(pc); + auto geom = o2::its::GeometryTGeo::Instance(); + mTracker.loadData(recoData); + mTracker.prepareITStracks(); + mTracker.process(); + pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS", 0}, mTracker.getStrangeTrackVec()); + pc.outputs().snapshot(Output{"GLO", "CLUSUPDATES", 0}, mTracker.getClusAttachments()); + + if (mUseMC) { + pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS_MC", 0}, mTracker.getStrangeTrackLabels()); + } + + mTimer.Stop(); +} + +void StrangenessTrackerSpec::storeConfigs(framework::ProcessingContext& pc) +{ + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + const auto& conf = StrangenessTrackingParamConfig::Instance(); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, conf.getName()), conf.getName()); + TMap md; + md.SetOwnerKeyValue(); + md.Add(new TObjString(conf.getName().c_str()), new TObjString(o2::conf::ConfigurableParam::asJSON(conf.getName()).c_str())); + pc.outputs().snapshot(Output{"META", "STRTRACKER", 0}, md); + } + } +} + +///_______________________________________ +void StrangenessTrackerSpec::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + // pc.inputs().get("cldict"); // just to trigger the finaliseCCDB + if (pc.inputs().getPos("itsTGeo") >= 0) { + pc.inputs().get("itsTGeo"); + } + o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } + if (o2::base::Propagator::Instance()->getNominalBz() != mTracker.getBz()) { + mTracker.setBz(o2::base::Propagator::Instance()->getNominalBz()); + mTracker.setupFitters(); + } + mTracker.setMCTruthOn(mUseMC); +} + +///_______________________________________ +void StrangenessTrackerSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mTracker.setClusterDictionaryITS((const o2::itsmft::TopologyDictionary*)obj); + return; + } + if (matcher == ConcreteDataMatcher("ITS", "GEOMTGEO", 0)) { + LOG(info) << "ITS GeomtetryTGeo loaded from ccdb"; + o2::its::GeometryTGeo::adopt((o2::its::GeometryTGeo*)obj); + return; + } +#ifdef ENABLE_UPGRADES + if (matcher == ConcreteDataMatcher("IT3", "CLUSDICT", 0)) { + LOG(info) << "it3 cluster dictionary updated"; + mTracker.setClusterDictionaryIT3((const o2::its3::TopologyDictionary*)obj); + return; + } +#endif +} + +void StrangenessTrackerSpec::endOfStream(framework::EndOfStreamContext& ec) +{ + LOGF(info, "Strangeness tracking total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getStrangenessTrackerSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useGeom) +{ + // ITS + auto dataRequest = std::make_shared(); + dataRequest->requestITSClusters(useMC); + dataRequest->requestTracks(src, useMC); + dataRequest->requestPrimaryVertices(useMC); + dataRequest->requestSecondaryVertices(useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // geometry + dataRequest->inputs, + true); + if (!useGeom) { + ggRequest->addInput({"itsTGeo", "ITS", "GEOMTGEO", 0, Lifetime::Condition, framework::ccdbParamSpec("ITS/Config/Geometry")}, dataRequest->inputs); + } + std::vector outputs; + outputs.emplace_back("GLO", "STRANGETRACKS", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "CLUSUPDATES", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back("GLO", "STRANGETRACKS_MC", 0, Lifetime::Timeframe); + LOG(info) << "Strangeness tracker will use MC"; + } + outputs.emplace_back("META", "STRTRACKER", 0, Lifetime::Sporadic); + + return DataProcessorSpec{ + "strangeness-tracker", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, + Options{}}; +} + +} // namespace strangeness_tracking +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingWriterSpec.cxx new file mode 100644 index 0000000000000..613df6a803eee --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingWriterSpec.cxx @@ -0,0 +1,58 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file StrangenessWriterSpec.cxx +/// \brief + +#include +#include "GlobalTrackingWorkflow/StrangenessTrackingWriterSpec.h" + +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "CommonDataFormat/TimeStamp.h" +#include "CommonDataFormat/RangeReference.h" +#include "ReconstructionDataFormats/StrangeTrack.h" +#include "StrangenessTracking/StrangenessTracker.h" +#include "SimulationDataFormat/MCCompLabel.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace strangeness_tracking +{ +using StrangeTrack = dataformats::StrangeTrack; + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using LabelsType = std::vector; +using namespace o2::header; + +DataProcessorSpec getStrangenessTrackingWriterSpec(bool useMC) +{ + auto loggerV = [](std::vector const& v) { + LOG(info) << "StrangenessTracker writer pulled " << v.size() << " strange tracks"; + }; + + auto inpStTrkID = InputSpec{"strangetracks", "GLO", "STRANGETRACKS", 0}; + auto inpClusAtt = InputSpec{"clusupdates", "GLO", "CLUSUPDATES", 0}; + auto inpMCLab = InputSpec{"stkmclabels", "GLO", "STRANGETRACKS_MC", 0}; + + return MakeRootTreeWriterSpec("strangenesstracking-writer", + "o2_strange_tracks.root", + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with Strange Tracks"}, + BranchDefinition>{inpStTrkID, "StrangeTracks", loggerV}, + BranchDefinition>{inpClusAtt, "ClusUpdates"}, + BranchDefinition{inpMCLab, "StrangeTrackMCLab", (useMC ? 1 : 0), ""} // one branch if mc labels enabled + )(); +} + +} // namespace strangeness_tracking +} // namespace o2 \ No newline at end of file diff --git a/Detectors/GlobalTrackingWorkflow/src/TOFEventTimeChecker.cxx b/Detectors/GlobalTrackingWorkflow/src/TOFEventTimeChecker.cxx index eb212f2507786..3d8b36043ddeb 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TOFEventTimeChecker.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TOFEventTimeChecker.cxx @@ -266,10 +266,10 @@ void TOFEventTimeChecker::processEvent(std::vector& tracks) float betaexpPr = mL / mExpPr * cinv; //Mass - float mass = mP / beta * TMath::Sqrt(TMath::Abs(1 - beta * beta)); - float massexpPi = mP / betaexpPi * TMath::Sqrt(TMath::Abs(1 - betaexpPi * betaexpPi)); - float massexpKa = mP / betaexpKa * TMath::Sqrt(TMath::Abs(1 - betaexpKa * betaexpKa)); - float massexpPr = mP / betaexpPr * TMath::Sqrt(TMath::Abs(1 - betaexpPr * betaexpPr)); + float mass = mP / beta * TMath::Sqrt(std::abs(1 - beta * beta)); + float massexpPi = mP / betaexpPi * TMath::Sqrt(std::abs(1 - betaexpPi * betaexpPi)); + float massexpKa = mP / betaexpKa * TMath::Sqrt(std::abs(1 - betaexpKa * betaexpKa)); + float massexpPr = mP / betaexpPr * TMath::Sqrt(std::abs(1 - betaexpPr * betaexpPr)); if (massexpPi < 0.13) { // remove wrong track lengths continue; diff --git a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx index 1ef21e4e575c0..17ee87b8d52cd 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx @@ -13,6 +13,8 @@ #include #include +#include +#include #include "TStopwatch.h" #include "Framework/ConfigParamRegistry.h" #include "DetectorsBase/GeometryManager.h" @@ -22,8 +24,9 @@ #include "DataFormatsGlobalTracking/RecoContainer.h" #include "Framework/Task.h" #include "Framework/DataProcessorSpec.h" +#include "Framework/DeviceSpec.h" #include "TPCCalibration/VDriftHelper.h" -#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCFastTransformPOD.h" // from Tracks #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -58,7 +61,7 @@ namespace globaltracking class TOFMatcherSpec : public Task { public: - TOFMatcherSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC, bool useFIT, bool tpcRefit, bool strict, bool pushMatchable) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT), mDoTPCRefit(tpcRefit), mStrict(strict), mPushMatchable(pushMatchable) {} + TOFMatcherSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC, bool useFIT, bool tpcRefit, bool strict, bool pushMatchable, int lanes = 1, bool requestCTPLumi = false) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT), mDoTPCRefit(tpcRefit), mStrict(strict), mPushMatchable(pushMatchable), mNlanes(lanes), mRequestCTPLumi(requestCTPLumi) {} ~TOFMatcherSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -67,16 +70,19 @@ class TOFMatcherSpec : public Task private: void updateTimeDependentParams(ProcessingContext& pc); + void storeConfigs(ProcessingContext& pc); std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; o2::tpc::VDriftHelper mTPCVDriftHelper{}; - o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps = nullptr; bool mUseMC = true; bool mUseFIT = false; bool mDoTPCRefit = false; bool mStrict = false; bool mPushMatchable = false; float mExtraTolTRD = 0.; + int mNlanes = 1; + bool mRequestCTPLumi = false; MatchTOF mMatcher; ///< Cluster finder TStopwatch mTimer; }; @@ -90,15 +96,17 @@ void TOFMatcherSpec::init(InitContext& ic) mMatcher.setHighPurity(); } mMatcher.storeMatchable(mPushMatchable); - mMatcher.setExtraTimeToleranceTRD(mExtraTolTRD); + mMatcher.setNlanes(mNlanes); } void TOFMatcherSpec::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTPCVDriftHelper.extractCCDBInputs(pc); - o2::tpc::CorrectionMapsLoader::extractCCDBInputs(pc); + auto const& raw = pc.inputs().get("corrMap"); + mTPCCorrMaps = &o2::gpu::TPCFastTransformPOD::get(raw); + float lumiCTP = mRequestCTPLumi ? pc.inputs().get("lumiCTP") : 0; static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once const auto bcs = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getBunchFilling().getFilledBCs(); @@ -108,13 +116,7 @@ void TOFMatcherSpec::updateTimeDependentParams(ProcessingContext& pc) initOnceDone = true; // put here init-once stuff } - // we may have other params which need to be queried regularly - bool updateMaps = false; - if (mTPCCorrMapsLoader.isUpdated()) { - mMatcher.setTPCCorrMaps(&mTPCCorrMapsLoader); - mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; - } + mMatcher.setTPCCorrMaps(mTPCCorrMaps, lumiCTP); if (mTPCVDriftHelper.isUpdated()) { LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, @@ -122,10 +124,6 @@ void TOFMatcherSpec::updateTimeDependentParams(ProcessingContext& pc) mTPCVDriftHelper.getSourceName()); mMatcher.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } @@ -137,9 +135,6 @@ void TOFMatcherSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } } void TOFMatcherSpec::run(ProcessingContext& pc) @@ -149,6 +144,7 @@ void TOFMatcherSpec::run(ProcessingContext& pc) RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); updateTimeDependentParams(pc); + storeConfigs(pc); auto creationTime = pc.services().get().creation; LOG(debug) << "isTrackSourceLoaded: TPC -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPC); @@ -165,89 +161,103 @@ void TOFMatcherSpec::run(ProcessingContext& pc) mMatcher.setTS(creationTime); - mMatcher.run(recoData); + mMatcher.run(recoData, pc.services().get().firstTForbit); + static pmr::vector dummyMCLab; if (isTPCused) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_TPC", ss, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC)); - if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_TPC", ss, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC)); - } - + auto& mtcInfo = pc.outputs().make>(Output{o2::header::gDataOriginTOF, "MTC_TPC", ss}); + auto& mclabels = mUseMC ? pc.outputs().make>(Output{o2::header::gDataOriginTOF, "MCMTC_TPC", ss}) : dummyMCLab; + auto& tracksTPCTOF = pc.outputs().make>(OutputRef{"tpctofTracks", ss}); auto nmatch = mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC).size(); - if (mDoTPCRefit) { - LOG(debug) << "Refitting " << nmatch << " matched TPC tracks with TOF time info"; - } else { - LOG(debug) << "Shifting Z for " << nmatch << " matched TPC tracks according to TOF time info"; - } - auto& tracksTPCTOF = pc.outputs().make>(OutputRef{"tpctofTracks", ss}, nmatch); - mMatcher.makeConstrainedTPCTracks(tracksTPCTOF); + LOG(debug) << (mDoTPCRefit ? "Refitting " : "Shifting Z for ") << nmatch << " matched TPC tracks with TOF time info"; + mMatcher.makeConstrainedTPCTracks(mtcInfo, mclabels, tracksTPCTOF); } if (isITSTPCused) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPC", 0, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPC)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPC", 0}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPC)); if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_ITSTPC", 0, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPC)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_ITSTPC", 0}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPC)); } } if (isTPCTRDused) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_TPCTRD", ss, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPCTRD)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_TPCTRD", ss}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPCTRD)); if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_TPCTRD", ss, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPCTRD)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_TPCTRD", ss}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPCTRD)); } } if (isITSTPCTRDused) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPCTRD", 0, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPCTRD)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPCTRD", 0}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPCTRD)); if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_ITSTPCTRD", 0, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPCTRD)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_ITSTPCTRD", 0}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPCTRD)); } } // TODO: TRD-matched tracks - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe}, mMatcher.getCalibVector()); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBDATA", 0}, mMatcher.getCalibVector()); if (mPushMatchable) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_0", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(0)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_1", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(1)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_2", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(2)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_3", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(3)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_4", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(4)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_5", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(5)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_6", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(6)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_7", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(7)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_8", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(8)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_9", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(9)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_10", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(10)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_11", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(11)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_12", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(12)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_13", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(13)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_14", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(14)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_15", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(15)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_16", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(16)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_17", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(17)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_0", 0}, mMatcher.getMatchedTracksPair(0)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_1", 0}, mMatcher.getMatchedTracksPair(1)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_2", 0}, mMatcher.getMatchedTracksPair(2)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_3", 0}, mMatcher.getMatchedTracksPair(3)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_4", 0}, mMatcher.getMatchedTracksPair(4)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_5", 0}, mMatcher.getMatchedTracksPair(5)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_6", 0}, mMatcher.getMatchedTracksPair(6)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_7", 0}, mMatcher.getMatchedTracksPair(7)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_8", 0}, mMatcher.getMatchedTracksPair(8)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_9", 0}, mMatcher.getMatchedTracksPair(9)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_10", 0}, mMatcher.getMatchedTracksPair(10)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_11", 0}, mMatcher.getMatchedTracksPair(11)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_12", 0}, mMatcher.getMatchedTracksPair(12)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_13", 0}, mMatcher.getMatchedTracksPair(13)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_14", 0}, mMatcher.getMatchedTracksPair(14)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_15", 0}, mMatcher.getMatchedTracksPair(15)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_16", 0}, mMatcher.getMatchedTracksPair(16)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_17", 0}, mMatcher.getMatchedTracksPair(17)); } mTimer.Stop(); } +void TOFMatcherSpec::storeConfigs(ProcessingContext& pc) +{ + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + const auto& conf = MatchTOFParams::Instance(); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, conf.getName()), conf.getName()); + TMap md; + md.SetOwnerKeyValue(); + md.Add(new TObjString(conf.getName().c_str()), new TObjString(o2::conf::ConfigurableParam::asJSON(conf.getName()).c_str())); + pc.outputs().snapshot(Output{"META", "TOFMATCHER", 0}, md); + } + } +} + void TOFMatcherSpec::endOfStream(EndOfStreamContext& ec) { LOGF(debug, "TOF matching total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable) +DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, bool requestCTPLumi, int nlanes) { uint32_t ss = o2::globaltracking::getSubSpec(strict ? o2::globaltracking::MatchingType::Strict : o2::globaltracking::MatchingType::Standard); + Options opts; auto dataRequest = std::make_shared(); if (strict) { dataRequest->setMatchingInputStrict(); } dataRequest->requestTracks(src, useMC); dataRequest->requestClusters(GID::getSourceMask(GID::TOF), useMC); + if (tpcRefit && src[GID::TPC]) { + dataRequest->requestClusters(GID::getSourceMask(GID::TPC), false); + } if (useFIT) { - dataRequest->requestClusters(GID::getSourceMask(GID::FT0), false); + dataRequest->requestFT0RecPoints(false); } auto ggRequest = std::make_shared(false, // orbitResetTime @@ -259,7 +269,10 @@ DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bo dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs); + dataRequest->inputs.emplace_back("corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe); + if (requestCTPLumi) { + dataRequest->inputs.emplace_back("lumiCTP", o2::header::gDataOriginCTP, "LUMICTP", 0, Lifetime::Timeframe); + } std::vector outputs; if (GID::includesSource(GID::TPC, src)) { outputs.emplace_back(o2::header::gDataOriginTOF, "MTC_TPC", ss, Lifetime::Timeframe); @@ -308,13 +321,14 @@ DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bo outputs.emplace_back(o2::header::gDataOriginTOF, "MATCHABLES_16", 0, Lifetime::Timeframe); outputs.emplace_back(o2::header::gDataOriginTOF, "MATCHABLES_17", 0, Lifetime::Timeframe); } + outputs.emplace_back("META", "TOFMATCHER", 0, Lifetime::Sporadic); return DataProcessorSpec{ "tof-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, useFIT, tpcRefit, strict, pushMatchable)}, - Options{}}; + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, useFIT, tpcRefit, strict, pushMatchable, nlanes, requestCTPLumi)}, + opts}; } } // namespace globaltracking diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index 1f1a296f448cb..7f63b61e02be0 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -12,13 +12,18 @@ /// @file TPCITSMatchingSpec.cxx #include - +#include +#include #include "GlobalTracking/MatchTPCITS.h" +#include "GlobalTracking/MatchTPCITSParams.h" +#include "FT0Reconstruction/InteractionTag.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "DataFormatsTPC/Constants.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "Framework/DataRefUtils.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" #include #include "TStopwatch.h" #include "Framework/ConfigParamRegistry.h" @@ -35,7 +40,9 @@ #include "DataFormatsTPC/WorkflowHelper.h" #include "DetectorsBase/GeometryManager.h" #include "DetectorsBase/Propagator.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DetectorsBase/GlobalParams.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" +#include "ITSBase/GeometryTGeo.h" #include "GlobalTracking/MatchTPCITSParams.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "DataFormatsParameters/GRPECSObject.h" @@ -45,7 +52,11 @@ #include "ITSMFTReconstruction/ClustererParam.h" #include "DetectorsBase/GRPGeomHelper.h" #include "TPCCalibration/VDriftHelper.h" -#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCFastTransformPOD.h" + +#ifdef ENABLE_UPGRADES +#include "ITS3Reconstruction/TopologyDictionary.h" +#endif using namespace o2::framework; using MCLabelsCl = o2::dataformats::MCTruthContainer; @@ -60,8 +71,9 @@ namespace globaltracking class TPCITSMatchingDPL : public Task { public: - TPCITSMatchingDPL(std::shared_ptr dr, std::shared_ptr gr, bool useFT0, bool calib, bool skipTPCOnly, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mUseFT0(useFT0), mCalibMode(calib), mSkipTPCOnly(skipTPCOnly), mUseMC(useMC) {} + TPCITSMatchingDPL(std::shared_ptr dr, std::shared_ptr gr, + bool useFT0, bool calib, bool skipTPCOnly, bool useMC, bool requestCTPLumi) + : mDataRequest(dr), mGGCCDBRequest(gr), mUseFT0(useFT0), mCalibMode(calib), mSkipTPCOnly(skipTPCOnly), mUseMC(useMC), mRequestCTPLumi(requestCTPLumi) {} ~TPCITSMatchingDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -70,15 +82,17 @@ class TPCITSMatchingDPL : public Task private: void updateTimeDependentParams(ProcessingContext& pc); + void storeConfigs(ProcessingContext& pc); std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; o2::tpc::VDriftHelper mTPCVDriftHelper{}; - o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps{}; o2::globaltracking::MatchTPCITS mMatching; // matching engine bool mUseFT0 = false; bool mCalibMode = false; bool mSkipTPCOnly = false; // to use only externally constrained tracks (for test only) bool mUseMC = true; + bool mRequestCTPLumi = false; TStopwatch mTimer; }; @@ -86,6 +100,9 @@ void TPCITSMatchingDPL::init(InitContext& ic) { mTimer.Stop(); mTimer.Reset(); + int lane = ic.services().get().inputTimesliceId; + int maxLanes = ic.services().get().maxInputTimeslices; + mMatching.setDebugTreeFileName(maxLanes == 1 ? "dbg_TPCITSmatch.root" : fmt::format("dbg_TPCITSmatch_{}.root", lane)); o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mMatching.setNThreads(std::max(1, ic.options().get("nthreads"))); mMatching.setUseBCFilling(!ic.options().get("ignore-bc-check")); @@ -98,28 +115,48 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + storeConfigs(pc); - mMatching.run(recoData); + static pmr::vector dummyMCLab, dummyMCLabAB; + static pmr::vector> dummyCalib; - pc.outputs().snapshot(Output{"GLO", "TPCITS", 0, Lifetime::Timeframe}, mMatching.getMatchedTracks()); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_REFS", 0, Lifetime::Timeframe}, mMatching.getABTrackletRefs()); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_CLID", 0, Lifetime::Timeframe}, mMatching.getABTrackletClusterIDs()); - if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "TPCITS_MC", 0, Lifetime::Timeframe}, mMatching.getMatchLabels()); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_MC", 0, Lifetime::Timeframe}, mMatching.getABTrackletLabels()); - } + auto& matchedTracks = pc.outputs().make>(Output{"GLO", "TPCITS", 0}); + auto& ABTrackletRefs = pc.outputs().make>(Output{"GLO", "TPCITSAB_REFS", 0}); + auto& ABTrackletClusterIDs = pc.outputs().make>(Output{"GLO", "TPCITSAB_CLID", 0}); + auto& matchLabels = mUseMC ? pc.outputs().make>(Output{"GLO", "TPCITS_MC", 0}) : dummyMCLab; + auto& ABTrackletLabels = mUseMC ? pc.outputs().make>(Output{"GLO", "TPCITSAB_MC", 0}) : dummyMCLabAB; + auto& calib = mCalibMode ? pc.outputs().make>>(Output{"GLO", "TPCITS_VDTGL", 0}) : dummyCalib; + + mMatching.run(recoData, matchedTracks, ABTrackletRefs, ABTrackletClusterIDs, matchLabels, ABTrackletLabels, calib); - if (mCalibMode) { - pc.outputs().snapshot(Output{"GLO", "TPCITS_VDTGL", 0, Lifetime::Timeframe}, mMatching.getTglITSTPC()); - } mTimer.Stop(); } +void TPCITSMatchingDPL::storeConfigs(ProcessingContext& pc) +{ + static bool first = true; + if (first) { + first = false; + const auto& confMatch = MatchTPCITSParams::Instance(); + const auto& confInt = ft0::InteractionTag::Instance(); + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, confMatch.getName()), confMatch.getName()); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, confInt.getName()), confInt.getName()); + TMap md; + md.SetOwnerKeyValue(); + md.Add(new TObjString(confMatch.getName().c_str()), new TObjString(o2::conf::ConfigurableParam::asJSON(confMatch.getName()).c_str())); + md.Add(new TObjString(confInt.getName().c_str()), new TObjString(o2::conf::ConfigurableParam::asJSON(confInt.getName()).c_str())); + pc.outputs().snapshot(Output{"META", "TPCITSMATCHER", 0}, md); + } + } +} + void TPCITSMatchingDPL::endOfStream(EndOfStreamContext& ec) { mMatching.end(); LOGF(info, "TPC-ITS matching total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + mMatching.reportTiming(); } void TPCITSMatchingDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -130,11 +167,12 @@ void TPCITSMatchingDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { + if (matcher == ConcreteDataMatcher("GLO", "ITSTPCPARAM", 0)) { + LOG(info) << "ITS-TPC Matching params updated from ccdb"; return; } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated"; + LOG(info) << "its cluster dictionary updated"; mMatching.setITSDictionary((const o2::itsmft::TopologyDictionary*)obj); return; } @@ -144,16 +182,32 @@ void TPCITSMatchingDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) par.printKeyValues(); return; } + if (matcher == ConcreteDataMatcher("ITS", "GEOMTGEO", 0)) { + LOG(info) << "ITS GeometryTGeo loaded from ccdb"; + o2::its::GeometryTGeo::adopt((o2::its::GeometryTGeo*)obj); + return; + } +#ifdef ENABLE_UPGRADES + if (matcher == ConcreteDataMatcher("IT3", "CLUSDICT", 0)) { + LOG(info) << "it3 cluster dictionary updated"; + mMatching.setIT3Dictionary((const o2::its3::TopologyDictionary*)obj); + return; + } +#endif } void TPCITSMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTPCVDriftHelper.extractCCDBInputs(pc); - o2::tpc::CorrectionMapsLoader::extractCCDBInputs(pc); + auto const& raw = pc.inputs().get("corrMap"); + mTPCCorrMaps = &o2::gpu::TPCFastTransformPOD::get(raw); + float lumiCTP = mRequestCTPLumi ? pc.inputs().get("lumiCTP") : 0; + static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; + pc.inputs().get("MatchParam"); // Note: ITS/CLUSDICT and ITS/ALPIDEPARAM are requested/loaded by the recocontainer const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); @@ -165,6 +219,7 @@ void TPCITSMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) mMatching.setITSTimeBiasInBC(alpParams.roFrameBiasInBC); mMatching.setSkipTPCOnly(mSkipTPCOnly); mMatching.setITSTriggered(!o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS)); + mMatching.setNHBPerTF(o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF()); mMatching.setMCTruthOn(mUseMC); mMatching.setUseFT0(mUseFT0); mMatching.setVDriftCalib(mCalibMode); @@ -173,40 +228,49 @@ void TPCITSMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) } else { mMatching.setCosmics(true); } + if (pc.inputs().getPos("itsTGeo") >= 0) { + pc.inputs().get("itsTGeo"); + } mMatching.init(); + // check consistency of material correction options + if (mMatching.getUseMatCorrFlag() == o2::base::Propagator::MatCorrType::USEMatCorrTGeo && !o2::base::GeometryManager::isGeometryLoaded()) { + LOGP(fatal, "USEMatCorrTGeo cannot work w/o full geometry request in the GRPGeomHelper"); + } } - // we may have other params which need to be queried regularly - bool updateMaps = false; - if (mTPCCorrMapsLoader.isUpdated()) { - mMatching.setTPCCorrMaps(&mTPCCorrMapsLoader); - mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; - } + + mMatching.setTPCCorrMaps(mTPCCorrMaps, lumiCTP); + if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + LOGP(info, "Updating TPC VDrift: corrFact {} wrt refVDrift {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, mTPCVDriftHelper.getSourceName()); mMatching.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } -DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useMC) +DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC, bool requestCTPLumi) { std::vector outputs; auto dataRequest = std::make_shared(); if ((src & GTrackID::getSourcesMask("TPC-TRD,TPC-TOF,TPC-TRD-TOF")).any()) { // preliminary stage of extended workflow ? dataRequest->setMatchingInputStrict(); } + dataRequest->inputs.emplace_back("MatchParam", "GLO", "ITSTPCPARAM", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/ITSTPCParam")); dataRequest->requestTracks(src, useMC); dataRequest->requestTPCClusters(false); - dataRequest->requestITSClusters(useMC); // Only ITS clusters labels are needed for the afterburner +// Only ITS clusters labels are needed for the afterburner and request possibly for ITS3 +#ifdef ENABLE_UPGRADES + if (o2::GlobalParams::Instance().withITS3) { + dataRequest->requestIT3Clusters(useMC); + } else { + dataRequest->requestITSClusters(useMC); + } +#else + dataRequest->requestITSClusters(useMC); +#endif if (useFT0) { dataRequest->requestFT0RecPoints(false); } @@ -224,26 +288,37 @@ DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool } // Note: ITS/CLUSDICT and ITS/ALPIDEPARAM are requested/loaded by the recocontainer - auto ggRequest = std::make_shared(true, // orbitResetTime - true, // GRPECS=true - true, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - o2::base::GRPGeomRequest::Aligned, // geometry + auto ggRequest = std::make_shared(true, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // geometry dataRequest->inputs, true); // query only once all objects except mag.field + if (!useGeom) { // load ITS GeomTGeo since the full geometry is not loaded + ggRequest->addInput({"itsTGeo", "ITS", "GEOMTGEO", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/Geometry")}, dataRequest->inputs); + } + + Options opts{ + {"nthreads", VariantType::Int, 1, {"Number of afterburner threads"}}, + {"ignore-bc-check", VariantType::Bool, false, {"Do not check match candidate against BC filling"}}, + {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}; + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs); + dataRequest->inputs.emplace_back("corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe); + if (requestCTPLumi) { + dataRequest->inputs.emplace_back("lumiCTP", o2::header::gDataOriginCTP, "LUMICTP", 0, Lifetime::Timeframe); + } + + outputs.emplace_back("META", "TPCITSMATCHER", 0, Lifetime::Sporadic); return DataProcessorSpec{ "itstpc-track-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useFT0, calib, skipTPCOnly, useMC)}, - Options{ - {"nthreads", VariantType::Int, 1, {"Number of afterburner threads"}}, - {"ignore-bc-check", VariantType::Bool, false, {"Do not check match candidate against BC filling"}}, - {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}}; + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useFT0, calib, skipTPCOnly, useMC, requestCTPLumi)}, + opts}; } } // namespace globaltracking diff --git a/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx index 281fd06dea7eb..c08d3ab9f4c53 100644 --- a/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx @@ -23,7 +23,7 @@ #include "TPCBase/ParameterElectronics.h" #include "TPCBase/ParameterDetector.h" #include "TPCCalibration/VDriftHelper.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "TStopwatch.h" using namespace o2::framework; @@ -78,8 +78,8 @@ void VertexTrackMatcherSpec::run(ProcessingContext& pc) mMatcher.process(recoData, trackIndex, vtxRefs); - pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe}, trackIndex); - pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTCREFS", 0, Lifetime::Timeframe}, vtxRefs); + pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTC", 0}, trackIndex); + pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTCREFS", 0}, vtxRefs); mTimer.Stop(); LOG(info) << "Made " << trackIndex.size() << " track associations for " << recoData.getPrimaryVertices().size() @@ -98,6 +98,7 @@ void VertexTrackMatcherSpec::updateTimeDependentParams(ProcessingContext& pc) mMatcher.setITSROFrameLengthMUS(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS) ? alpParamsITS.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingMUS : alpParamsITS.roFrameLengthTrig * 1.e-3); const auto& alpParamsMFT = o2::itsmft::DPLAlpideParam::Instance(); mMatcher.setMFTROFrameLengthMUS(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::MFT) ? alpParamsMFT.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingMUS : alpParamsMFT.roFrameLengthTrig * 1.e-3); + mMatcher.init(); LOGP(info, "VertexTrackMatcher ITSROFrameLengthMUS:{} MFTROFrameLengthMUS:{}", mMatcher.getITSROFrameLengthMUS(), mMatcher.getMFTROFrameLengthMUS()); } // we may have other params which need to be queried regularly @@ -150,7 +151,7 @@ DataProcessorSpec getVertexTrackMatcherSpec(GTrackID::mask_t src) dataRequest->requestTracks(src, false); dataRequest->requestClusters(src & GTrackID::getSourcesMask("EMC,PHS,CPV"), false); - dataRequest->requestPrimaryVerterticesTMP(false); + dataRequest->requestPrimaryVerticesTMP(false); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true diff --git a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx index d3f7aafe202e5..6148894fcbb8a 100644 --- a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx @@ -17,6 +17,7 @@ #include "ITSMFTWorkflow/ClusterReaderSpec.h" #include "TPCReaderWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/ClusterReaderSpec.h" +#include "TPCWorkflow/TPCScalerSpec.h" #include "TPCWorkflow/ClusterSharingMapSpec.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" #include "TOFWorkflowIO/TOFMatchedReaderSpec.h" @@ -30,6 +31,8 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" +#include "TPCCalibration/CorrectionMapsOptions.h" using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -48,8 +51,11 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, + {"use-pv-info", o2::framework::VariantType::Bool, false, {"request primary vertex for relevant cuts in the collision/cosmics interleaved data"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); + o2::tpc::CorrectionMapsOptions::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -74,21 +80,42 @@ void customize(std::vector& policies) WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { WorkflowSpec specs; - GID::mask_t alowedSources = GID::getSourcesMask("ITS,TPC,ITS-TPC,TPC-TOF,ITS-TPC-TOF"); + GID::mask_t alowedSources = GID::getSourcesMask("ITS,TPC,ITS-TPC,TPC-TRD,TPC-TOF,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2match-cosmics-workflow_configuration.ini"); - + auto sclOpt = o2::tpc::CorrectionMapsOptions::parseGlobalOptions(configcontext.options()); auto useMC = !configcontext.options().get("disable-mc"); auto disableRootOut = configcontext.options().get("disable-root-output"); GID::mask_t src = alowedSources & GID::getSourcesMask(configcontext.options().get("track-sources")); + if (GID::includesDet(DetID::TPC, src)) { + src |= GID::getSourceMask(GID::TPC); + } + if (GID::includesDet(DetID::TRD, src)) { + src |= GID::getSourceMask(GID::TRD); + } + if (GID::includesDet(DetID::TOF, src)) { + src |= GID::getSourceMask(GID::TOF); + } + if (sclOpt.requestCTPLumi) { + src = src | GID::getSourcesMask("CTP"); + } + + GID::mask_t srcCl = src; GID::mask_t dummy; - specs.emplace_back(o2::globaltracking::getCosmicsMatchingSpec(src, useMC)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); + } + bool usePV = configcontext.options().get("use-pv-info"); + specs.emplace_back(o2::globaltracking::getCosmicsMatchingSpec(src, usePV, useMC)); o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, src, src, src, useMC, dummy); // clusters MC is not needed + if (usePV) { + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed + } if (!disableRootOut) { specs.emplace_back(o2::globaltracking::getTrackCosmicsWriterSpec(useMC)); diff --git a/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx index 13a842130e5d1..fd90aff5f32ff 100644 --- a/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx @@ -21,6 +21,7 @@ #include "GlobalTrackingWorkflow/MatchedMFTMCHWriterSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "GlobalTracking/MatchGlobalFwdParam.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -46,6 +47,7 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"do not write output root files"}}, {"enable-match-output", o2::framework::VariantType::Bool, false, {"stores mftmch matching info on mftmchmatches.root"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addMFTConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/src/hmp-matcher-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/hmp-matcher-workflow.cxx new file mode 100644 index 0000000000000..2d748ae3cffb3 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/hmp-matcher-workflow.cxx @@ -0,0 +1,144 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "TPCReaderWorkflow/TPCSectorCompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "ITSWorkflow/TrackReaderSpec.h" +#include "TPCReaderWorkflow/TrackReaderSpec.h" + +#include "GlobalTrackingWorkflow/HMPMatcherSpec.h" +#include "HMPIDWorkflow/HMPMatchedWriterSpec.h" + +#include "HMPIDWorkflow/ClustersReaderSpec.h" + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" +#include "Algorithm/RangeTokenizer.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +// #include "TOFBase/Utils.h" +#include "Steer/MCKinematicsReader.h" +#include "TSystem.h" +#include "DetectorsBase/DPLWorkflowUtils.h" + +#include "GlobalTracking/MatchHMP.h" + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using GID = o2::dataformats::GlobalTrackID; +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& policies) +{ + // ordered policies for the writers + policies.push_back(CompletionPolicyHelpers::consumeWhenAllOrdered(".*(?:HMP|hmp).*[W,w]riter.*")); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, + {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use: allowed ITS-TPC,TPC-TRD,TPC-TOF,TPC-TRD-TOF,ITS-TPC-TRD,ITS-TPC-TRD-TOF (all)"}}, + {"trd-extra-tolerance", o2::framework::VariantType::Float, 0.0f, {"Extra time tolerance for TRD tracks in microsec"}}, + {"tof-extra-tolerance", o2::framework::VariantType::Float, 0.0f, {"Extra time tolerance for TRD tracks in microsec"}}, + {"combine-devices", o2::framework::VariantType::Bool, false, {"merge DPL source/writer devices"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + // write the configuration used for the workflow + + auto useMC = !configcontext.options().get("disable-mc"); + auto extratolerancetrd = configcontext.options().get("trd-extra-tolerance"); + auto extratolerancetof = configcontext.options().get("tof-extra-tolerance"); + auto disableRootIn = configcontext.options().get("disable-root-input"); + auto disableRootOut = configcontext.options().get("disable-root-output"); + + bool writematching = 0; + bool writecalib = 0; + + LOG(debug) << "HMP MATCHER WORKFLOW configuration"; + LOG(debug) << "HMP disable-mc = " << configcontext.options().get("disable-mc"); + LOG(debug) << "HMP disable-root-input = " << disableRootIn; + LOG(debug) << "HMP disable-root-output = " << disableRootOut; + + GID::mask_t alowedSources = GID::getSourcesMask("ITS-TPC,TPC-TRD,TPC-TOF,ITS-TPC-TRD,ITS-TPC-TOF,TPC-TRD-TOF,ITS-TPC-TRD-TOF"); + + GID::mask_t src = alowedSources & GID::getSourcesMask(configcontext.options().get("track-sources")); + + GID::mask_t mcmaskcl; + GID::mask_t nonemask = GID::getSourcesMask(GID::NONE); + GID::mask_t clustermask = GID::getSourcesMask("HMP"); + + if (useMC) { + mcmaskcl |= GID::getSourceMask(GID::HMP); + } + + WorkflowSpec inputspecs; + o2::globaltracking::InputHelper::addInputSpecs(configcontext, inputspecs, clustermask, src, src, useMC, mcmaskcl, GID::getSourcesMask(GID::ALL)); + if (configcontext.options().get("combine-devices")) { + std::vector unmerged; + specs.push_back(specCombiner("HMP-readers", inputspecs, unmerged)); + if (unmerged.size() > 0) { + LOG(fatal) << "Unexpected DPL device merge problem"; + } + } else { + for (auto& s : inputspecs) { + specs.push_back(s); + } + } + + specs.emplace_back(o2::globaltracking::getHMPMatcherSpec(src, useMC, extratolerancetrd, extratolerancetof)); + + if (!disableRootOut) { + std::vector writers; + writers.emplace_back(o2::hmpid::getHMPMatchedWriterSpec(useMC, "o2match_hmp.root")); //, false, (int)o2::globaltracking::MatchHMP::trackType::CONSTR, false)); + + if (configcontext.options().get("combine-devices")) { + std::vector unmerged; + specs.push_back(specCombiner("HMP-writers", writers, unmerged)); + if (unmerged.size() > 0) { + LOG(fatal) << "Unexpected DPL device merge problem"; + } + } else { + for (auto& s : writers) { + specs.push_back(s); + } + } + } + + // configure dpl timer to inject correct firstTFOrbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx index 0fb10c9530200..d0a0b829c03bc 100644 --- a/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx @@ -57,6 +57,7 @@ void customize(std::vector& workflowOptions) {"skip", VariantType::Bool, false, {"pass-through mode (skip vertexing)"}}, {"validate-with-ft0", o2::framework::VariantType::Bool, false, {"use FT0 time for vertex validation"}}, {"vertex-track-matching-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use in vertex-track associations or \"none\" to disable matching"}}, + {"use-full-geometry", o2::framework::VariantType::Bool, false, {"load full geometry"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); @@ -83,6 +84,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto disableRootOut = configcontext.options().get("disable-root-output"); auto validateWithFT0 = configcontext.options().get("validate-with-ft0"); auto skip = configcontext.options().get("skip"); + auto useGeom = configcontext.options().get("use-full-geometry"); GID::mask_t srcPV = allowedSourcesPV & GID::getSourcesMask(configcontext.options().get("vertexing-sources")); GID::mask_t srcVT = allowedSourcesVT & GID::getSourcesMask(configcontext.options().get("vertex-track-matching-sources")); @@ -92,7 +94,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) GID::mask_t srcComb = srcPV | srcVT; GID::mask_t dummy, srcClus = GID::includesDet(DetID::TOF, srcComb) ? GID::getSourceMask(GID::TOF) : dummy; - specs.emplace_back(o2::vertexing::getPrimaryVertexingSpec(srcPV, skip, validateWithFT0, useMC)); + specs.emplace_back(o2::vertexing::getPrimaryVertexingSpec(srcPV, skip, validateWithFT0, useMC, useGeom)); specs.emplace_back(o2::vertexing::getVertexTrackMatcherSpec(srcVT)); auto srcMtc = srcComb; diff --git a/Detectors/GlobalTrackingWorkflow/src/reader-driver-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/reader-driver-workflow.cxx new file mode 100644 index 0000000000000..5deabc8e70344 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/reader-driver-workflow.cxx @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/ChannelSpecHelpers.h" +#include "GlobalTrackingWorkflow/ReaderDriverSpec.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"timeframes-shm-limit", VariantType::String, "0", {"Minimum amount of SHM required in order to publish data"}}, + {"metric-feedback-channel-format", VariantType::String, "name=metric-feedback,type=pull,method=connect,address=ipc://{}metric-feedback-{},transport=shmem,rateLogging=0", {"format for the metric-feedback channel for TF rate limiting"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root,upstream"); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + int rateLimitingIPCID = std::stoi(configcontext.options().get("timeframes-rate-limit-ipcid")); + std::string chanFmt = configcontext.options().get("metric-feedback-channel-format"); + std::string metricChannel{}; + if (rateLimitingIPCID > -1 && !chanFmt.empty()) { + metricChannel = fmt::format(fmt::runtime(chanFmt), o2::framework::ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); + } + size_t minSHM = std::stoul(configcontext.options().get("timeframes-shm-limit")); + + WorkflowSpec specs; + specs.emplace_back(o2::globaltracking::getReaderDriverSpec(metricChannel, minSHM)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx index cb851b8733404..e630a8dad72dd 100644 --- a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx @@ -11,11 +11,13 @@ #include "GlobalTrackingWorkflow/SecondaryVertexingSpec.h" #include "GlobalTrackingWorkflow/SecondaryVertexWriterSpec.h" +#include "GlobalTrackingWorkflow/StrangenessTrackingWriterSpec.h" #include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" #include "GlobalTrackingWorkflowReaders/PrimaryVertexReaderSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "ITSWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/TrackReaderSpec.h" +#include "TPCWorkflow/TPCScalerSpec.h" #include "TOFWorkflowIO/TOFMatchedReaderSpec.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -27,6 +29,8 @@ #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" #include "DetectorsBase/DPLWorkflowUtils.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" +#include "TPCCalibration/CorrectionMapsOptions.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -50,11 +54,17 @@ void customize(std::vector& workflowOptions) std::vector options{ {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC (relevant for strangeness tracker only))"}}, {"vertexing-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use in vertexing"}}, {"disable-cascade-finder", o2::framework::VariantType::Bool, false, {"do not run cascade finder"}}, - {"enable-3body-finder", o2::framework::VariantType::Bool, false, {"run 3 body finder"}}, + {"disable-3body-finder", o2::framework::VariantType::Bool, false, {"do not run 3 body finder"}}, + {"disable-strangeness-tracker", o2::framework::VariantType::Bool, false, {"do not run strangeness tracker"}}, + {"disable-ccdb-params", o2::framework::VariantType::Bool, false, {"do not load the svertexer parameters from the ccdb"}}, + {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); + o2::tpc::CorrectionMapsOptions::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -71,17 +81,30 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2secondary-vertexing-workflow_configuration.ini"); - bool useMC = false; + bool useMC = !configcontext.options().get("disable-mc"); auto disableRootOut = configcontext.options().get("disable-root-output"); + auto enableCCDBParams = !configcontext.options().get("disable-ccdb-params"); auto enableCasc = !configcontext.options().get("disable-cascade-finder"); - auto enable3body = configcontext.options().get("enable-3body-finder"); - + auto enable3body = !configcontext.options().get("disable-3body-finder"); + auto enableStrTr = !configcontext.options().get("disable-strangeness-tracker"); + auto useGeom = configcontext.options().get("use-full-geometry"); + auto sclOpt = o2::tpc::CorrectionMapsOptions::parseGlobalOptions(configcontext.options()); GID::mask_t src = allowedSources & GID::getSourcesMask(configcontext.options().get("vertexing-sources")); GID::mask_t dummy, srcClus = GID::includesDet(DetID::TOF, src) ? GID::getSourceMask(GID::TOF) : dummy; // eventually, TPC clusters will be needed for refit - + if (enableStrTr) { + srcClus |= GID::getSourceMask(GID::ITS); + } + if (src[GID::TPC]) { + srcClus |= GID::getSourceMask(GID::TPC); + if (sclOpt.requestCTPLumi) { + src = src | GID::getSourcesMask("CTP"); + } + } WorkflowSpec specs; - - specs.emplace_back(o2::vertexing::getSecondaryVertexingSpec(src, enableCasc, enable3body)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); + } + specs.emplace_back(o2::vertexing::getSecondaryVertexingSpec(src, enableCasc, enable3body, enableStrTr, enableCCDBParams, useMC, useGeom)); // only TOF clusters are needed if TOF is involved, no clusters MC needed WorkflowSpec inputspecs; @@ -106,6 +129,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (!disableRootOut) { specs.emplace_back(o2::vertexing::getSecondaryVertexWriterSpec()); + if (enableStrTr) { + specs.emplace_back(o2::strangeness_tracking::getStrangenessTrackingWriterSpec(useMC)); + } } // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit diff --git a/Detectors/StrangenessTracking/workflow/src/strangeness-tracking-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx similarity index 81% rename from Detectors/StrangenessTracking/workflow/src/strangeness-tracking-workflow.cxx rename to Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx index e0b892e51e42f..8c42871ac05bf 100644 --- a/Detectors/StrangenessTracking/workflow/src/strangeness-tracking-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx @@ -9,8 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "StrangenessTrackingWorkflow/StrangenessTrackingSpec.h" -#include "StrangenessTrackingWorkflow/StrangenessTrackingWriterSpec.h" +#include "GlobalTrackingWorkflow/StrangenessTrackingSpec.h" +#include "GlobalTrackingWorkflow/StrangenessTrackingWriterSpec.h" #include "CommonUtils/ConfigurableParam.h" #include "StrangenessTracking/StrangenessTrackingConfigParam.h" @@ -21,6 +21,7 @@ #include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" #include "GlobalTrackingWorkflowReaders/PrimaryVertexReaderSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -42,8 +43,11 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, + {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC"}}, + {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -57,19 +61,23 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); - auto useRootInput = !configcontext.options().get("disable-root-input"); + auto disableRootOut = configcontext.options().get("disable-root-output"); + auto useGeom = configcontext.options().get("use-full-geometry"); o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); o2::conf::ConfigurableParam::writeINI("o2strangeness_tracking_workflow_configuration.ini"); GID::mask_t itsSource = GID::getSourceMask(GID::ITS); // ITS tracks and clusters WorkflowSpec specs; - specs.emplace_back(o2::strangeness_tracking::getStrangenessTrackerSpec(itsSource)); + specs.emplace_back(o2::strangeness_tracking::getStrangenessTrackerSpec(itsSource, useMC, useGeom)); o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, itsSource, itsSource, itsSource, useMC, itsSource); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); // S-vertex is always needed - specs.emplace_back(getStrangenessTrackingWriterSpec()); + + if (!disableRootOut) { + specs.emplace_back(getStrangenessTrackingWriterSpec(useMC)); + } // configure dpl timer to inject correct firstTFOrbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx index 624e2602fda42..8843570fc3b52 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx @@ -26,9 +26,7 @@ void customize(std::vector& policies) void customize(std::vector& workflowOptions) { // option allowing to set parameters - std::vector options{ - ConfigParamSpec{"dataspec", VariantType::String, "tfidinfo:FLP/DISTSUBTIMEFRAME/0xccdb", {"spec from which the TFIDInfo will be extracted"}}, - ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + std::vector options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; std::swap(workflowOptions, options); } @@ -37,7 +35,10 @@ void customize(std::vector& workflowOptions) #include "Framework/DataProcessingHeader.h" #include "Framework/Task.h" #include "CommonDataFormat/TFIDInfo.h" +#include "CommonUtils/TreeStreamRedirector.h" #include "CommonUtils/NameConf.h" +#include "CommonConstants/LHCConstants.h" +#include "Framework/CCDBParamSpec.h" #include #include @@ -53,17 +54,31 @@ class TFIDInfoWriter : public o2::framework::Task void run(o2::framework::ProcessingContext& pc) final { + const auto& tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged) { // new run is starting + auto v = pc.inputs().get*>("orbitReset"); + mOrbitReset = (*v)[0]; + } o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mData.emplace_back()); } void endOfStream(EndOfStreamContext& ec) final { + o2::utils::TreeStreamRedirector pcstream; TFile fl(mOutFileName.c_str(), "recreate"); fl.WriteObjectAny(&mData, "std::vector", "tfidinfo"); - LOGP(info, "Wrote TFIDInfo vector with {} entries to {}", mData.size(), fl.GetName()); + pcstream.SetFile(&fl); + for (const auto& info : mData) { + long ts = (mOrbitReset + long(info.firstTForbit * o2::constants::lhc::LHCOrbitMUS)) / 1000; + pcstream << "tfidTree" + << "tfidinfo=" << info << "ts=" << ts << "\n"; + } + pcstream.Close(); + LOGP(info, "Wrote tfidinfo vector and tfidTree with {} entries to {}", mData.size(), fl.GetName()); } private: + long mOrbitReset = 0; std::string mOutFileName{}; std::vector mData; }; @@ -72,8 +87,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { WorkflowSpec wf; o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(DataProcessorSpec{"tfid-info-writer", o2::framework::select(cfgc.options().get("dataspec").c_str()), - std::vector{}, AlgorithmSpec{adaptFromTask()}, + wf.emplace_back(DataProcessorSpec{"tfid-info-writer", + {{"orbitReset", "CTP", "ORBITRESET", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/OrbitReset")}}, + std::vector{}, + AlgorithmSpec{adaptFromTask()}, Options{{"tfidinfo-file-name", VariantType::String, o2::base::NameConf::getTFIDInfoFileName(), {"output file for TFIDInfo"}}}}); return wf; } diff --git a/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx index 909692e214780..96d7c783022c3 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx @@ -32,6 +32,8 @@ #include "Steer/MCKinematicsReader.h" #include "TSystem.h" #include "DetectorsBase/DPLWorkflowUtils.h" +#include "TPCCalibration/CorrectionMapsOptions.h" +#include "TPCWorkflow/TPCScalerSpec.h" using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -54,6 +56,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"tof-lanes", o2::framework::VariantType::Int, 1, {"number of parallel lanes up to the matcher"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use: allowed TPC,ITS-TPC,TPC-TRD,ITS-TPC-TRD (all)"}}, @@ -63,9 +66,11 @@ void customize(std::vector& workflowOptions) {"output-type", o2::framework::VariantType::String, "matching-info", {"matching-info, calib-info"}}, {"enable-dia", o2::framework::VariantType::Bool, false, {"to require diagnostic freq and then write to calib outputs (obsolete since now default)"}}, {"trd-extra-tolerance", o2::framework::VariantType::Float, 500.0f, {"Extra time tolerance for TRD tracks in ns"}}, + {"refit-tpc-tof", o2::framework::VariantType::Bool, false, {"Refit unconstrained TPC tracks matched to TOF (if false - just move)"}}, {"write-matchable", o2::framework::VariantType::Bool, false, {"write all matchable pairs in a file (o2matchable_tof.root)"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-devices", o2::framework::VariantType::Bool, false, {"merge DPL source/writer devices"}}}; + o2::tpc::CorrectionMapsOptions::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -92,10 +97,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto diagnostic = configcontext.options().get("enable-dia"); auto extratolerancetrd = configcontext.options().get("trd-extra-tolerance"); auto writeMatchable = configcontext.options().get("write-matchable"); - + auto sclOpt = o2::tpc::CorrectionMapsOptions::parseGlobalOptions(configcontext.options()); bool writematching = 0; bool writecalib = 0; + bool refitTPCTOF = configcontext.options().get("refit-tpc-tof"); auto outputType = configcontext.options().get("output-type"); + auto nLanes = configcontext.options().get("tof-lanes"); if (outputType.rfind("matching-info") < outputType.size()) { writematching = 1; } @@ -107,9 +114,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } } - if (!writecalib) { - useFIT = false; - } + // if (!writecalib) { + // useFIT = false; + // } LOG(debug) << "TOF MATCHER WORKFLOW configuration"; LOG(debug) << "TOF track inputs = " << configcontext.options().get("track-sources"); @@ -122,6 +129,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) LOG(debug) << "TOF matching in strict mode = " << strict; LOG(debug) << "TOF extra time tolerance for TRD tracks = " << extratolerancetrd; LOG(debug) << "Store all matchables = " << writeMatchable; + LOG(debug) << "TOF Nlanes for matcher = " << nLanes; //GID::mask_t alowedSources = GID::getSourcesMask("TPC,ITS-TPC"); GID::mask_t alowedSources = GID::getSourcesMask("TPC,ITS-TPC,TPC-TRD,ITS-TPC-TRD"); @@ -137,7 +145,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (useFIT) { clustermask |= GID::getSourceMask(GID::FT0); } - + if (src[GID::TPC] && refitTPCTOF) { // load clusters + clustermask |= GID::getSourceMask(GID::TPC); + } + if (sclOpt.requestCTPLumi) { + src = src | GID::getSourcesMask("CTP"); + } if (useMC) { mcmaskcl |= GID::getSourceMask(GID::TOF); } @@ -155,8 +168,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) specs.push_back(s); } } - - specs.emplace_back(o2::globaltracking::getTOFMatcherSpec(src, useMC, useFIT, false, strict, extratolerancetrd, writeMatchable)); // doTPCrefit not yet supported (need to load TPC clusters?) + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); + } + specs.emplace_back(o2::globaltracking::getTOFMatcherSpec(src, useMC, useFIT, refitTPCTOF, strict, extratolerancetrd, writeMatchable, sclOpt.requestCTPLumi, nLanes)); // doTPCrefit not yet supported (need to load TPC clusters?) if (!disableRootOut) { std::vector writers; diff --git a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx index 09a3c3e865ed0..78e5db9e4b391 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "TPCWorkflow/ClusterSharingMapSpec.h" +#include "TPCWorkflow/TPCScalerSpec.h" #include "GlobalTrackingWorkflow/TPCITSMatchingSpec.h" #include "GlobalTrackingWorkflow/TrackWriterTPCITSSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" @@ -23,6 +24,8 @@ #include "Framework/CallbacksPolicy.h" #include "Framework/ConfigContext.h" #include "Framework/CompletionPolicyHelpers.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" +#include "TPCCalibration/CorrectionMapsOptions.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -42,7 +45,10 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"track-sources", VariantType::String, "TPC", {"comma-separated list of sources to use: TPC,TPC-TOF,TPC-TRD,TPC-TRD-TOF"}}, {"produce-calibration-data", o2::framework::VariantType::Bool, false, {"produce output for TPC vdrift calibration"}}, + {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); + o2::tpc::CorrectionMapsOptions::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -69,20 +75,28 @@ WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& configcont o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2matchtpcits-workflow_configuration.ini"); - GID::mask_t alowedSources = GID::getSourcesMask("ITS,TPC,TPC-TOF"); GID::mask_t src = alowedSources & GID::getSourcesMask(configcontext.options().get("track-sources")); bool needStrictTRDTOF = (src & GID::getSourcesMask("TPC-TRD,TPC-TOF,TPC-TRD-TOF")).any(); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); // RS at the moment is not passed to the matching w-flow + auto sclOpt = o2::tpc::CorrectionMapsOptions::parseGlobalOptions(configcontext.options()); + auto useGeom = configcontext.options().get("use-full-geometry"); auto useFT0 = configcontext.options().get("use-ft0"); if (useFT0) { src |= GID::getSourceMask(GID::FT0); } auto useMC = !configcontext.options().get("disable-mc"); auto calib = configcontext.options().get("produce-calibration-data"); - auto srcL = src | GID::getSourcesMask("ITS,TPC"); // ITS is neadded always, TPC must be loaded even if bare TPC tracks are not used in matching + auto srcL = src | GID::getSourcesMask("ITS,TPC"); // ITS is needed always, TPC must be loaded even if bare TPC tracks are not used in matching + if (sclOpt.requestCTPLumi) { + srcL = srcL | GID::getSourcesMask("CTP"); + } o2::framework::WorkflowSpec specs; - specs.emplace_back(o2::globaltracking::getTPCITSMatchingSpec(srcL, useFT0, calib, !GID::includesSource(GID::TPC, src), useMC)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); + } + specs.emplace_back(o2::globaltracking::getTPCITSMatchingSpec(srcL, useFT0, calib, !GID::includesSource(GID::TPC, src), useGeom, useMC, sclOpt.requestCTPLumi)); if (!configcontext.options().get("disable-root-output")) { specs.emplace_back(o2::globaltracking::getTrackWriterTPCITSSpec(useMC)); diff --git a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt index 0aa22985441c9..4ee9f2f314a08 100644 --- a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt @@ -9,18 +9,48 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +#add_compile_options(-O0 -g -fPIC) + o2_add_library(GlobalTrackingStudy + TARGETVARNAME targetName SOURCES src/TPCTrackStudy.cxx - SOURCES src/TrackingStudy.cxx + src/TrackingStudy.cxx + src/SVStudy.cxx + src/TrackMCStudy.cxx + src/TPCDataFilter.cxx + src/ITSOffsStudy.cxx + src/DumpTracks.cxx + src/V0Ext.cxx + src/TrackInfoExt.cxx + src/TrackMCStudyConfig.cxx + src/TrackMCStudyTypes.cxx + src/TPCClusSelector.cxx + src/CheckResidSpec.cxx + src/CheckResidConfig.cxx + src/HistoManager.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTracking O2::GlobalTrackingWorkflowReaders O2::GlobalTrackingWorkflowHelpers O2::DataFormatsGlobalTracking + O2::DetectorsVertexing + O2::TPCWorkflow O2::SimulationDataFormat) o2_target_root_dictionary(GlobalTrackingStudy - HEADERS include/GlobalTrackingStudy/TPCTrackStudy.h - include/GlobalTrackingStudy/TrackingStudy.h) + HEADERS include/GlobalTrackingStudy/V0Ext.h + include/GlobalTrackingStudy/TrackInfoExt.h + include/GlobalTrackingStudy/TrackMCStudyConfig.h + include/GlobalTrackingStudy/TrackMCStudyTypes.h + include/GlobalTrackingStudy/CheckResidTypes.h + include/GlobalTrackingStudy/CheckResidConfig.h + include/GlobalTrackingStudy/HistoManager.h + LINKDEF src/GlobalTrackingStudyLinkDef.h +) + +o2_add_executable(study-workflow + COMPONENT_NAME sv + SOURCES src/sv-study-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) o2_add_executable(study-workflow COMPONENT_NAME tpc-track @@ -31,3 +61,33 @@ o2_add_executable(study-workflow COMPONENT_NAME tracking SOURCES src/tracking-study-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + +o2_add_executable(study-workflow + COMPONENT_NAME trackMC + SOURCES src/trackMCStudy-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + +o2_add_executable(filter-workflow + COMPONENT_NAME tpc-data + SOURCES src/tpc-data-filter-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + +o2_add_executable(study-workflow + COMPONENT_NAME its-offset + SOURCES src/its-offset-study-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + +o2_add_executable(dump-workfow + COMPONENT_NAME bc-tracks + SOURCES src/track-dump-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + +o2_add_executable(resid-workfow + COMPONENT_NAME check + SOURCES src/check-resid-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + +if (OpenMP_CXX_FOUND) + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) + target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) +endif() diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h new file mode 100644 index 0000000000000..c1b5291515af0 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h @@ -0,0 +1,67 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_CHECK_RESID_CONFIG_H +#define O2_CHECK_RESID_CONFIG_H +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::checkresid +{ +struct CheckResidConfig : o2::conf::ConfigurableParamHelper { + int minPVContributors = 10; + int minITSCl = 7; + float minPt = 0.4f; + float maxPt = 50.f; + float maxTgl = 2.f; + float rCompIBOB = 12.f; + + bool pvcontribOnly = true; + bool addPVAsCluster = true; + bool useStableRef = true; + bool doIBOB = true; + bool doResid = true; + bool useITSGloContributors = false; + + bool refitPV = true; + float refitPVMV = false; + float refitPVIniScale = 100.f; + + std::string outname{"checkResid"}; + // histogram settings + int nBinsRes = 100; + int nBinsPhi = 30; + int nBinsZ = 20; + int nBinsPt = 15; + int nBinsTgl = 20; + int nBinsPVXYZ = 1000; + int minHistoStat2Fit = 1000; + float maxPull = 4; + float zranges[8] = {10.f, 15.f, 15.f, 15.f, 40.f, 40.f, 74.f, 74.f}; + float maxDYZ[8] = {0.03, 0.015, 0.01, 0.01, 0.08, 0.08, 0.12, 0.1}; + float maxDPar[5] = {0.15, 0.15, 0.015, 0.015, 1.}; + float maxHPVXY = 0.5; + float maxHPVZ = 20.; + float maxHPVN = 200.; + // drawing settings + float resMMLrY[8] = {0.003, 0.003, 0.003, 0.003, 0.005, 0.005, 0.005, 0.005}; + float resMMLrZ[8] = {0.002, 0.0015, 0.0015, 0.0015, 0.005, 0.005, 0.005, 0.005}; + float resMMPar[5] = {0.03, 0.01, 0.005, 0.001, 0.5}; + // + // string with existing histomanagers files to draw (comma or semicolon separated) and optional legends + std::string ext_hm_list{}; + std::string ext_leg_list{}; + + O2ParamDef(CheckResidConfig, "checkresid"); +}; +} // namespace o2::checkresid + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidSpec.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidSpec.h new file mode 100644 index 0000000000000..3cae8e94b8e68 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidSpec.h @@ -0,0 +1,26 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_CHECK_RESID_H +#define O2_CHECK_RESID_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" + +namespace o2::checkresid +{ +/// create a processor spec +o2::framework::DataProcessorSpec getCheckResidSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool drawOnly, bool postProcOnly); + +} // namespace o2::checkresid + +#endif // O2_CHECK_RESID_H diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h new file mode 100644 index 0000000000000..ebb6a7aabe9fa --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h @@ -0,0 +1,42 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_CHECK_RESID_TYPES_H +#define O2_CHECK_RESID_TYPES_H + +#include "ReconstructionDataFormats/Track.h" + +namespace o2::checkresid +{ +struct Point { + float dy = 0.f; + float dz = 0.f; + float sig2y = 0.f; + float sig2z = 0.f; + float phi = 0.f; + float z = 0.f; + int16_t sens = -1; + int8_t lr = -1; // -1 = vtx + ClassDefNV(Point, 1) +}; + +struct Track { + o2::dataformats::GlobalTrackID gid{}; + o2::track::TrackPar track; + o2::track::TrackParCov trIBOut; + o2::track::TrackParCov trOBInw; + std::vector points; + ClassDefNV(Track, 1) +}; + +} // namespace o2::checkresid + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/DumpTracks.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/DumpTracks.h new file mode 100644 index 0000000000000..f52c6ef80a2a4 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/DumpTracks.h @@ -0,0 +1,29 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_DUMP_TRACKS_H +#define O2_DUMP_TRACKS_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/Track.h" +#include "MathUtils/detail/Bracket.h" +#include "DataFormatsTPC/ClusterNative.h" + +namespace o2::trackstudy +{ +/// create a processor spec +o2::framework::DataProcessorSpec getDumpTracksSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC); + +} // namespace o2::trackstudy + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/HistoManager.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/HistoManager.h new file mode 100644 index 0000000000000..eb9cf6876333e --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/HistoManager.h @@ -0,0 +1,93 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef _O2_HISTOMANAGER_H_ +#define _O2_HISTOMANAGER_H_ + +#include +#include +#include "TObjArray.h" + +class TH1; +class TH2; +class TH1F; +class TH2F; +class TProfile; +class TGraph; +class TFile; + +namespace o2 +{ + +class HistoManager : public TObjArray +{ + public: + HistoManager(const std::string& dirname = "", const std::string& fname = "histoman.root", bool load = kFALSE, const std::string& prefix = ""); + ~HistoManager() override { Delete(); } + + HistoManager* createClone(const std::string& prefix) const; + void addPrefix(const std::string& pref = ""); + + int getNHistos() const { return mNHistos; } + TGraph* getGraph(int id) const; + TH1* getHisto(int id) const; + TH1* getHisto(const std::string& name) const; + TH1F* getHisto1F(int id) const; + TH2F* getHisto2F(int id) const; + TProfile* getHistoP(int id) const; + + int addHisto(TH1* histo, int at = -1); + int addGraph(TGraph* gr, int at = -1); + void delHisto(int at); + + void setFile(TFile* file); + void setFileName(const std::string& fname); + const std::string& getFileName() const { return mDefName; } + void setDirName(const std::string& name) { mDirName = name; } + const std::string& getDirName() const { return mDirName; } + + void reset(); + void write(TFile* file = nullptr); + int write(const std::string& flname) + { + setFileName(flname); + write(); + return 0; + } + + void addHistos(const HistoManager* hm, Double_t c1 = 1.); + void divideHistos(const HistoManager* hm); + void multiplyHistos(const HistoManager* hm); + void scaleHistos(Double_t c1 = 1.); + void setColor(int tcolor = 1); + void setMarkerStyle(Style_t mstyle = 1, Size_t msize = 1); + void setMarkerSize(Size_t msize = 1); + void sumw2(); + int load(const std::string& fname, const std::string& dirname = ""); + + void purify(bool emptyToo = kFALSE); + + void Print(Option_t* option = "") const override; + void Clear(Option_t* option = "") override; + void Delete(Option_t* option = "") override; + void Compress() override; + + private: + int mNHistos{0}; //! Number of histograms defined + std::string mDefName{}; //! Default file name + std::string mDirName{}; //! Directory name in the output file + + ClassDefOverride(HistoManager, 0); +}; + +} // namespace o2 + +#endif // _O2_HISTOMANAGER_H_ diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/ITSOffsStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/ITSOffsStudy.h new file mode 100644 index 0000000000000..a639020f2ea41 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/ITSOffsStudy.h @@ -0,0 +1,27 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKING_STUDY_H +#define O2_TRACKING_STUDY_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/Track.h" + +namespace o2::trackstudy +{ +/// create a processor spec +o2::framework::DataProcessorSpec getITSOffsStudy(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus); + +} // namespace o2::trackstudy + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h new file mode 100644 index 0000000000000..d54513cb07a60 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h @@ -0,0 +1,29 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_SV_STUDY_H +#define O2_SV_STUDY_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/Track.h" +#include "MathUtils/detail/Bracket.h" +#include "DataFormatsTPC/ClusterNative.h" + +namespace o2::svstudy +{ +/// create a processor spec +o2::framework::DataProcessorSpec getSVStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcCls, bool useMC); + +} // namespace o2::svstudy + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCClusSelector.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCClusSelector.h new file mode 100644 index 0000000000000..c1765558458c2 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCClusSelector.h @@ -0,0 +1,92 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// helper class for TPC clusters selection + +#ifndef ALICEO2_TPCCLUSSELECTOR_H +#define ALICEO2_TPCCLUSSELECTOR_H + +#include +#include +#include + +namespace o2::tpc +{ +class ClusterNativeAccess; + +class TPCClusSelector +{ + // helper to select TPC cluster matching to certain timebin and optionally pads range + // example of usage: + /* + TPCClusSelector clSel; + o2::tpc::ClusterNativeHelper::Reader tcpClusterReader; + tcpClusterReader.init(native_clusters_file.c_str()); + o2::tpc::ClusterNativeAccess tpcClusterIdxStruct; + std::unique_ptr tpcClusterBuffer; ///< buffer for clusters in tpcClusterIdxStruct + o2::tpc::ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer tpcClusterMCBuffer; ///< buffer for mc labels + + tcpClusterReader.read(iTF); + tcpClusterReader.fillIndex(tpcClusterIdxStruct, tpcClusterBuffer, tpcClusterMCBuffer); + + clSel.fill(tpcClusterIdxStruct); // Create sorted index + // to get i-th cluster in orderer timebins: + const auto& clus = tpcClusterIdxStruct.clusters[sector][row][ clSel.getIndex(sector, row, i)]; + + // to get sorted indices range of clusters in the tbmin:tbmax range + auto rng = clSel.findClustersRange(sector, row, tbmin, tbmax, tpcClusterIdxStruct); + if (rng.first>rng.second) { // nothing is found } + const auto& cln = tpcClusterIdxStruct.clusters[sector][row][clSel.getIndex(sector, row, rng.first )]; /... + + // to get number of clusters in tbmin:tbmax, padmin:padmax range (and optionally get the list) + std::vector cllist; // optional list + int nfnd = clSel.findClustersEntries(sector, row, tbmin, tbmax, padmin, padmax, tpcClusterIdxStruct, &cllist); + for (int i=0;i findClustersRange(int sec, int row, float tbmin, float tbmax, const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct); + int findClustersEntries(int sec, int row, float tbmin, float tbmax, float padmin, float padmax, const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct, std::vector* clIDDirect = nullptr); + void fill(const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct); + + int getNThreads() const { return mNThreads; } + void setNThreads(int n); + + private: + struct Sector { + static constexpr int NRows = 152; + std::array, NRows> rows; + void clear() + { + for (auto& r : rows) + r.clear(); + } + }; + + static constexpr int NSectors = 36; + std::array mSectors{}; + int mNThreads = 1; + + ClassDefNV(TPCClusSelector, 1); +}; + +} // namespace o2::tpc + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCDataFilter.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCDataFilter.h new file mode 100644 index 0000000000000..cf73a97516b53 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCDataFilter.h @@ -0,0 +1,28 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKING_STUDY_H +#define O2_TRACKING_STUDY_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/Track.h" +#include "DataFormatsTPC/ClusterNative.h" + +namespace o2::global +{ +/// create a processor spec +o2::framework::DataProcessorSpec getTPCDataFilter(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClusters, bool useMC); + +} // namespace o2::global + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h index b271ab8c191e6..f6396f682ac3b 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h @@ -9,8 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef O2_TPCTRACK_STUDY_H -#define O2_TPCTRACK_STUDY_H +#ifndef O2_TPC_DATA_FILTER_H +#define O2_TPC_DATA_FILTER_H #include "ReconstructionDataFormats/GlobalTrackID.h" #include "Framework/Task.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h new file mode 100644 index 0000000000000..e33a0def63842 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h @@ -0,0 +1,83 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// class for extended Track info (for debugging) + +#ifndef ALICEO2_TRINFOEXT_H +#define ALICEO2_TRINFOEXT_H + +#include "ReconstructionDataFormats/MatchInfoTOF.h" +#include "ReconstructionDataFormats/DCA.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/Track.h" + +namespace o2 +{ +namespace dataformats +{ + +struct TrackInfoExt { + enum { TPCA = 0, + TPCC = 1, + kBitMask = 0xffff }; + o2::track::TrackParCov track; + DCA dca{}; + DCA dcaTPC{}; + VtxTrackIndex gid; + MatchInfoTOF infoTOF; + std::array innerTPCPos{}; // innermost cluster position at assigned time + std::array innerTPCPos0{}; // innermost cluster position at nominal time0 + float ttime = 0; + float ttimeE = 0; + float xmin = 0; + float chi2TPC = 0.f; + float chi2ITSTPC = 0.f; + float q2ptITS = 0.f; + float q2ptTPC = 0.f; + float q2ptITSTPC = 0.f; + float q2ptITSTPCTRD = 0.f; + uint16_t nClTPC = 0; + uint16_t nClTPCShared = 0; + uint16_t flags = 0; + uint8_t pattITS = 0; + uint8_t nClITS = 0; + uint8_t rowMinTPC = 0; + uint8_t padFromEdge = -1; + uint8_t rowMaxTPC = 0; + uint8_t rowCountTPC = 0; + size_t hashIU = 0; + void setTPCA() { setBit(int(TPCA)); } + void setTPCC() { setBit(int(TPCC)); } + void setTPCAC() { setBit(int(TPCC)); } + + bool isTPCA() const { return isBitSet(int(TPCA)); } + bool isTPCC() const { return isBitSet(int(TPCC)); } + bool isTPCAC() const { return isBitSet(int(TPCA)) && isBitSet(int(TPCC)); } + + float getTPCInX() const { return innerTPCPos[0]; } + float getTPCInY() const { return innerTPCPos[1]; } + float getTPCInZ() const { return innerTPCPos[2]; } + float getTPCInX0() const { return innerTPCPos0[0]; } + float getTPCInY0() const { return innerTPCPos0[1]; } + float getTPCInZ0() const { return innerTPCPos0[2]; } + + void setBits(std::uint16_t b) { flags = b; } + void setBit(int bit) { flags |= kBitMask & (0x1 << bit); } + void resetBit(int bit) { flags &= ~(kBitMask & (0x1 << bit)); } + bool isBitSet(int bit) const { return flags & (kBitMask & (0x1 << bit)); } + + ClassDefNV(TrackInfoExt, 8); +}; + +} // namespace dataformats +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h new file mode 100644 index 0000000000000..18ce3dbf8ab87 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h @@ -0,0 +1,27 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKING_STUDY_H +#define O2_TRACKING_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2::trackstudy +{ + +/// create a processor spec +o2::framework::DataProcessorSpec getTrackMCStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool checkSV); + +} // namespace o2::trackstudy + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h new file mode 100644 index 0000000000000..ed78ba2a710ec --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h @@ -0,0 +1,43 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKING_STUDY_CONFIG_H +#define O2_TRACKING_STUDY_CONFIG_H +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::trackstudy +{ +struct TrackMCStudyConfig : o2::conf::ConfigurableParamHelper { + float minPt = 0.05; + float maxTgl = 1.5; + float minPtMC = 0.05; + float maxTglMC = 1.5; + float maxRMC = 33.; + float maxPosTglMC = 2.; + float maxPVZOffset = 15.; + float decayMotherMaxT = 1.0f; // max TOF in ns for mother particles to study + bool requireITSorTPCTrackRefs = true; + bool requireTopBottomRefs = false; + bool storeTPCTrackRefs = false; + bool storeITSInfo = true; + int minTPCRefsToExtractClRes = 2; + int nOccBinsDrift = 10; // number of bins for TPC max drift time, where we integrate the occupancies + int nTBPerOccBin = 48; // number of TB per occ bin + float rejectClustersResStat = 0.1; + float maxTPCRefExtrap = 2; // max dX to extrapolate the track ref when extrapolating track true posions + int minITSClForITSoutput = 7; // create special ITS otput only for long enough tracks + int decayPDG[5] = {310, 3122, 411, 421, -1}; // decays to study, must end by -1 + O2ParamDef(TrackMCStudyConfig, "trmcconf"); +}; +} // namespace o2::trackstudy + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h new file mode 100644 index 0000000000000..f5846bcce5a49 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h @@ -0,0 +1,324 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKING_STUDY_TYPES_H +#define O2_TRACKING_STUDY_TYPES_H +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/Track.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "CommonConstants/LHCConstants.h" +#include "CommonDataFormat/TimeStamp.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "SimulationDataFormat/TrackReference.h" +#include +#include + +namespace o2::trackstudy +{ +struct MCTrackInfo { + + inline float getMCTimeMUS() const { return bcInTF * o2::constants::lhc::LHCBunchSpacingMUS; } + inline bool hasITSHitOnLr(int i) const { return (pattITSCl & ((0x1 << i) & 0x7f)) != 0; } + int getNITSClusCont() const; + int getNITSClusForAB() const; + int getLowestITSLayer() const; + int getHighestITSLayer() const; + std::vector occTPCV{}; + std::vector trackRefsTPC{}; + o2::track::TrackPar track{}; + o2::MCCompLabel label{}; + float occTPC = -1.f; + int occITS = -1.f; + int bcInTF = -1; + int pdg = 0; + int pdgParent = 0; + int parentEntry = -1; + int16_t nTPCCl = 0; + int16_t nTPCClShared = 0; + int8_t parentDecID = -1; + uint8_t minTPCRow = -1; + uint8_t maxTPCRow = 0; + uint8_t nUsedPadRows = 0; + uint8_t maxTPCRowInner = 0; // highest row in the sector containing the lowest one + uint8_t minTPCRowSect = -1; + uint8_t maxTPCRowSect = -1; + int8_t nITSCl = 0; + int8_t pattITSCl = 0; + uint8_t flags = 0; + + enum Flags : uint32_t { Primary = 0, + AddedAtRecStage = 2, + BitMask = 0xff }; + + bool isPrimary() const { return isBitSet(Primary); } + bool isAddedAtRecStage() const { return isBitSet(AddedAtRecStage); } + void setPrimary() { setBit(Primary); } + void setAddedAtRecStage() { setBit(AddedAtRecStage); } + + uint8_t getBits() const { return flags; } + bool isBitSet(int bit) const { return flags & (0xff & (0x1 << bit)); } + void setBits(std::uint8_t b) { flags = b; } + void setBit(int bit) { flags |= BitMask & (0x1 << bit); } + void resetBit(int bit) { flags &= ~(BitMask & (0x1 << bit)); } + + o2::track::TrackPar getTrackParTPC(float b, float x = 90) const; + float getTrackParTPCPar(int i, float b, float x = 90) const; + float getTrackParTPCPhiSec(float b, float x = 90) const; + + ClassDefNV(MCTrackInfo, 8); +}; + +struct RecTrack { + enum FakeFlag { + FakeITS = 0x1 << 0, + FakeTPC = 0x1 << 1, + FakeTRD = 0x1 << 2, + FakeTOF = 0x1 << 3, + FakeITSTPC = 0x1 << 4, + FakeITSTPCTRD = 0x1 << 5, + HASACSides = 0x1 << 6, + FakeGLO = 0x1 << 7 + }; + o2::track::TrackParCov track{}; + o2::dataformats::VtxTrackIndex gid{}; + o2::dataformats::TimeStampWithError ts{}; + o2::MCEventLabel pvLabel{}; + short pvID = -1; + uint8_t nClTPCShared = 0; + uint8_t flags = 0; + uint8_t nClITS = 0; + uint8_t nClTPC = 0; + uint8_t pattITS = 0; + int8_t lowestPadRow = -1; + int8_t padFromEdge = -1; + uint8_t rowMaxTPC = 0; + uint8_t rowCountTPC = 0; + + bool isFakeGLO() const { return flags & FakeGLO; } + bool isFakeITS() const { return flags & FakeITS; } + bool isFakeTPC() const { return flags & FakeTPC; } + bool isFakeTRD() const { return flags & FakeTRD; } + bool isFakeTOF() const { return flags & FakeTOF; } + bool isFakeITSTPC() const { return flags & FakeITSTPC; } + bool hasACSides() const { return flags & HASACSides; } + + ClassDefNV(RecTrack, 3); +}; + +struct TrackPairInfo { + RecTrack tr0; + RecTrack tr1; + uint8_t nshTPC = 0; + uint8_t nshTPCRow = 0; + + int getComb() const { return tr0.track.getSign() != tr1.track.getSign() ? 0 : (tr0.track.getSign() > 0 ? 1 : 2); } + float getDPhi() const + { + float dphi = tr0.track.getPhi() - tr1.track.getPhi(); + if (dphi < -o2::constants::math::PI) { + dphi += o2::constants::math::TwoPI; + } else if (dphi > o2::constants::math::PI) { + dphi -= o2::constants::math::TwoPI; + } + return dphi; + } + float getDTgl() const { return tr0.track.getTgl() - tr1.track.getTgl(); } + + ClassDefNV(TrackPairInfo, 1) +}; + +struct TrackFamily { // set of tracks related to the same MC label + MCTrackInfo mcTrackInfo{}; + std::vector recTracks{}; + o2::track::TrackParCov trackITSProp{}; + o2::track::TrackParCov trackTPCProp{}; + int8_t entITS = -1; + int8_t entTPC = -1; + int8_t entITSTPC = -1; + int8_t entITSFound = -1; // ITS track for this MC track, regardless if it was matched to TPC of another track + int8_t flags = 0; + float tpcT0 = -999.; + + bool contains(const o2::dataformats::VtxTrackIndex& ref) const + { + for (const auto& tr : recTracks) { + if (ref == tr.gid) { + return true; + } + } + return false; + } + const RecTrack& getTrackWithITS() const { return entITS < 0 ? dummyRecTrack : recTracks[entITS]; } + const RecTrack& getTrackWithTPC() const { return entTPC < 0 ? dummyRecTrack : recTracks[entTPC]; } + const RecTrack& getTrackWithITSTPC() const { return entITSTPC < 0 ? dummyRecTrack : recTracks[entITSTPC]; } + const RecTrack& getTrackWithITSFound() const { return entITSFound < 0 ? dummyRecTrack : recTracks[entITSFound]; } + const RecTrack& getLongestTPCTrack() const + { + int n = getLongestTPCTrackEntry(); + return n < 0 ? dummyRecTrack : recTracks[n]; + } + int getLongestTPCTrackEntry() const; + int getNTPCClones() const; + static RecTrack dummyRecTrack; // + + ClassDefNV(TrackFamily, 1); +}; + +struct ClResTPCCont { + // contributor to TPC Cluster + std::array xyz{}; + std::array below{}; + std::array above{}; + float snp = 0.; + float tgl = 0.; + float q2pt = 0.; + bool corrAttach = false; + + int getNExt() const { return (below[0] > 1.) + (above[0] > 1.); } + + float getClX() const { return xyz[0]; } + float getClY() const { return xyz[1]; } + float getClZ() const { return xyz[2]; } + + float getDY() const { return xyz[1] - getYRef(); } + float getDZ() const { return xyz[2] - getZRef(); } + + float getYRef() const + { + float y = 0; + int n = 0; + if (below[0] > 1.) { + y += below[1]; + n++; + } + if (above[0] > 1.) { + y += above[1]; + n++; + } + return n == 1 ? y : 0.5 * y; + } + + float getZRef() const + { + float z = 0; + int n = 0; + if (below[0] > 1.) { + z += below[2]; + n++; + } + if (above[0] > 1.) { + z += above[2]; + n++; + } + return n == 1 ? z : 0.5 * z; + } + + float getDXMin() const + { + float adxA = 1e9, adxB = 1e9; + if (above[0] > 1.) { + adxA = xyz[0] - above[0]; + } + if (below[0] > 1.) { + adxB = xyz[1] - below[0]; + } + return std::abs(adxA) < std::abs(adxB) ? adxA : adxB; + } + + float getDXMax() const + { + float adxA = 0, adxB = 0; + if (above[0] > 1.) { + adxA = xyz[0] - above[0]; + } + if (below[0] > 1.) { + adxB = xyz[0] - below[0]; + } + return std::abs(adxA) > std::abs(adxB) ? adxA : adxB; + } + + float getEY() const { return getNExt() > 1 ? below[1] - above[1] : -999; } + float getEZ() const { return getNExt() > 1 ? below[2] - above[2] : -999; } + + ClassDefNV(ClResTPCCont, 1); +}; + +struct ClResTPC { + uint8_t sect = 0; + uint8_t row = 0; + uint8_t ncont = 0; + uint8_t flags = 0; + uint8_t sigmaTimePacked; + uint8_t sigmaPadPacked; + float qmax = 0; + float qtot = 0; + float occ = 0; + float occBin = 0; + float getSigmaPad() const { return float(sigmaPadPacked) * (1.f / 32); } + float getSigmaTime() const { return float(sigmaTimePacked) * (1.f / 32); } + + std::vector contTracks; + int getNCont() const { return contTracks.size(); } + + float getDY(int i) const { return i < getNCont() ? contTracks[i].getDY() : -999.; } + float getDZ(int i) const { return i < getNCont() ? contTracks[i].getDZ() : -999.; } + float getYRef(int i) const { return i < getNCont() ? contTracks[i].getYRef() : -999.; } + float getZRef(int i) const { return i < getNCont() ? contTracks[i].getZRef() : -999.; } + float getDXMin(int i) const { return i < getNCont() ? contTracks[i].getDXMin() : -999.; } + float getDXMax(int i) const { return i < getNCont() ? contTracks[i].getDXMax() : -999.; } + float getEY(int i) const { return i < getNCont() ? contTracks[i].getEY() : -999.; } + float getEZ(int i) const { return i < getNCont() ? contTracks[i].getEZ() : -999.; } + + void sortCont() + { + std::sort(contTracks.begin(), contTracks.end(), [](const ClResTPCCont& a, const ClResTPCCont& b) { + float dya = a.getDY(), dyb = b.getDY(), dza = a.getDZ(), dzb = b.getDZ(); + return dya * dya + dza * dza < dyb * dyb + dzb * dzb; + }); + } + + ClassDefNV(ClResTPC, 2); +}; + +struct ITSHitInfo { + o2::BaseCluster clus{}; + o2::TrackReference tref{}; + float trefXT = 0; // track ref tracking frame coordinates + float trefYT = 0; + float chipX = 0; + float chipAlpha = 0; + ClassDefNV(ITSHitInfo, 1); +}; + +struct RecPV { + o2::dataformats::PrimaryVertex pv{}; + o2::MCEventLabel mcEvLbl{}; + ClassDefNV(RecPV, 1); +}; + +struct MCVertex { + float getX() const { return pos[0]; } + float getY() const { return pos[1]; } + float getZ() const { return pos[2]; } + + std::array pos{0., 0., -1999.f}; + float ts = 0; + int nTrackSel = 0; // number of selected MC charged tracks + int ID = -1; + std::vector recVtx{}; + std::vector occTPCV{}; + ClassDefNV(MCVertex, 2); +}; + +} // namespace o2::trackstudy +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h new file mode 100644 index 0000000000000..b1a9f6923f04d --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h @@ -0,0 +1,49 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// class for extended V0 info (for debugging) + +#ifndef ALICEO2_V0EXT_H +#define ALICEO2_V0EXT_H + +#include "ReconstructionDataFormats/V0.h" +#include "SimulationDataFormat/MCCompLabel.h" + +namespace o2::dataformats +{ + +struct ProngInfoExt { + o2::track::TrackParCov trackTPC; + int nClTPC = 0; + int nClITS = 0; + int pattITS = 0; + float chi2ITSTPC = 0.f; + uint8_t lowestRow = -1; + uint8_t padFromEdge = -1; + int8_t corrGlo = -1; + int8_t corrITSTPC = -1; + int8_t corrITS = -1; + int8_t corrTPC = -1; + ClassDefNV(ProngInfoExt, 3); +}; + +struct V0Ext { + V0 v0; + V0Index v0ID; + std::array prInfo{}; + const ProngInfoExt& getPrInfo(int i) const { return prInfo[i]; } + int mcPID = -1; + ClassDefNV(V0Ext, 2); +}; + +} // namespace o2::dataformats + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx new file mode 100644 index 0000000000000..a754d1196017f --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/CheckResidConfig.h" + +O2ParamImpl(o2::checkresid::CheckResidConfig); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResidSpec.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidSpec.cxx new file mode 100644 index 0000000000000..93249946c9011 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidSpec.cxx @@ -0,0 +1,1029 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/CheckResidSpec.h" +#include "GlobalTrackingStudy/CheckResidTypes.h" +#include "GlobalTrackingStudy/CheckResidConfig.h" +#include +#include "ReconstructionDataFormats/Track.h" +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsITSMFT/TrkClusRef.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "SimulationDataFormat/MCUtils.h" +#include "CommonUtils/NameConf.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" +#include "ITSBase/GeometryTGeo.h" +#include "ITStracking/IOUtils.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "DetectorsVertexing/PVertexer.h" +#include "GlobalTrackingStudy/HistoManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WITH_OPENMP +#include +#endif + +// Attention: in case the residuals are checked with geometry different from the one used for initial reconstruction, +// pass a --configKeyValues option for vertex refit as: +// ;pvertexer.useMeanVertexConstraint=false;pvertexer.meanVertexExtraErrSelection=0.2;pvertexer.iniScale2=100;pvertexer.acceptableScale2=10.; +// In any case, it is better to pass ;pvertexer.useMeanVertexConstraint=false; + +namespace o2::checkresid +{ +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; + +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using GTrackID = o2::dataformats::GlobalTrackID; +using timeEst = o2::dataformats::TimeStampWithError; + +class CheckResidSpec final : public Task +{ + public: + CheckResidSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool drawOnly, bool postProcOnly) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mDrawOnly(drawOnly), mPostProcOnly(postProcOnly) + { + } + ~CheckResidSpec() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + bool refitPV(o2::dataformats::PrimaryVertex& pv, int vid); + bool refitITStrack(o2::track::TrackParCov& track, GTrackID gid); + bool processITSTrack(const o2::its::TrackITS& iTrack, const o2::dataformats::PrimaryVertex& pv, o2::checkresid::Track& resTrack, o2::track::PID pid); + void bookHistos(); + void fillHistos(const o2::checkresid::Track& trc); + void postProcessHistos(); + void drawHistos(); + + o2::globaltracking::RecoContainer* mRecoData = nullptr; + int mNThreads = 1; + bool mMeanVertexUpdated = false; + float mITSROFrameLengthMUS = 0.f; + o2::dataformats::MeanVertexObject mMeanVtx{}; + std::vector> mITSClustersArray; ///< ITS clusters created in run() method from compact clusters + const o2::itsmft::TopologyDictionary* mITSDict = nullptr; ///< cluster patterns dictionary + o2::vertexing::PVertexer mVertexer; + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mDBGOut; + GTrackID::mask_t mTracksSrc{}; + + bool mDrawOnly = false; + bool mPostProcOnly = false; + bool mDraw = false; + bool mFillHistos = true; + bool mFillTree = true; + std::vector> mHManV{}; + std::vector mPVUsed; + o2::HistoManager* mHMan = nullptr; +}; + +void CheckResidSpec::init(InitContext& ic) +{ + mDraw = true; + if (!mDrawOnly) { + mDraw = ic.options().get("draw-report"); + mFillHistos = !ic.options().get("no-hist"); + mFillTree = !ic.options().get("no-tree"); + mNThreads = ic.options().get("nthreads"); + } + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + int lane = ic.services().get().inputTimesliceId; + int maxLanes = ic.services().get().maxInputTimeslices; + std::string nm = params.outname; + if (maxLanes > 1) { + o2::conf::ConfigurableParam::updateFromString(fmt::format("checkresid.outname={}_t{}", nm, lane)); + } + if (mDraw) { + mFillHistos = true; + } + if (!mDrawOnly && mFillHistos) { + bookHistos(); + } + if (!params.ext_hm_list.empty()) { + auto vecNames = o2::utils::Str::tokenize(params.ext_hm_list, ','); + auto vecLegends = o2::utils::Str::tokenize(params.ext_leg_list, ','); + bool useLeg = true; + if (vecNames.size() != vecLegends.size()) { + LOGP(warn, "{} legend names provided for {} external histomanagers, will use file names as legends", vecLegends.size(), vecNames.size()); + useLeg = false; + } + int cntH = 0; + for (const auto& vn : vecNames) { + LOGP(info, "Loading external HistoManager {}", vn); + mHManV.emplace_back() = std::make_unique("", vn, true); + auto hm = mHManV.back().get(); + if (!hm) { + LOGP(error, "Failed to load histograms from {}", vn); + mHManV.pop_back(); + } else { + hm->SetName(useLeg ? vecLegends[cntH].c_str() : vn.c_str()); + } + cntH++; + } + } + if (mDrawOnly) { + return; + } + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); +#ifndef WITH_OPENMP + if (mNThreads > 1) { + LOGP(warn, "No OpenMP"); + } + mNThreads = 1; +#endif + if (mFillTree) { + mDBGOut = std::make_unique(fmt::format("{}.root", params.outname).c_str(), "recreate"); + } +} + +void CheckResidSpec::run(ProcessingContext& pc) +{ + bool quit = false; + if (mPostProcOnly) { + + postProcessHistos(); + quit = true; + } + if (mDrawOnly) { + drawHistos(); + quit = true; + } + if (quit) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + return; + } + o2::globaltracking::RecoContainer recoData; + mRecoData = &recoData; + mRecoData->collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer + mRecoData = &recoData; + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(); + mRecoData = nullptr; +} + +void CheckResidSpec::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + pc.inputs().get("meanvtx"); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + initOnceDone = true; + // Note: reading of the ITS AlpideParam needed for ITS timing is done by the RecoContainer + auto grp = o2::base::GRPGeomHelper::instance().getGRPECS(); + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); + if (!grp->isDetContinuousReadOut(DetID::ITS)) { + mITSROFrameLengthMUS = alpParams.roFrameLengthTrig / 1.e3; // ITS ROFrame duration in \mus + } else { + mITSROFrameLengthMUS = alpParams.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; // ITS ROFrame duration in \mus + } + auto geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); + o2::conf::ConfigurableParam::updateFromString("pvertexer.useTimeInChi2=false;"); + mVertexer.init(); + } + if (mMeanVertexUpdated) { + mMeanVertexUpdated = false; + mVertexer.setMeanVertex(&mMeanVtx); + mVertexer.initMeanVertexConstraint(); + } +} + +void CheckResidSpec::process() +{ + if (!mITSDict) { + LOGP(fatal, "ITS data is not loaded"); + } + const auto itsTracks = mRecoData->getITSTracks(); + // const auto itsLbls = mRecoData->getITSTracksMCLabels(); + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + const auto clusITS = mRecoData->getITSClusters(); + const auto patterns = mRecoData->getITSClustersPatterns(); + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + auto pattIt = patterns.begin(); + mITSClustersArray.clear(); + mITSClustersArray.reserve(clusITS.size()); + + o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict); + + auto pvvec = mRecoData->getPrimaryVertices(); + auto trackIndex = mRecoData->getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = mRecoData->getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto prop = o2::base::Propagator::Instance(); + static int TFCount = 0; + int nv = vtxRefs.size() - 1; + std::vector> slots; + mPVUsed.clear(); + slots.resize(mNThreads); + int nvGood = 0, nvUse = 0, nvRefFail = 0; + long pvFitDuration{}; + for (int iv = 0; iv < nv; iv++) { + const auto& vtref = vtxRefs[iv]; + auto pve = pvvec[iv]; + if (pve.getNContributors() < params.minPVContributors) { + continue; + } + nvGood++; + if (params.refitPV) { + LOGP(debug, "Refitting PV#{} of {} tracks", iv, pve.getNContributors()); + auto tStartPVF = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + bool res = refitPV(pve, iv); + pvFitDuration += std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count() - tStartPVF; + if (!res) { + nvRefFail++; + continue; + } + } + nvUse++; + mPVUsed.push_back(pve); + const auto& itsContRefs = vtref.getITSGloContributors(); + int idMinITSGlo = 0, idMaxITSGlo = 0; + if (params.useITSGloContributors) { + if (itsContRefs.getFirstEntry() < vtref.getEntries() && itsContRefs.getEntries() == 0) { + LOGP(fatal, "Usage of stored ITS global contributors is requested but they are missing"); + } + idMinITSGlo = itsContRefs.getFirstEntry(); + idMaxITSGlo = idMinITSGlo + itsContRefs.getEntries(); + } + int cntPVCont = 0; + for (int is = 0; is < GTrackID::NSources; is++) { + if (!params.useITSGloContributors && (!mTracksSrc[is] || !mRecoData->isTrackSourceLoaded(is))) { + continue; + } + int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is); + DetID::mask_t dm = GTrackID::getSourceDetectorsMask(is); + if (!dm[DetID::ITS]) { + continue; + } +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) +#endif + for (int i = idMin; i < idMax; i++) { + auto vid = trackIndex[i]; + bool pvCont = vid.isPVContributor(); + if (!pvCont && (params.pvcontribOnly || params.useITSGloContributors)) { + continue; + } + GTrackID gidITS; + o2::track::PID pidITS; + if (params.useITSGloContributors) { + int id = idMinITSGlo + cntPVCont; + if (id >= idMaxITSGlo) { + LOGP(fatal, "Calculated GlobalContributor ITS track index {} exceeds number of stored indices {}", id, itsContRefs.getEntries()); + } + pidITS = trackIndex[id].getSource(); + gidITS = GTrackID(trackIndex[id].getIndex(), GTrackID::ITS); + cntPVCont++; + } else { + gidITS = mRecoData->getITSContributorGID(vid); + if (gidITS.getSource() != GTrackID::ITS) { + continue; + } + } + const auto& itsTrack = mRecoData->getITSTrack(gidITS); + if (itsTrack.getNClusters() < params.minITSCl) { + continue; + } + const auto& trc = params.useITSGloContributors ? ((o2::track::TrackParCov&)itsTrack) : mRecoData->getTrackParam(vid); + if (!params.useITSGloContributors) { + pidITS = trc.getPID(); + } + auto pt = trc.getPt(); + if (pt < params.minPt || pt > params.maxPt) { + continue; + } + if (std::abs(trc.getTgl()) > params.maxTgl) { + continue; + } +#ifdef WITH_OPENMP + auto& accum = slots[omp_get_thread_num()]; +#else + auto& accum = slots[0]; +#endif + auto& resTrack = accum.emplace_back(); + resTrack.gid = vid; + if (!processITSTrack(itsTrack, pve, resTrack, pidITS)) { + accum.pop_back(); + continue; + } + } + } + } + // output + for (const auto& accum : slots) { + for (const auto& tr : accum) { + if (mDBGOut) { + (*mDBGOut) << "res" << "tr=" << tr << "\n"; + } + if (mHMan) { + fillHistos(tr); + } + } + } + if (mDBGOut) { + (*mDBGOut) << "pvUsed" << "pv=" << mPVUsed << "\n"; + } + if (mHMan) { + for (const auto& pv : mPVUsed) { + mHMan->getHisto(20000 + 0)->Fill(pv.getX()); + mHMan->getHisto(20000 + 1)->Fill(pv.getY()); + mHMan->getHisto(20000 + 2)->Fill(pv.getZ()); + mHMan->getHisto(20000 + 3)->Fill(pv.getNContributors()); + } + } + LOGP(info, "processed {} PVs out of {} good vertices (out of {} in total), PV refits took {} mus, {} refits failed", nvUse, nvGood, nv, pvFitDuration, nvRefFail); + TFCount++; +} + +bool CheckResidSpec::processITSTrack(const o2::its::TrackITS& iTrack, const o2::dataformats::PrimaryVertex& pv, o2::checkresid::Track& resTrack, o2::track::PID pid) +{ + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + auto trFitInw = iTrack.getParamOut(); // seed for inward refit + auto trFitOut = iTrack.getParamIn(); // seed for outward refit + trFitInw.setPID(pid); + trFitOut.setPID(pid); + auto prop = o2::base::Propagator::Instance(); + auto geom = o2::its::GeometryTGeo::Instance(); + float pvAlpha = 0; + float bz = prop->getNominalBz(); + std::array*, 8> clArr{}; + const auto& params = CheckResidConfig::Instance(); + std::array extrapOut, extrapInw; // 2-way Kalman extrapolations, vertex + 7 layers + + auto rotateTrack = [bz](o2::track::TrackParCov& tr, float alpha, o2::track::TrackPar* refLin) { + return refLin ? tr.rotate(alpha, *refLin, bz) : tr.rotate(alpha); + }; + + auto accountCluster = [&](int i, std::array& extrapDest, o2::track::TrackParCov& tr, o2::track::TrackPar* refLin) { + if (clArr[i]) { // update with cluster + if (!rotateTrack(tr, i == 0 ? pvAlpha : geom->getSensorRefAlpha(clArr[i]->getSensorID()), refLin) || + !prop->propagateTo(tr, refLin, clArr[i]->getX(), true)) { + return 0; + } + extrapDest[i] = tr; // before update + if (!tr.update(*clArr[i])) { + return 0; + } + } else { + extrapDest[i].invalidate(); + return -1; + } + return 1; + }; + + auto inv2d = [](float s00, float s11, float s01) -> std::array { + auto det = s00 * s11 - s01 * s01; + if (det < 1e-16) { + LOGP(error, "Singular det {}, input: {} {} {}", det, s00, s11, s01); + return {0.f, 0.f, 0.f}; + } + det = 1.f / det; + return {s11 * det, s00 * det, -s01 * det}; + }; + + resTrack.points.clear(); + if (!prop->propagateToDCA(pv, trFitOut, bz)) { + LOGP(debug, "Failed to propagateToDCA, {}", trFitOut.asString()); + return false; + } + o2::BaseCluster bcPV; + if (params.addPVAsCluster) { + float cosAlp, sinAlp; + pvAlpha = trFitOut.getAlpha(); + o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp); // vertex position rotated to track frame + bcPV.setXYZ(pv.getX() * cosAlp + pv.getY() * sinAlp, -pv.getX() * sinAlp + pv.getY() * cosAlp, pv.getZ()); + bcPV.setSigmaY2(0.5 * (pv.getSigmaX2() + pv.getSigmaY2())); + bcPV.setSigmaZ2(pv.getSigmaZ2()); + bcPV.setSensorID(-1); + clArr[0] = &bcPV; + } + // collect all track clusters to array, placing them to layer+1 slot + int nCl = iTrack.getNClusters(); + for (int i = 0; i < nCl; i++) { // clusters are ordered from the outermost to the innermost + const auto& curClu = mITSClustersArray[itsClRefs[iTrack.getClusterEntry(i)]]; + + int llr = geom->getLayer(curClu.getSensorID()); + if (clArr[1 + llr]) { + LOGP(error, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), curClu.getSensorID()); + } + clArr[1 + geom->getLayer(curClu.getSensorID())] = &curClu; + } + o2::track::TrackPar refLinInw0, refLinOut0, *refLinOut = nullptr, *refLinInw = nullptr; + o2::track::TrackPar refLinIBOut0, refLinOBInw0, *refLinOBInw = nullptr, *refLinIBOut = nullptr; + if (params.useStableRef) { + refLinOut = &(refLinOut0 = trFitOut); + refLinInw = &(refLinInw0 = trFitInw); + } + trFitOut.resetCovariance(); + trFitOut.setCov(trFitOut.getQ2Pt() * trFitOut.getQ2Pt() * trFitOut.getCov()[14], 14); + trFitInw.resetCovariance(); + trFitInw.setCov(trFitInw.getQ2Pt() * trFitInw.getQ2Pt() * trFitInw.getCov()[14], 14); + // fit in inward and outward direction + for (int i = 0; i <= 7; i++) { + int resOut, resInw; + // process resOut in ascending order (0-->7) and resInw in descending order (7-->0) + if (!(resOut = accountCluster(i, extrapOut, trFitOut, refLinOut)) || !(resInw = accountCluster(7 - i, extrapInw, trFitInw, refLinInw))) { + return false; + } + // at layer 3, find the IB track (trIBOut) and the OB track (trOBInw) + // propagate both trcaks to a common radius, RCompIBOB (12cm), and rotates + // them to the same reference frame for comparison + if (i == 3 && resOut == 1 && resInw == 1 && params.doIBOB && nCl == 7) { + resTrack.trIBOut = trFitOut; // outward track updated at outermost IB layer + resTrack.trOBInw = trFitInw; // inward track updated at innermost OB layer + o2::track::TrackPar refLinIBOut0, refLinIBIn0; + if (refLinOut) { + refLinIBOut = &(refLinIBOut0 = refLinOut0); + refLinOBInw = &(refLinOBInw0 = refLinInw0); + } + float xRref; + if (!resTrack.trOBInw.getXatLabR(params.rCompIBOB, xRref, bz) || + !prop->propagateTo(resTrack.trOBInw, refLinOBInw, xRref, true) || + !rotateTrack(resTrack.trOBInw, resTrack.trOBInw.getPhiPos(), refLinOBInw) || // propagate OB track to ref R and rotate + !rotateTrack(resTrack.trIBOut, resTrack.trOBInw.getAlpha(), refLinIBOut) || + !prop->propagateTo(resTrack.trIBOut, refLinIBOut, resTrack.trOBInw.getX(), true)) { // rotate OB track to same frame and propagate to same X + // if any propagation or rotation steps fail, invalidate both tracks + return false; + } + } + } + + bool innerDone = false; + if (params.doResid) { + for (int i = 0; i <= 7; i++) { + if (clArr[i]) { + // calculate interpolation as a weighted mean of inward/outward extrapolations to this layer + const auto &tInw = extrapInw[i], &tOut = extrapOut[i]; + auto wInw = inv2d(tInw.getSigmaY2(), tInw.getSigmaZ2(), tInw.getSigmaZY()); + auto wOut = inv2d(tOut.getSigmaY2(), tOut.getSigmaZ2(), tOut.getSigmaZY()); + if (wInw[0] == 0.f || wOut[0] == 0.f) { + return false; + } + std::array wTot = {wInw[0] + wOut[0], wInw[1] + wOut[1], wInw[2] + wOut[2]}; + auto cTot = inv2d(wTot[0], wTot[1], wTot[2]); + auto ywi = wInw[0] * tInw.getY() + wInw[2] * tInw.getZ() + wOut[0] * tOut.getY() + wOut[2] * tOut.getZ(); + auto zwi = wInw[2] * tInw.getY() + wInw[1] * tInw.getZ() + wOut[2] * tOut.getY() + wOut[1] * tOut.getZ(); + auto yw = ywi * cTot[0] + zwi * cTot[2]; + auto zw = ywi * cTot[2] + zwi * cTot[1]; + // posCl.push_back(clArr[i]->getXYZGlo(*o2::its::GeometryTGeo::Instance())); + auto phi = i == 0 ? tInw.getPhi() : tInw.getPhiPos(); + o2::math_utils::bringTo02Pi(phi); + resTrack.points.emplace_back(clArr[i]->getY() - yw, clArr[i]->getZ() - zw, cTot[0] + clArr[i]->getSigmaY2(), cTot[1] + clArr[i]->getSigmaZ2(), phi, clArr[i]->getZ(), clArr[i]->getSensorID(), i - 1); + if (!innerDone) { + resTrack.track = tInw; + innerDone = true; + } + } else { + LOGP(debug, "No cluster on lr {}", i); + } + } + } + return true; +} + +bool CheckResidSpec::refitPV(o2::dataformats::PrimaryVertex& pv, int vid) +{ + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + std::vector tracks; + std::vector useTrack; + std::vector gidsITS; + int ntr = pv.getNContributors(), ntrIni = ntr; + tracks.reserve(ntr); + useTrack.reserve(ntr); + gidsITS.reserve(ntr); + const auto& vtref = mRecoData->getPrimaryVertexMatchedTrackRefs()[vid]; + auto trackIndex = mRecoData->getPrimaryVertexMatchedTracks(); + const auto& itsContRefs = vtref.getITSGloContributors(); + if (params.useITSGloContributors && itsContRefs.getEntries()) { + int itr = itsContRefs.getFirstEntry(), itLim = itr + itsContRefs.getEntries(); + for (; itr < itLim; itr++) { + auto tid = trackIndex[itr]; // these are ITS tracks, the Source part is substituted by the PID!!! + tracks.emplace_back().setPID(tid.getSource()); + gidsITS.emplace_back(tid.getIndex(), GTrackID::ITS); + } + } else { + int itr = vtref.getFirstEntry(), itLim = itr + vtref.getEntries(); + for (; itr < itLim; itr++) { + auto tid = trackIndex[itr]; + if (tid.isPVContributor() && mRecoData->isTrackSourceLoaded(tid.getSource())) { + tracks.emplace_back().setPID(mRecoData->getTrackParam(tid).getPID()); + gidsITS.push_back(mRecoData->getITSContributorGID(tid)); + } + } + } + ntr = tracks.size(); + useTrack.resize(ntr); +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) +#endif + for (int itr = 0; itr < ntr; itr++) { + if (!(useTrack[itr] = refitITStrack(tracks[itr], gidsITS[itr]))) { + tracks[itr] = mRecoData->getTrackParam(gidsITS[itr]); // this track will not be used but participates in prepareVertexRefit + } + } + ntr = 0; + for (auto v : useTrack) { + ntr++; + } + if (ntr < params.minPVContributors || !mVertexer.prepareVertexRefit(tracks, pv)) { + LOGP(warn, "Abandon vertex refit: NcontribNew = {} vs NcontribOld = {}", ntr, ntrIni); + return false; + } + LOGP(debug, "Original vtx: Nc:{} {}, chi2={}", pv.getNContributors(), pv.asString(), pv.getChi2()); + auto pvSave = pv; + pv = mVertexer.refitVertexFull(useTrack, pv); + LOGP(debug, "Refitted vtx: Nc:{} {}, chi2={}", ntr, pv.asString(), pv.getChi2()); + if (pv.getChi2() < 0.f) { + LOGP(warn, "Failed to refit PV {}", pvSave.asString()); + return false; + } + return true; +} + +bool CheckResidSpec::refitITStrack(o2::track::TrackParCov& track, GTrackID gid) +{ + // destination tack might have non-default PID assigned + const auto& trkITS = mRecoData->getITSTrack(gid); + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + const auto& params = CheckResidConfig::Instance(); + auto pid = track.getPID(); + track = trkITS.getParamOut(); + track.resetCovariance(); + track.setCov(track.getQ2Pt() * track.getQ2Pt() * track.getCov()[14], 14); + track.setPID(pid); + auto nCl = trkITS.getNumberOfClusters(); + auto geom = o2::its::GeometryTGeo::Instance(); + auto prop = o2::base::Propagator::Instance(); + float bz = prop->getNominalBz(); + o2::track::TrackPar refLin{track}; + + for (int iCl = 0; iCl < nCl; iCl++) { // clusters are stored from outer to inner layers + const auto& cls = mITSClustersArray[itsClRefs[trkITS.getClusterEntry(iCl)]]; + auto alpha = geom->getSensorRefAlpha(cls.getSensorID()); + if (!(params.useStableRef ? track.rotate(alpha, refLin, bz) : track.rotate(alpha)) || + !prop->propagateTo(track, params.useStableRef ? &refLin : nullptr, cls.getX(), true)) { + LOGP(debug, "refitITStrack failed on propagation to cl#{}, alpha={}, x={} | {}", iCl, alpha, cls.getX(), track.asString()); + return false; + } + if (!track.update(cls)) { + LOGP(debug, "refitITStrack failed on update with cl#{}, | {}", iCl, track.asString()); + return false; + } + } + return true; +} + +void CheckResidSpec::fillHistos(const o2::checkresid::Track& trc) +{ + const auto& params = CheckResidConfig::Instance(); + int np = trc.points.size(); + auto pt = trc.track.getPt(); + if (pt < params.minPt || pt > params.maxPt) { + return; + } + for (int ip = 0; ip < np; ip++) { + const auto& pnt = trc.points[ip]; + int il = pnt.lr >= 0 ? pnt.lr + 1 : 0; + mHMan->getHisto2F(il * 10 + 0 * 100)->Fill(pnt.phi, pnt.dy); + mHMan->getHisto2F(il * 10 + 0 * 100 + 1000)->Fill(pnt.z, pnt.dy); + mHMan->getHisto2F(il * 10 + 0 * 100 + 2000)->Fill(pt, pnt.dy); + mHMan->getHisto2F(il * 10 + 0 * 100 + 3000)->Fill(trc.track.getTgl(), pnt.dy); + if (pnt.sig2y > 0) { + auto pull = pnt.dy / std::sqrt(pnt.sig2y); + mHMan->getHisto2F(il * 10 + 0 * 100 + 5)->Fill(pnt.phi, pull); + mHMan->getHisto2F(il * 10 + 0 * 100 + 5 + 1000)->Fill(pnt.z, pull); + mHMan->getHisto2F(il * 10 + 0 * 100 + 5 + 2000)->Fill(pt, pull); + mHMan->getHisto2F(il * 10 + 0 * 100 + 5 + 3000)->Fill(trc.track.getTgl(), pull); + } + mHMan->getHisto2F(il * 10 + 1 * 100)->Fill(pnt.phi, pnt.dz); + mHMan->getHisto2F(il * 10 + 1 * 100 + 1000)->Fill(pnt.z, pnt.dz); + mHMan->getHisto2F(il * 10 + 1 * 100 + 2000)->Fill(pt, pnt.dz); + mHMan->getHisto2F(il * 10 + 1 * 100 + 3000)->Fill(trc.track.getTgl(), pnt.dz); + if (pnt.sig2z > 0) { + auto pull = pnt.dz / std::sqrt(pnt.sig2z); + mHMan->getHisto2F(il * 10 + 1 * 100 + 5)->Fill(pnt.phi, pull); + mHMan->getHisto2F(il * 10 + 1 * 100 + 5 + 1000)->Fill(pnt.z, pull); + mHMan->getHisto2F(il * 10 + 1 * 100 + 5 + 2000)->Fill(pt, pull); + mHMan->getHisto2F(il * 10 + 1 * 100 + 5 + 3000)->Fill(trc.track.getTgl(), pull); + } + } + //-------------- + if (trc.trIBOut.getX() > 1 && std::abs(trc.trIBOut.getX() - trc.trOBInw.getX()) < 0.1) { + for (int ip = 0; ip < 5; ip++) { + float d = trc.trIBOut.getParam(ip) - trc.trOBInw.getParam(ip); + mHMan->getHisto2F(10000 + ip * 10)->Fill(trc.trIBOut.getPhiPos(), d); + mHMan->getHisto2F(11000 + ip * 10)->Fill(trc.trIBOut.getZ(), d); + mHMan->getHisto2F(12000 + ip * 10)->Fill(pt, d); + mHMan->getHisto2F(13000 + ip * 10)->Fill(trc.track.getTgl(), d); + float sg = trc.trIBOut.getCovarElem(ip, ip) + trc.trOBInw.getCovarElem(ip, ip); + if (sg > 0) { + auto pull = d / std::sqrt(sg); + mHMan->getHisto2F(10000 + ip * 10 + 5)->Fill(trc.trIBOut.getPhiPos(), pull); + mHMan->getHisto2F(11000 + ip * 10 + 5)->Fill(trc.trIBOut.getZ(), pull); + mHMan->getHisto2F(12000 + ip * 10 + 5)->Fill(pt, pull); + mHMan->getHisto2F(13000 + ip * 10 + 5)->Fill(trc.track.getTgl(), pull); + } + } + } +} + +void CheckResidSpec::bookHistos() +{ + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + mHManV.emplace_back() = std::make_unique("", fmt::format("{}_hman.root", params.outname)); + mHMan = mHManV.back().get(); + mHMan->SetName(params.outname.c_str()); + auto defLogAxis = [](float xMn, float xMx, int nbin) { // get array for log axis + if (xMn <= 0 || xMx <= xMn || nbin < 2) { + LOGP(fatal, "Wrong log axis request: xmin = {} xmax = {} nbins = {}", xMn, xMx, nbin); + } + auto dx = std::log(xMx / xMn) / nbin; + std::vector xax(nbin + 1); + for (int i = 0; i <= nbin; i++) { + xax[i] = xMn * std::exp(dx * i); + } + return xax; + }; + float minPt = std::max(0.1f, params.minPt), maxPt = std::min(50.f, params.maxPt); + auto ptax = defLogAxis(minPt, maxPt, params.nBinsPt); + + for (int il = 0; il < 8; il++) { + std::string lrName = il == 0 ? "Vtx" : fmt::format("Lr{}", il - 1); + for (int iyz = 0; iyz < 2; iyz++) { + std::string dname = iyz == 0 ? "dy" : "dz", dtit = iyz == 0 ? "#DeltaY" : "#DeltaZ"; + auto h2 = new TH2F(fmt::format("{}_{}_{}", dname, lrName, "phi").c_str(), fmt::format("{}_{{{}}} vs {};#phi;{}", dtit, lrName, "#phi", dtit).c_str(), params.nBinsPhi, 0, TMath::Pi() * 2, params.nBinsRes, -params.maxDYZ[il], params.maxDYZ[il]); + mHMan->addHisto(h2, il * 10 + iyz * 100); + auto h2p = new TH2F(fmt::format("{}_{}_{}_pull", dname, lrName, "phi").c_str(), fmt::format("pull {}_{{{}}} vs {};#phi; pull{}", dtit, lrName, "phi", dtit).c_str(), params.nBinsPhi, 0, TMath::Pi() * 2, params.nBinsRes, -params.maxPull, params.maxPull); + mHMan->addHisto(h2p, il * 10 + iyz * 100 + 5); + + auto hz2 = new TH2F(fmt::format("{}_{}_{}", dname, lrName, "Z").c_str(), fmt::format("{}_{{{}}} vs {};Z;{}", dtit, lrName, "Z", dtit).c_str(), params.nBinsZ, -params.zranges[il], params.zranges[il], params.nBinsRes, -params.maxDYZ[il], params.maxDYZ[il]); + mHMan->addHisto(hz2, il * 10 + iyz * 100 + 1000); + auto hz2p = new TH2F(fmt::format("{}_{}_{}_pull", dname, lrName, "Z").c_str(), fmt::format("pull {}_{{{}}} vs {};Z; pull{}", dtit, lrName, "Z", dtit).c_str(), params.nBinsZ, -params.zranges[il], params.zranges[il], params.nBinsRes, -params.maxPull, params.maxPull); + mHMan->addHisto(hz2p, il * 10 + iyz * 100 + 5 + 1000); + + auto hpt2 = new TH2F(fmt::format("{}_{}_{}", dname, lrName, "Pt").c_str(), fmt::format("{}_{{{}}} vs {};p_{{T}};{}", dtit, lrName, "p_{T}", dtit).c_str(), params.nBinsPt, ptax.data(), params.nBinsRes, -params.maxDYZ[il], params.maxDYZ[il]); + mHMan->addHisto(hpt2, il * 10 + iyz * 100 + 2000); + auto hpt2p = new TH2F(fmt::format("{}_{}_{}_pull", dname, lrName, "Pt").c_str(), fmt::format("pull {}_{{{}}} vs {};p_{{T}}; pull{}", dtit, lrName, "p_{T}", dtit).c_str(), params.nBinsPt, ptax.data(), params.nBinsRes, -params.maxPull, params.maxPull); + mHMan->addHisto(hpt2p, il * 10 + iyz * 100 + 5 + 2000); + + auto htgl2 = new TH2F(fmt::format("{}_{}_{}", dname, lrName, "tgl").c_str(), fmt::format("{}_{{{}}} vs {};tg#lambda;{}", dtit, lrName, "tg#lambda", dtit).c_str(), params.nBinsTgl, -params.maxTgl, params.maxTgl, params.nBinsRes, -params.maxDYZ[il], params.maxDYZ[il]); + mHMan->addHisto(htgl2, il * 10 + iyz * 100 + 3000); + auto htgl2p = new TH2F(fmt::format("{}_{}_{}_pull", dname, lrName, "tgl").c_str(), fmt::format("pull {}_{{{}}} vs {};tg#lambda; pull{}", dtit, lrName, "tg#lambda", dtit).c_str(), params.nBinsTgl, -params.maxTgl, params.maxTgl, params.nBinsRes, -params.maxPull, params.maxPull); + mHMan->addHisto(htgl2p, il * 10 + iyz * 100 + 5 + 3000); + } + } + + for (int ip = 0; ip < 5; ip++) { + auto h2 = new TH2F(fmt::format("dPar{}_IBOBphi", ip).c_str(), fmt::format("#Delta par{} IB-OB vs phi;#phi;#Delta par{}", ip, ip).c_str(), params.nBinsPhi, 0, TMath::Pi() * 2, params.nBinsRes, -params.maxDPar[ip], params.maxDPar[ip]); + mHMan->addHisto(h2, 10000 + ip * 10); + auto h2p = new TH2F(fmt::format("dPar{}_IBOBphi_pull", ip).c_str(), fmt::format("pull #Delta par{} IB-OB vs phi;#phi;pull #Delta par{}", ip, ip).c_str(), params.nBinsPhi, 0, TMath::Pi() * 2, params.nBinsRes, -params.maxPull, params.maxPull); + mHMan->addHisto(h2p, 10000 + ip * 10 + 5); + + auto hz2 = new TH2F(fmt::format("dPar{}_IBOBz", ip).c_str(), fmt::format("#Delta par{} IB-OB vs Z;Z;#Delta par{}", ip, ip).c_str(), params.nBinsZ, -20., 20., params.nBinsRes, -params.maxDPar[ip], params.maxDPar[ip]); + mHMan->addHisto(hz2, 11000 + ip * 10); + auto hz2p = new TH2F(fmt::format("dPar{}_IBOBz_pull", ip).c_str(), fmt::format("pull #Delta par{} IB-OB vs Z;Z;pull #Delta par{}", ip, ip).c_str(), params.nBinsZ, -20., 20., params.nBinsRes, -params.maxPull, params.maxPull); + mHMan->addHisto(hz2p, 11000 + ip * 10 + 5); + + auto hpt2 = new TH2F(fmt::format("dPar{}_IBOBpt", ip).c_str(), fmt::format("#Delta par{} IB-OB vs pT;p_{{T}};#Delta par{}", ip, ip).c_str(), params.nBinsPt, ptax.data(), params.nBinsRes, -params.maxDPar[ip], params.maxDPar[ip]); + mHMan->addHisto(hpt2, 12000 + ip * 10); + auto hpt2p = new TH2F(fmt::format("dPar{}_IBOBpt_pull", ip).c_str(), fmt::format("pull #Delta par{} IB-OB vs pT;p_{{T}};pull #Delta par{}", ip, ip).c_str(), params.nBinsPt, ptax.data(), params.nBinsRes, -params.maxPull, params.maxPull); + mHMan->addHisto(hpt2p, 12000 + ip * 10 + 5); + + auto htgl2 = new TH2F(fmt::format("dPar{}_IBOBtgl", ip).c_str(), fmt::format("#Delta par{} IB-OB vs tg#lambda;tg#lambda;#Delta par{}", ip, ip).c_str(), params.nBinsTgl, -params.maxTgl, params.maxTgl, params.nBinsRes, -params.maxDPar[ip], params.maxDPar[ip]); + mHMan->addHisto(htgl2, 13000 + ip * 10); + auto htgl2p = new TH2F(fmt::format("dPar{}_IBOBtgl_pull", ip).c_str(), fmt::format("pull #Delta par{} IB-OB vs tg#lambda;tg#lambda;pull #Delta par{}", ip, ip).c_str(), params.nBinsTgl, -params.maxTgl, params.maxTgl, params.nBinsRes, -params.maxPull, params.maxPull); + mHMan->addHisto(htgl2p, 13000 + ip * 10 + 5); + } + // used PV + mHMan->addHisto(new TH1F("pvX", "PV X;x;", params.nBinsPVXYZ, -params.maxHPVXY, params.maxHPVXY), 20000 + 0); + mHMan->addHisto(new TH1F("pvY", "PV Y;y;", params.nBinsPVXYZ, -params.maxHPVXY, params.maxHPVXY), 20000 + 1); + mHMan->addHisto(new TH1F("pvZ", "PV Z;z;", params.nBinsPVXYZ, -params.maxHPVZ, params.maxHPVZ), 20000 + 2); + mHMan->addHisto(new TH1F("pvN", "PV Contributors;Nc;", params.maxHPVN, 1.5, params.maxHPVN + 1.5), 20000 + 3); +} + +void CheckResidSpec::postProcessHistos() +{ + printf("Fitting histos\n"); + if (!mHMan) { + if (mHManV.empty()) { + LOGP(warn, "nothing to process"); + return; + } + mHMan = mHManV[0].get(); + } + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + auto gs = new TF1("gs", "gaus", -1, 1); + int maxH = mPostProcOnly ? mHManV.size() : 1; + TObjArray arr; + for (int ihm = 0; ihm < maxH; ihm++) { + auto* histm = mHManV[ihm].get(); + auto fitSlices = [&](int id) { + auto h2 = histm->getHisto2F(id); + if (!h2 || h2->GetEntries() < params.minHistoStat2Fit) { + return; + } + h2->FitSlicesY(gs, 0, -1, 0, "QNR", &arr); + arr.SetOwner(true); + TH1* hmean = (TH1*)arr.RemoveAt(1); + if (hmean) { + hmean->SetTitle(Form("<%s>", h2->GetTitle())); + histm->addHisto(hmean, id + 1); + } + TH1* hsig = (TH1*)arr.RemoveAt(2); + if (hsig) { + hsig->SetTitle(Form("#sigma(%s)", h2->GetTitle())); + histm->addHisto(hsig, id + 2); + } + }; + for (int ioffs = 0; ioffs <= 3; ioffs++) { // vs phi, Z, pT, tgl + int offs = ioffs * 1000; + for (int iht = 0; iht < 2; iht++) { // resid, pull + int offsV = iht == 0 ? 0 : 5; + for (int il = 0; il < 8; il++) { + for (int iyz = 0; iyz < 2; iyz++) { + fitSlices(il * 10 + iyz * 100 + offsV + offs); + } + } + for (int ip = 0; ip < 5; ip++) { + fitSlices(10000 + ip * 10 + offsV + offs); + } + } + } + histm->write(); + } + delete gs; +} + +void CheckResidSpec::drawHistos() +{ + gROOT->SetBatch(true); + gStyle->SetTitleX(0.2); + gStyle->SetTitleY(0.88); + gStyle->SetTitleW(0.25); + gStyle->SetOptStat(0); + int nhm = mHManV.size(); + std::array hcol{EColor::kRed, EColor::kBlue, EColor::kGreen + 2}; + std::unique_ptr lg; + lg = std::make_unique(0.12, 0.13, 0.9, 0.13 + std::min(0.5f, nhm * 0.2f / 3.f)); + lg->SetFillStyle(0); + lg->SetBorderSize(0); + for (int i = 0; i < nhm; i++) { + auto hman = mHManV[i].get(); + if (!hman || hman->GetLast() < 1) { + continue; + } + hman->setMarkerStyle(20 + i + (i % 2) * 4, 0.5); + hman->setColor(hcol[i % hcol.size()]); + auto le = lg->AddEntry(hman->getHisto(1), hman->GetName(), "lp"); + le->SetTextColor(hcol[i % hcol.size()]); + } + TCanvas cly("cly", "", 600, 800), clz("clz", "", 600, 800), clpar("clpar", "", 600, 800); + TCanvas czly("czly", "", 600, 800), czlz("czlz", "", 600, 800), czlpar("czlpar", "", 600, 800); + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + + auto AddLabel = [](const char* txt, float x = 0.1, float y = 0.9, int color = kBlack, float size = 0.04) { + TLatex* lt = new TLatex(x, y, txt); + lt->SetNDC(); + lt->SetTextColor(color); + lt->SetTextSize(size); + lt->Draw(); + return lt; + }; + + auto drawResLr = [this](TCanvas& canv, int offs, const float resMM[8], bool logX) { + canv.Clear(); + canv.Divide(2, 4); + int nh = this->mHManV.size(); + for (int i = 0; i < 8; i++) { + canv.cd(i + 1); + bool same = false; + for (int j = 0; j < nh; j++) { + auto hman = this->mHManV[j].get(); + if (!hman || hman->GetLast() < 1) { + continue; + } + if (auto histo = hman->getHisto(10 * i + offs)) { + histo->Draw(same ? "same" : ""); + if (!same) { + histo->SetMinimum(-resMM[i]); + histo->SetMaximum(resMM[i]); + same = true; + } + } + } + gPad->SetGrid(); + gPad->SetLogx(logX); + } + }; + + auto drawResPar = [this](TCanvas& canv, int offs, const float resMM[8], bool logX) { + canv.Clear(); + canv.Divide(2, 3); + int nh = this->mHManV.size(); + for (int i = 0; i < 5; i++) { + canv.cd(i + 1); + bool same = false; + for (int j = 0; j < nh; j++) { + auto hman = this->mHManV[j].get(); + if (!hman || hman->GetLast() < 1) { + continue; + } + if (auto histo = hman->getHisto(10 * i + offs)) { + histo->Draw(same ? "same" : ""); + if (!same) { + histo->SetMinimum(-resMM[i]); + histo->SetMaximum(resMM[i]); + same = true; + } + } + } + gPad->SetGrid(); + gPad->SetLogx(logX); + } + }; + + cly.Print(Form("%s_hman.pdf[", params.outname.c_str())); + drawResLr(cly, 1, params.resMMLrY, false); + cly.cd(2); + lg->Draw(); + AddLabel("Y residuals", 0.1, 0.95); + cly.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResLr(clz, 101, params.resMMLrZ, false); + clz.cd(2); + lg->Draw(); + AddLabel("Z residuals", 0.1, 0.95); + clz.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResLr(czly, 1001, params.resMMLrY, false); + czly.cd(2); + lg->Draw(); + AddLabel("Y residuals", 0.1, 0.95); + czly.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResLr(czlz, 1101, params.resMMLrZ, false); + czlz.cd(2); + lg->Draw(); + AddLabel("Z residuals", 0.1, 0.95); + czlz.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResLr(czly, 2001, params.resMMLrY, true); + czly.cd(2); + lg->Draw(); + AddLabel("Y residuals", 0.1, 0.95); + czly.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResLr(czlz, 2101, params.resMMLrZ, true); + czlz.cd(2); + lg->Draw(); + AddLabel("Z residuals", 0.1, 0.95); + czlz.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResLr(czly, 3001, params.resMMLrY, false); + czly.cd(2); + lg->Draw(); + AddLabel("Y residuals", 0.1, 0.95); + czly.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResLr(czlz, 3101, params.resMMLrZ, false); + czlz.cd(2); + lg->Draw(); + AddLabel("Z residuals", 0.1, 0.95); + czlz.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResPar(clpar, 10001, params.resMMPar, false); + clpar.cd(6); + lg->Draw(); + AddLabel("IB-OB tracks params differences at R = 12 cm", 0.2, 0.8); + clpar.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResPar(czlpar, 11001, params.resMMPar, false); + czlpar.cd(6); + lg->Draw(); + AddLabel("IB-OB tracks params differences at R = 12 cm", 0.2, 0.8); + czlpar.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResPar(czlpar, 12001, params.resMMPar, true); + czlpar.cd(6); + lg->Draw(); + AddLabel("IB-OB tracks params differences at R = 12 cm", 0.2, 0.8); + czlpar.Print(Form("%s_hman.pdf", params.outname.c_str())); + + drawResPar(czlpar, 13001, params.resMMPar, false); + czlpar.cd(6); + lg->Draw(); + AddLabel("IB-OB tracks params differences at R = 12 cm", 0.2, 0.8); + czlpar.Print(Form("%s_hman.pdf", params.outname.c_str())); + + cly.Print(Form("%s_hman.pdf]", params.outname.c_str())); +} + +void CheckResidSpec::endOfStream(EndOfStreamContext& ec) +{ + mDBGOut.reset(); + if (mHManV.size()) { + postProcessHistos(); + } + if (mDraw) { + drawHistos(); + } +} + +void CheckResidSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mMeanVtx = *(const o2::dataformats::MeanVertexObject*)obj; + mMeanVertexUpdated = true; + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mITSDict = (const o2::itsmft::TopologyDictionary*)obj; + return; + } +} + +DataProcessorSpec getCheckResidSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool drawOnly, bool postProcOnly) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + if (!drawOnly && !postProcOnly) { + bool useMC = false; + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcClusters, useMC); + dataRequest->requestPrimaryVertices(useMC); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + } + auto ggRequest = drawOnly ? std::make_shared(false, false, false, false, false, o2::base::GRPGeomRequest::None, dataRequest->inputs) : std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, true); + Options opts; + if (!drawOnly) { + opts = Options{ + {"nthreads", VariantType::Int, 1, {"number of threads"}}, + {"no-tree", VariantType::Bool, false, {"do not fill residuals tree"}}, + {"no-hist", VariantType::Bool, false, {"do not fill residuals histograms"}}, + {"draw-report", VariantType::Bool, false, {"fill residuals report"}}, + }; + } + + return DataProcessorSpec{ + "check-resid", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, drawOnly, postProcOnly)}, + opts}; +} + +} // namespace o2::checkresid diff --git a/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx b/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx new file mode 100644 index 0000000000000..8c7931d599e93 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx @@ -0,0 +1,322 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "CommonDataFormat/BunchFilling.h" +#include "CommonUtils/NameConf.h" +#include "DataFormatsFT0/RecPoints.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "FT0Reconstruction/InteractionTag.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "GlobalTrackingStudy/TrackingStudy.h" +#include "TPCBase/ParameterElectronics.h" +#include "TPCBase/ParameterDetector.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "TPCCalibration/VDriftHelper.h" +#include "DetectorsVertexing/PVertexerParams.h" + +namespace o2::trackstudy +{ + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; + +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using GTrackID = o2::dataformats::GlobalTrackID; +using TBracket = o2::math_utils::Bracketf_t; + +using timeEst = o2::dataformats::TimeStampWithError; + +class DumpTracksSpec final : public Task +{ + public: + DumpTracksSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} + ~DumpTracksSpec() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(o2::globaltracking::RecoContainer& recoData); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + bool mUseMC{false}; ///< MC flag + std::unique_ptr mDBGOut; + GTrackID::mask_t mTracksSrc{}; + + std::vector mGlobalBC; + o2::tpc::VDriftHelper mTPCVDriftHelper{}; + int mBCToler = 0; // tolerance in BC for globalBC selections + int mVerbose = 0; + float mITSROFrameLengthMUS = 0; ///< ITS RO frame in mus + float mMFTROFrameLengthMUS = 0; ///< MFT RO frame in mus + float mMaxTPCDriftTimeMUS = 0; + float mTPCTDriftOffset = 0.f; + float mTPCBin2MUS = 0; +}; + +void DumpTracksSpec::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mDBGOut = std::make_unique("trackStudy.root", "recreate"); + + mBCToler = ic.options().get("bc-margin"); + mVerbose = ic.options().get("dump-verbosity"); + auto bcstr = ic.options().get("sel-bc"); + auto bctok = o2::utils::Str::tokenize(bcstr, ','); + if (bctok.empty()) { + LOG(error) << "empty BC list is provided " << bcstr; + } + for (auto& bcs : bctok) { + try { + long bcglo = std::stol(bcs); + mGlobalBC.push_back(bcglo); + LOGP(info, "adding {} to global BCs to dump", bcglo); + } catch (...) { + LOGP(fatal, "failed to extract global BC from {}", bcstr); + } + } +} + +void DumpTracksSpec::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(recoData); +} + +void DumpTracksSpec::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + mTPCVDriftHelper.extractCCDBInputs(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + // put here init-once stuff + const auto& alpParamsITS = o2::itsmft::DPLAlpideParam::Instance(); + mITSROFrameLengthMUS = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS) ? alpParamsITS.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingMUS : alpParamsITS.roFrameLengthTrig * 1.e-3; + const auto& alpParamsMFT = o2::itsmft::DPLAlpideParam::Instance(); + mMFTROFrameLengthMUS = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::MFT) ? alpParamsMFT.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingMUS : alpParamsMFT.roFrameLengthTrig * 1.e-3; + LOGP(info, "VertexTrackMatcher ITSROFrameLengthMUS:{} MFTROFrameLengthMUS:{}", mITSROFrameLengthMUS, mMFTROFrameLengthMUS); + } + // we may have other params which need to be queried regularly + // VDrift may change from time to time + if (mTPCVDriftHelper.isUpdated()) { + auto& elParam = o2::tpc::ParameterElectronics::Instance(); + auto& detParam = o2::tpc::ParameterDetector::Instance(); + mTPCBin2MUS = elParam.ZbinWidth; + auto& vd = mTPCVDriftHelper.getVDriftObject(); + mMaxTPCDriftTimeMUS = detParam.TPClength / (vd.refVDrift * vd.corrFact); + mTPCTDriftOffset = vd.getTimeOffset(); + } +} + +void DumpTracksSpec::process(o2::globaltracking::RecoContainer& recoData) +{ + std::vector selBCTF; + auto irMin = recoData.startIR; + auto irMax = irMin + o2::base::GRPGeomHelper::instance().getNHBFPerTF() * o2::constants::lhc::LHCMaxBunches; + float tBCErr = mBCToler * o2::constants::lhc::LHCBunchSpacingMUS; + LOGP(info, "TF dump for {}:{}", irMin.asString(), irMax.asString()); + + for (const auto& bc : mGlobalBC) { + if (bc >= irMin.toLong() && bc < irMax.toLong()) { + o2::InteractionRecord bcir(bc % o2::constants::lhc::LHCMaxBunches, bc / o2::constants::lhc::LHCMaxBunches); + float t = (bc - irMin.toLong()) * o2::constants::lhc::LHCBunchSpacingMUS; + LOGP(info, "Selected BC {}({}) -> {}({}) : {}({}) mus", bc, bcir.asString(), + t - tBCErr, (bcir - mBCToler).asString(), + t + tBCErr, (bcir + mBCToler).asString()); + selBCTF.emplace_back(t - tBCErr, t + tBCErr); + } + } + if (selBCTF.empty()) { + LOGP(info, "No selections for {}:{}", irMin.asString(), irMax.asString()); + return; + } + + auto pvvec = recoData.getPrimaryVertices(); + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + + float itsBias = 0.5 * mITSROFrameLengthMUS + o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; // ITS time is supplied in \mus as beginning of ROF + float mftBias = 0.5 * mMFTROFrameLengthMUS + o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; // MFT time is supplied in \mus as beginning of ROF + + GTrackID::Source prevSrc = GTrackID::Source(-1); + auto creator = [this, &selBCTF, itsBias, mftBias, &recoData, &prevSrc](auto& _tr, GTrackID _origID, float t0, float terr) { + const auto& PVParams = o2::vertexing::PVertexerParams::Instance(); + if constexpr (isTPCTrack()) { + // unconstrained TPC track, with t0 = TrackTPC.getTime0+0.5*(DeltaFwd-DeltaBwd) and terr = 0.5*(DeltaFwd+DeltaBwd) in TimeBins + t0 *= this->mTPCBin2MUS; + t0 -= this->mTPCTDriftOffset; + terr *= this->mTPCBin2MUS; + } else if constexpr (isITSTrack()) { + t0 += itsBias; + terr *= this->mITSROFrameLengthMUS; // error is supplied as a half-ROF duration, convert to \mus + } else if constexpr (isMFTTrack()) { // Same for MFT + t0 += mftBias; + terr *= this->mMFTROFrameLengthMUS; + } else if constexpr (!(isMCHTrack() || isGlobalFwdTrack())) { + // for all other tracks the time is in \mus with gaussian error + terr *= PVParams.nSigmaTimeTrack; // gaussian errors must be scaled by requested n-sigma + } + + terr += PVParams.timeMarginTrackTime; + TBracket tb{t0 - terr, t0 + terr}; + for (const auto& stb : selBCTF) { + if (tb.isOutside(stb)) { + return false; + } + } + auto curSrc = GTrackID::Source(_origID.getSource()); + if (prevSrc != curSrc) { + prevSrc = curSrc; + LOGP(info, "Dumping {} tracks", GTrackID::getSourceName(prevSrc)); + } + + std::string outs; + if constexpr (isGlobalFwdTrack() || isMFTTrack() || isMCHTrack() || isMIDTrack()) { + outs = fmt::format("{:>15} {:8.3f}/{:5.3f} -> ", _origID.asString(), t0, terr); + } else { + outs = fmt::format("{:>15} {:8.3f}/{:5.3f} |{}| -> ", _origID.asString(), t0, terr, ((o2::track::TrackPar)_tr).asString()); + } + + // contributions + auto refs = recoData.getSingleDetectorRefs(_origID); + for (auto r : refs) { + if (r.isSourceSet()) { + outs += fmt::format(" {}", r.asString()); + } + } + LOG(info) << outs; + return false; // allow redundancy + }; + + recoData.createTracksVariadic(creator); + + // print matching vertices + int pvcnt = 0; + for (const auto& pv : pvvec) { + TBracket pbv{pv.getIRMin().differenceInBCMUS(irMin), pv.getIRMax().differenceInBCMUS(irMin)}; + for (const auto& stb : selBCTF) { + if (!stb.isOutside(pbv)) { + LOG(info) << "#" << pvcnt << " " << pv; + LOG(info) << "References: " << vtxRefs[pvcnt]; + for (int is = 0; is < VTIndex::NSources; is++) { + int ncontrib = 0, nambig = 0; + int idMin = vtxRefs[pvcnt].getFirstEntryOfSource(is), idMax = idMin + vtxRefs[pvcnt].getEntriesOfSource(is); + for (int i = idMin; i < idMax; i++) { + if (trackIndex[i].isPVContributor()) { + ncontrib++; + } else if (trackIndex[i].isAmbiguous()) { + nambig++; + } + } + if (vtxRefs[pvcnt].getEntriesOfSource(is)) { + LOGP(info, "{} : total attached: {}, contributors: {}, ambiguous: {}", VTIndex::getSourceName(is), vtxRefs[pvcnt].getEntriesOfSource(is), ncontrib, nambig); + } + if (mVerbose < 2) { + continue; + } + std::string trIDs; + int cntT = 0; + for (int i = idMin; i < idMax; i++) { + if (mVerbose > 2 || trackIndex[i].isPVContributor()) { + trIDs += trackIndex[i].asString() + " "; + if (!((++cntT) % 15)) { + LOG(info) << trIDs; + trIDs = ""; + } + } + } + if (!trIDs.empty()) { + LOG(info) << trIDs; + } + } + } + pvcnt++; + } + } +} + +void DumpTracksSpec::endOfStream(EndOfStreamContext& ec) +{ + mDBGOut.reset(); +} + +void DumpTracksSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { + LOG(info) << "ITS Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); + return; + } + if (matcher == ConcreteDataMatcher("MFT", "ALPIDEPARAM", 0)) { + LOG(info) << "MFT Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); + return; + } +} + +DataProcessorSpec getDumpTracksSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcClusters, useMC); + dataRequest->requestPrimaryVertices(useMC); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + dataRequest->inputs, + true); + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + + return DataProcessorSpec{ + "tracks-dump", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, + Options{ + {"sel-bc", VariantType::String, "", {"Dump tracks compatible with global BC list"}}, + {"bc-margin", VariantType::Int, 0, {"Apply margin in BC to selected global BCs list"}}, + {"dump-verbosity", VariantType::Int, 0, {"Dump verbosity level"}}}}; +} + +} // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h index c85dd2d378ccb..b1fe732fbefc4 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h +++ b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h @@ -15,4 +15,40 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::dataformats::ProngInfoExt + ; +#pragma link C++ class o2::dataformats::V0Ext + ; +#pragma link C++ class o2::dataformats::TrackInfoExt + ; +#pragma link C++ class std::vector < o2::dataformats::TrackInfoExt> + ; +#pragma link C++ class std::vector < o2::dataformats::ProngInfoExt> + ; +#pragma link C++ class std::vector < o2::dataformats::V0Ext> + ; +#pragma link C++ class o2::trackstudy::TrackMCStudyConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trackstudy::TrackMCStudyConfig> + ; +#pragma link C++ class o2::trackstudy::RecTrack + ; +#pragma link C++ class std::vector < o2::trackstudy::RecTrack> + ; +#pragma link C++ class o2::trackstudy::TrackFamily + ; +#pragma link C++ class std::vector < o2::trackstudy::TrackFamily> + ; +#pragma link C++ class o2::trackstudy::MCTrackInfo + ; +#pragma link C++ class std::vector < o2::trackstudy::MCTrackInfo> + ; +#pragma link C++ class o2::trackstudy::RecPV + ; +#pragma link C++ class std::vector < o2::trackstudy::RecPV> + ; +#pragma link C++ class o2::trackstudy::MCVertex + ; +#pragma link C++ class std::vector < o2::trackstudy::MCVertex> + ; +#pragma link C++ class o2::trackstudy::ClResTPC + ; +#pragma link C++ class o2::trackstudy::ClResTPCCont + ; +#pragma link C++ class std::vector < o2::trackstudy::ClResTPCCont> + ; +#pragma link C++ class o2::trackstudy::TrackPairInfo + ; +#pragma link C++ class std::vector < o2::trackstudy::TrackPairInfo> + ; +#pragma ling C++ class o2::tpc::TPCClusSelector + ; + +#pragma link C++ class o2::trackstudy::ITSHitInfo + ; +#pragma link C++ class std::vector < o2::trackstudy::ITSHitInfo> + ; + +#pragma link C++ class o2::checkresid::Point + ; +#pragma link C++ class std::vector < o2::checkresid::Point> + ; +#pragma link C++ class o2::checkresid::Track + ; +#pragma link C++ class o2::checkresid::CheckResidConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::checkresid::CheckResidConfig> + ; + +#pragma link C++ class o2::HistoManager + ; + #endif diff --git a/Detectors/GlobalTrackingWorkflow/study/src/HistoManager.cxx b/Detectors/GlobalTrackingWorkflow/study/src/HistoManager.cxx new file mode 100644 index 0000000000000..23ab2e8b923c8 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/HistoManager.cxx @@ -0,0 +1,513 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Framework/Logger.h" +#include "GlobalTrackingStudy/HistoManager.h" + +namespace o2 +{ + +HistoManager::HistoManager(const std::string& dirname, const std::string& fname, bool load, const std::string& prefix) : mDirName(dirname) +{ + setFileName(fname); + if (load && !mDefName.empty()) { + int nh = this->load(fname, dirname); + LOGP(info, "HistoManager::load was requested: got {} histos from {}/{}", nh, fname, dirname); + if (!prefix.empty()) { + addPrefix(prefix); + } + } +} + +HistoManager* HistoManager::createClone(const std::string& prefix) const +{ + auto* hm = static_cast(Clone()); + hm->addPrefix(prefix); + for (int i = 0; i < GetLast() + 1; ++i) { + TObject* obj = hm->UncheckedAt(i); + if (!obj) { + continue; + } + if (auto* histo = dynamic_cast(obj)) { + histo->SetDirectory(nullptr); + } + } + hm->mNHistos = mNHistos; + hm->setFileName(mDefName); + hm->setDirName(mDirName); + return hm; +} + +int HistoManager::addHisto(TH1* histo, int at) +{ + if (!histo) { + return mNHistos; + } + if (at < 0) { + at = mNHistos; + } + AddAtAndExpand(histo, at); + histo->SetDirectory(nullptr); + histo->SetUniqueID(at + 1); + return mNHistos++; +} + +TGraph* HistoManager::getGraph(int id) const +{ + return id <= GetLast() ? dynamic_cast(UncheckedAt(id)) : nullptr; +} + +TH1* HistoManager::getHisto(int id) const +{ + return id <= GetLast() ? dynamic_cast(UncheckedAt(id)) : nullptr; +} + +TH1* HistoManager::getHisto(const std::string& name) const +{ + return dynamic_cast(FindObject(name.c_str())); +} + +TH1F* HistoManager::getHisto1F(int id) const +{ + return dynamic_cast(UncheckedAt(id)); +} + +TH2F* HistoManager::getHisto2F(int id) const +{ + return dynamic_cast(UncheckedAt(id)); +} + +TProfile* HistoManager::getHistoP(int id) const +{ + return dynamic_cast(UncheckedAt(id)); +} + +int HistoManager::addGraph(TGraph* gr, int at) +{ + if (!gr) { + return mNHistos; + } + if (at < 0) { + at = mNHistos; + } + AddAtAndExpand(gr, at); + gr->SetUniqueID(at + 1); + return mNHistos++; +} + +void HistoManager::Compress() +{ + TObjArray::Compress(); + for (int i = 0; i < GetLast() + 1; ++i) { + if (TObject* histo = At(i)) { + histo->SetUniqueID(i + 1); + } + } +} + +void HistoManager::write(TFile* file) +{ + if (!mNHistos) { + return; + } + + bool localFile = kFALSE; + TFile* lfile = nullptr; + const char* dirName = nullptr; + if (file) { + lfile = file; + } else { + auto* tmpF = static_cast(gROOT->GetListOfFiles()->FindObject(mDefName.c_str())); + if (tmpF && tmpF->IsOpen()) { + TString opt = tmpF->GetOption(); + opt.ToLower(); + if (!opt.Contains("read")) { + lfile = tmpF; + tmpF->cd(); + } + } + } + + TString pwd = gDirectory->GetPath(); + if (!lfile) { + std::string originalName = mDefName; + if (mDefName.empty() || mDefName[0] == ' ') { + mDefName = "histoman"; + } + TString rootName = mDefName.c_str(); + if (!rootName.Contains(".root")) { + mDefName += ".root"; + } + lfile = TFile::Open(mDefName.c_str(), "UPDATE"); + mDefName = originalName; + localFile = kTRUE; + } + + lfile->cd(); + dirName = mDirName.c_str(); + if (dirName && dirName[0] && dirName[0] != ' ') { + if (!lfile->Get(dirName)) { + lfile->mkdir(dirName); + } + lfile->cd(dirName); + } + LOGP(info, "Writing histograms to: {}/{}", lfile->GetPath(), dirName); + + for (int i = 0; i < GetLast() + 1; ++i) { + TObject* obj = UncheckedAt(i); + if (!obj) { + continue; + } + auto* histo = dynamic_cast(obj); + TDirectory* dr = nullptr; + if (histo) { + dr = histo->GetDirectory(); + histo->SetDirectory(nullptr); + } + obj->Write(nullptr, TObject::kOverwrite); + if (dr && histo) { + histo->SetDirectory(dr); + } + } + + if (localFile) { + lfile->Close(); + delete lfile; + } + auto* oldDir = static_cast(gROOT->GetListOfFiles()->FindObject(pwd.Data())); + if (oldDir) { + oldDir->cd(); + } +} + +void HistoManager::Clear(Option_t*) +{ + int nent = GetLast() + 1; + for (int i = 0; i < nent; ++i) { + TObject* hh = UncheckedAt(i); + if (!hh) { + continue; + } + RemoveAt(i); + --mNHistos; + } +} + +//_______________________________________________________________ +void HistoManager::Delete(Option_t*) +{ + int nent = GetLast() + 1; + for (int i = 0; i < nent; ++i) { + TObject* hh = UncheckedAt(i); + if (!hh) { + continue; + } + RemoveAt(i); + delete hh; + } + mNHistos = 0; +} + +void HistoManager::Print(Option_t* option) const +{ + int nent = GetLast() + 1; + for (int i = 0; i < nent; ++i) { + TObject* hh = UncheckedAt(i); + if (!hh) { + continue; + } + LOGP(info, "At position #{}", i); + hh->Print(option); + } + LOGP(info, "Total number of defined histograms: %d", mNHistos); + LOGP(info, "Current output path: {}/{}", mDefName, mDirName); +} + +void HistoManager::addPrefix(const std::string& pref) +{ + if (pref.empty()) { + return; + } + int nent = GetLast() + 1; + for (int i = 0; i < nent; ++i) { + TObject* hh = UncheckedAt(i); + if (!hh) { + continue; + } + if (hh->InheritsFrom("TNamed")) { + auto name = pref + hh->GetName(); + static_cast(hh)->SetName(name.c_str()); + } + } +} + +void HistoManager::addHistos(const HistoManager* hm, Double_t c1) +{ + if (!hm) { + return; + } + int nent = GetLast() + 1; + int nent1 = hm->GetLast() + 1; + if (nent != nent1) { + Error("addHistos", "HistoManagers have different content: %d vs %d", nent, nent1); + return; + } + for (int i = 0; i < nent; ++i) { + TH1* hh1 = getHisto(i); + TH1* hh2 = hm->getHisto(i); + if (!hh1 || !hh2) { + continue; + } + hh1->Add(hh2, c1); + } +} + +void HistoManager::divideHistos(const HistoManager* hm) +{ + if (!hm) { + return; + } + int nent = GetLast() + 1; + int nent1 = hm->GetLast() + 1; + if (nent != nent1) { + Error("divideHistos", "HistoManagers have different content: %d vs %d", nent, nent1); + return; + } + for (int i = 0; i < nent; ++i) { + TH1* hh1 = getHisto(i); + TH1* hh2 = hm->getHisto(i); + if (!hh1 || !hh2) { + continue; + } + hh1->Divide(hh2); + } +} + +//_______________________________________________________________ +void HistoManager::multiplyHistos(const HistoManager* hm) +{ + if (!hm) { + return; + } + int nent = GetLast() + 1; + int nent1 = hm->GetLast() + 1; + if (nent != nent1) { + Error("multiplyHistos", "HistoManagers have different content: %d vs %d", nent, nent1); + return; + } + for (int i = 0; i < nent; ++i) { + TH1* hh1 = getHisto(i); + TH1* hh2 = hm->getHisto(i); + if (!hh1 || !hh2) { + continue; + } + hh1->Multiply(hh2); + } +} + +void HistoManager::scaleHistos(Double_t c1) +{ + int nent = GetLast() + 1; + for (int i = 0; i < nent; ++i) { + TH1* hh1 = getHisto(i); + if (hh1) { + hh1->Scale(c1); + } + } +} + +void HistoManager::sumw2() +{ + int nent = GetLast() + 1; + for (int i = 0; i < nent; ++i) { + auto* hh1 = dynamic_cast(UncheckedAt(i)); + if (hh1) { + hh1->Sumw2(); + } + } +} + +void HistoManager::setFile(TFile* file) +{ + if (file) { + mDefName = file->GetName(); + } +} + +void HistoManager::delHisto(int at) +{ + TH1* hist = getHisto(at); + if (hist) { + RemoveAt(at); + delete hist; + --mNHistos; + } +} + +void HistoManager::purify(bool emptyToo) +{ + int last = GetLast() + 1; + if (emptyToo) { + for (int i = 0; i < last; ++i) { + TH1* hist = getHisto(i); + if (!hist) { + continue; + } + if (hist->GetEntries() < 1) { + delHisto(i); + } + } + } + Compress(); +} + +void HistoManager::setFileName(const std::string& name) +{ + TString sName = name; + gSystem->ExpandPathName(sName); + mDefName = sName.Data(); +} + +void HistoManager::reset() +{ + int last = GetLast() + 1; + for (int i = 0; i < last; ++i) { + TH1* hist = getHisto(i); + if (!hist) { + continue; + } + hist->Reset(); + } +} + +int HistoManager::load(const std::string& fname, const std::string& dirname) +{ + TString sName = fname; + if (gSystem->ExpandPathName(sName)) { + LOGP(error, "Cannot expand file name {}", fname); + return 0; + } + TFile* file = TFile::Open(sName); + if (!file) { + LOGP(error, "No file {}", fname); + return 0; + } + if (!dirname.empty() && dirname[0] != ' ') { + if (!file->Get(dirname.c_str())) { + LOGP(error, "No {} directory in file {}", dirname, fname); + file->Close(); + delete file; + return 0; + } + file->cd(dirname.c_str()); + } + int count = 0; + TList* lst = gDirectory->GetListOfKeys(); + TIter nextKey(lst); + TKey* key = nullptr; + while ((key = static_cast(nextKey()))) { + if (FindObject(key->GetName())) { + continue; + } + TString clName = key->GetClassName(); + if (!(clName.BeginsWith("TH") || clName.BeginsWith("TProfile") || clName.BeginsWith("TGraph"))) { + printf("Object %s of type %s is not processed\n", key->GetName(), clName.Data()); + continue; + } + TObject* hst = key->ReadObj(); + int id = hst->GetUniqueID(); + if (auto* h = dynamic_cast(hst)) { + addHisto(h, id - 1); + ++count; + continue; + } + if (auto* gr = dynamic_cast(hst)) { + addGraph(gr, id - 1); + ++count; + } + } + file->Close(); + delete file; + auto nm = fname; + if (!dirname.empty()) { + nm += fmt::format("/{}", dirname); + } + SetName(nm.c_str()); + return count; +} + +void HistoManager::setColor(int tcolor) +{ + int last = GetLast() + 1; + for (int i = 0; i < last; ++i) { + TH1* hist = getHisto(i); + if (!hist) { + continue; + } + hist->SetLineColor(tcolor); + hist->SetMarkerColor(tcolor); + TList* lst = hist->GetListOfFunctions(); + if (lst) { + int nf = lst->GetSize(); + for (int j = 0; j < nf; ++j) { + TObject* fnc = lst->At(j); + if (fnc->InheritsFrom("TF1")) { + static_cast(fnc)->SetLineColor(tcolor); + static_cast(fnc)->SetLineWidth(1); + static_cast(fnc)->ResetBit(TF1::kNotDraw); + } else if (fnc->InheritsFrom("TPaveStats")) { + static_cast(fnc)->SetTextColor(tcolor); + } + } + } + } +} + +void HistoManager::setMarkerStyle(Style_t mstyle, Size_t msize) +{ + int last = GetLast() + 1; + for (int i = 0; i < last; ++i) { + TH1* hist = getHisto(i); + if (!hist) { + continue; + } + hist->SetMarkerStyle(mstyle); + hist->SetMarkerSize(msize); + } +} + +void HistoManager::setMarkerSize(Size_t msize) +{ + int last = GetLast() + 1; + for (int i = 0; i < last; ++i) { + TH1* hist = getHisto(i); + if (!hist) { + continue; + } + hist->SetMarkerSize(msize); + } +} + +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/study/src/ITSOffsStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/ITSOffsStudy.cxx new file mode 100644 index 0000000000000..484782dd9095c --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/ITSOffsStudy.cxx @@ -0,0 +1,175 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/ITSOffsStudy.h" + +#include +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "CommonDataFormat/BunchFilling.h" +#include "CommonUtils/NameConf.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include + +namespace o2::trackstudy +{ + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; + +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using GTrackID = o2::dataformats::GlobalTrackID; + +using timeEst = o2::dataformats::TimeStampWithError; + +class ITSOffsStudy final : public Task +{ + public: + ITSOffsStudy(std::shared_ptr dr, GTrackID::mask_t src) : mDataRequest(dr), mTracksSrc(src) {} + ~ITSOffsStudy() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(o2::globaltracking::RecoContainer& recoData); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + std::shared_ptr mDataRequest; + std::unique_ptr mDBGOut; + GTrackID::mask_t mTracksSrc{}; + std::unique_ptr mDTHisto{}; + const std::string mOutName{"its_offset_Study.root"}; +}; + +void ITSOffsStudy::init(InitContext& ic) +{ + mDBGOut = std::make_unique(mOutName.c_str(), "recreate"); + mDTHisto = std::make_unique("dT", "T_{TOF} - T_{ITS-ROF}, #mus", 1000, 1, -1); + mDTHisto->SetDirectory(nullptr); +} + +void ITSOffsStudy::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(recoData); +} + +void ITSOffsStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + } +} + +void ITSOffsStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + constexpr float PS2MUS = 1e-6; + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto tofClusters = recoData.getTOFClusters(); + auto itsROFs = recoData.getITSTracksROFRecords(); + std::vector itsTr2ROFID; + std::unordered_map ambigMap; + + int cntROF = 0; + for (const auto& rof : itsROFs) { + size_t maxE = rof.getFirstEntry() + rof.getNEntries(); + if (itsTr2ROFID.size() < maxE) { + itsTr2ROFID.resize(maxE, cntROF); + } + cntROF++; + } + + int nv = vtxRefs.size(); + for (int iv = 0; iv < nv; iv++) { + const auto& vtref = vtxRefs[iv]; + for (int is = 0; is < GTrackID::NSources; is++) { + if (!mTracksSrc[is] || !(GTrackID::getSourceDetectorsMask(is)[GTrackID::ITS] && GTrackID::getSourceDetectorsMask(is)[GTrackID::TOF])) { + continue; + } + int idMin = vtxRefs[iv].getFirstEntryOfSource(is), idMax = idMin + vtxRefs[iv].getEntriesOfSource(is); + for (int i = idMin; i < idMax; i++) { + auto vid = trackIndex[i]; + if (vid.isAmbiguous()) { + auto& ambEntry = ambigMap[vid]; + if (ambEntry > 0) { + continue; + } + ambEntry = 1; + } + auto tofMatch = recoData.getTOFMatch(vid); + auto refs = recoData.getSingleDetectorRefs(vid); + if (!refs[GTrackID::ITS].isIndexSet()) { // might be an afterburner track + continue; + } + const auto& tofCl = tofClusters[refs[GTrackID::TOF]]; + float timeTOFMUS = (tofCl.getTime() - recoData.getTOFMatch(vid).getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; + + int itsTrackID = refs[GTrackID::ITS].getIndex(); + // find the ROF corresponding to this track + const auto& rof = itsROFs[itsTr2ROFID[itsTrackID]]; + auto tsROF = rof.getBCData().differenceInBC(recoData.startIR) * o2::constants::lhc::LHCBunchSpacingMUS; + (*mDBGOut) << "itstof" + << "gid=" << vid << "ttof=" << timeTOFMUS << "tits=" << tsROF << "itsROFID=" << itsTr2ROFID[itsTrackID] << "\n"; + mDTHisto->Fill(timeTOFMUS - tsROF); + const auto& trc = recoData.getTrackParam(tofMatch.getTrackRef()); + (*mDBGOut) << "dttof" + << "refgid=" << tofMatch.getTrackRef() << "dtime=" << tofMatch.getDeltaT() << "phi=" << trc.getPhi() << "tgl=" << trc.getTgl() << "q2t=" << trc.getQ2Pt() << "\n"; + } + } + } +} + +void ITSOffsStudy::endOfStream(EndOfStreamContext& ec) +{ + mDBGOut.reset(); + TFile fout(mOutName.c_str(), "update"); + fout.WriteTObject(mDTHisto.get()); + LOGP(info, "Stored time differences histogram {} and tree {} into {}", mDTHisto->GetName(), "itstof", mOutName.c_str()); + fout.Close(); +} + +void ITSOffsStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ +} + +DataProcessorSpec getITSOffsStudy(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + bool useMC = false; + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcClusters, useMC); + dataRequest->requestPrimaryVertices(useMC); + + return DataProcessorSpec{ + "its-offset-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, srcTracks)}, + Options{}}; +} + +} // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx new file mode 100644 index 0000000000000..4d0b6bdbdb213 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx @@ -0,0 +1,450 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "DetectorsVertexing/SVertexerParams.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/V0.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "SimulationDataFormat/MCUtils.h" +#include "SimulationDataFormat/MCTrack.h" +#include "CommonDataFormat/BunchFilling.h" +#include "CommonUtils/NameConf.h" +#include "DataFormatsFT0/RecPoints.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "FT0Reconstruction/InteractionTag.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "GlobalTrackingStudy/TrackingStudy.h" +#include "TPCBase/ParameterElectronics.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "DataFormatsFT0/RecPoints.h" +#include "DataFormatsITSMFT/TrkClusRef.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ReconstructionDataFormats/DCA.h" +#include "Steer/MCKinematicsReader.h" +#include "DCAFitter/DCAFitterN.h" +#include "MathUtils/fit.h" +#include "GlobalTrackingStudy/V0Ext.h" +#include "GPUO2InterfaceConfiguration.h" +// #include "GPUSettingsO2.h" +#include "GPUParam.h" +#include "GPUParam.inc" +#include "GPUTPCGeometry.h" +#include "GPUO2InterfaceRefit.h" +#include "GPUO2InterfaceUtils.h" + +namespace o2::svstudy +{ + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; + +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using GTrackID = o2::dataformats::GlobalTrackID; +using TBracket = o2::math_utils::Bracketf_t; +using V0ID = o2::dataformats::V0Index; + +using timeEst = o2::dataformats::TimeStampWithError; + +class SVStudySpec final : public Task +{ + public: + SVStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useTPCCl, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseTPCCl(useTPCCl), mUseMC(useMC) {} + ~SVStudySpec() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(o2::globaltracking::RecoContainer& recoData); + o2::dataformats::V0Ext processV0(int iv, o2::globaltracking::RecoContainer& recoData); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + bool refitV0(const V0ID& id, o2::dataformats::V0& v0, o2::globaltracking::RecoContainer& recoData); + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + bool mUseMC{false}; ///< MC flag + std::unique_ptr mDBGOut; + float mSelK0 = -1; + bool mRefit = false; + bool mUseTPCCl = false; + float mMaxEta = 0.8; + float mBz = 0; + int mNHBPerTF = 0; + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + float mNTPCOccBinLengthInv; + float mTPCTBinMUSInv = 0.f; + GTrackID::mask_t mTracksSrc{}; + o2::vertexing::DCAFitterN<2> mFitterV0; + std::vector mTBinClOccAft, mTBinClOccBef; + std::unique_ptr mcReader; // reader of MC information + std::shared_ptr mParam = nullptr; +}; + +void SVStudySpec::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mDBGOut = std::make_unique("svStudy.root", "recreate"); + mRefit = ic.options().get("refit"); + mSelK0 = ic.options().get("sel-k0"); + mMaxEta = ic.options().get("max-eta"); + if (mUseMC) { + mcReader = std::make_unique("collisioncontext.root"); + } +} + +void SVStudySpec::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + + size_t occupancyMapSizeBytes = o2::gpu::GPUO2InterfaceRefit::fillOccupancyMapGetSize(mNHBPerTF, mParam.get()); + gsl::span TPCRefitterOccMap = recoData.occupancyMapTPC; + o2::gpu::GPUO2InterfaceUtils::paramUseExternalOccupancyMap(mParam.get(), mNHBPerTF, TPCRefitterOccMap.data(), occupancyMapSizeBytes); + + mTBinClOccBef.resize(1); + mTBinClOccAft.resize(1); + if (recoData.inputsTPCclusters && mUseTPCCl) { + mNTPCOccBinLength = mParam->rec.tpc.occupancyMapTimeBins; + mTBinClOccBef.clear(); + mTBinClOccAft.clear(); + // prepare TPC occupancy data + if (mNTPCOccBinLength > 1 && recoData.occupancyMapTPC.size()) { + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + int nTPCBins = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0; + int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); + mTBinClOccAft.resize(nTPCOccBins); + mTBinClOccBef.resize(nTPCOccBins); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + std::vector mltHistTB(nTPCOccBins); + for (int i = 0; i < nTPCOccBins; i++) { + mltHistTB[i] = mParam->GetUnscaledMult(tb); + tb += mNTPCOccBinLength; + } + for (int i = nTPCOccBins; i--;) { + sm += mltHistTB[i]; + if (i + sumBins < nTPCOccBins) { + sm -= mltHistTB[i + sumBins]; + } + mTBinClOccAft[i] = sm; + } + sm = 0; + for (int i = 0; i < nTPCOccBins; i++) { + sm += mltHistTB[i]; + if (i - sumBins > 0) { + sm -= mltHistTB[i - sumBins]; + } + mTBinClOccBef[i] = sm; + } + } + } + + process(recoData); +} + +void SVStudySpec::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + // Note: reading of the ITS AlpideParam needed for ITS timing is done by the RecoContainer + mFitterV0.setBz(mBz); + mFitterV0.setUseAbsDCA(svparam.useAbsDCA); + mFitterV0.setPropagateToPCA(false); + mFitterV0.setMaxR(svparam.maxRIni); + mFitterV0.setMinParamChange(svparam.minParamChange); + mFitterV0.setMinRelChi2Change(svparam.minRelChi2Change); + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setMatCorrType(o2::base::Propagator::MatCorrType(svparam.matCorr)); + mFitterV0.setUsePropagator(svparam.usePropagator); + mFitterV0.setRefitWithMatCorr(svparam.refitWithMatCorr); + mFitterV0.setMaxStep(svparam.maxStep); + mFitterV0.setMaxSnp(svparam.maxSnp); + mFitterV0.setMinXSeed(svparam.minXSeed); + + mNHBPerTF = o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + if (!mParam) { + // for occupancy estimator + mParam = o2::gpu::GPUO2InterfaceUtils::getFullParamShared(0.f, mNHBPerTF); + } + auto& elParam = o2::tpc::ParameterElectronics::Instance(); + mTPCTBinMUSInv = 1. / elParam.ZbinWidth; // 1./TPC bin in microseconds + } + mBz = o2::base::Propagator::Instance()->getNominalBz(); + mFitterV0.setBz(mBz); +} + +o2::dataformats::V0Ext SVStudySpec::processV0(int iv, o2::globaltracking::RecoContainer& recoData) +{ + o2::dataformats::V0Ext v0ext; + auto invalidate = [&v0ext]() { v0ext.v0ID.setVertexID(-1); }; + + auto v0s = recoData.getV0s(); + auto v0IDs = recoData.getV0sIdx(); + static int tfID = 0; + + const auto& v0id = v0IDs[iv]; + if (mRefit && !refitV0(v0id, v0ext.v0, recoData)) { + invalidate(); + return v0ext; + } + const auto& v0sel = mRefit ? v0ext.v0 : v0s[iv]; + if (mMaxEta < std::abs(v0sel.getEta())) { + invalidate(); + return v0ext; + } + if (mSelK0 > 0 && std::abs(std::sqrt(v0sel.calcMass2AsK0()) - 0.497) > mSelK0) { + invalidate(); + return v0ext; + } + if (!mRefit) { + v0ext.v0 = v0sel; + } + v0ext.v0ID = v0id; + const auto clRefs = recoData.getTPCTracksClusterRefs(); + o2::MCCompLabel lb[2]; + const o2::MCTrack* mcTrks[2]; + for (int ip = 0; ip < 2; ip++) { + auto& prInfo = v0ext.prInfo[ip]; + auto gid = v0ext.v0ID.getProngID(ip); + auto gidset = recoData.getSingleDetectorRefs(gid); + lb[ip] = recoData.getTrackMCLabel(gid); + if (lb[ip].isValid()) { + prInfo.corrGlo = !lb[ip].isFake(); + } + // get TPC tracks, if any + if (gidset[GTrackID::TPC].isSourceSet()) { + const auto& tpcTr = recoData.getTPCTrack(gidset[GTrackID::TPC]); + prInfo.trackTPC = tpcTr; + prInfo.nClTPC = tpcTr.getNClusters(); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::TPC]); + if (lb[ip].isValid()) { + prInfo.corrTPC = !lb[ip].isFake(); + } + if (mParam && mUseTPCCl) { + uint8_t clSect = 0, clRow = 0; + uint32_t clIdx = 0; + tpcTr.getClusterReference(clRefs, tpcTr.getNClusterReferences() - 1, clSect, clRow, clIdx); + const auto& clus = recoData.getTPCClusters().clusters[clSect][clRow][clIdx]; + prInfo.lowestRow = clRow; + int npads = o2::gpu::GPUTPCGeometry::NPads(clRow); + prInfo.padFromEdge = uint8_t(clus.getPad()); + if (prInfo.padFromEdge > npads / 2) { + prInfo.padFromEdge = npads - 1 - prInfo.padFromEdge; + } + } + } + // get ITS tracks, if any + if (gid.includesDet(DetID::ITS)) { + auto gidITS = recoData.getITSContributorGID(gid); + if (gidset[GTrackID::ITS].isSourceSet()) { + const auto& itsTr = recoData.getITSTrack(gidset[GTrackID::ITS]); + prInfo.nClITS = itsTr.getNClusters(); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::ITS]); + if (lb[ip].isValid()) { + prInfo.corrITS = !lb[ip].isFake(); + } + for (int il = 0; il < 7; il++) { + if (itsTr.hasHitOnLayer(il)) { + prInfo.pattITS |= 0x1 << il; + } + } + } else { + const auto& itsTrf = recoData.getITSABRefs()[gidset[GTrackID::ITSAB]]; + prInfo.nClITS = itsTrf.getNClusters(); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::ITSAB]); + if (lb[ip].isValid()) { + prInfo.corrITS = !lb[ip].isFake(); + } + for (int il = 0; il < 7; il++) { + if (itsTrf.hasHitOnLayer(il)) { + prInfo.pattITS |= 0x1 << il; + } + } + prInfo.pattITS |= 0x1 << 31; // flag AB + } + if (gidset[GTrackID::ITSTPC].isSourceSet()) { + auto mtc = recoData.getTPCITSTrack(gidset[GTrackID::ITSTPC]); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::ITSTPC]); + prInfo.chi2ITSTPC = mtc.getChi2Match(); + if (lb[ip].isValid()) { + prInfo.corrITSTPC = !lb[ip].isFake(); + } + } + } + if (mUseMC && lb[ip].isValid()) { // temp store of mctrks + mcTrks[ip] = mcReader->getTrack(lb[ip]); + } + } + if (mUseMC && (mcTrks[0] != nullptr) && (mcTrks[1] != nullptr)) { + // check majority vote on mother particle otherwise leave pdg -1 + if (lb[0].getSourceID() == lb[1].getSourceID() && lb[0].getEventID() == lb[1].getEventID() && + mcTrks[0]->getMotherTrackId() == mcTrks[1]->getMotherTrackId() && mcTrks[0]->getMotherTrackId() >= 0) { + const auto mother = mcReader->getTrack(lb[0].getSourceID(), lb[0].getEventID(), mcTrks[0]->getMotherTrackId()); + v0ext.mcPID = mother->GetPdgCode(); + } + } + return v0ext; +} + +void SVStudySpec::process(o2::globaltracking::RecoContainer& recoData) +{ + auto v0IDs = recoData.getV0sIdx(); + auto nv0 = v0IDs.size(); + if (nv0 > recoData.getV0s().size()) { + mRefit = true; + } + std::map> pv2sv; + static int tfID = 0; + for (int iv = 0; iv < nv0; iv++) { + const auto v0id = v0IDs[iv]; + pv2sv[v0id.getVertexID()].push_back(iv); + } + std::vector v0extVec; + for (auto it : pv2sv) { + int pvID = it.first; + auto& vv = it.second; + if (pvID < 0 || vv.size() == 0) { + continue; + } + v0extVec.clear(); + for (int iv0 : vv) { + auto v0ext = processV0(iv0, recoData); + if (v0ext.v0ID.getVertexID() < 0) { + continue; + } + v0extVec.push_back(v0ext); + } + if (v0extVec.size()) { + const auto& pv = recoData.getPrimaryVertex(pvID); + float tpcOccBef = 0., tpcOccAft = 0.; + int tb = pv.getTimeStamp().getTimeStamp() * mTPCTBinMUSInv * mNTPCOccBinLengthInv; + tpcOccBef = tb < 0 ? mTBinClOccBef[0] : (tb >= mTBinClOccBef.size() ? mTBinClOccBef.back() : mTBinClOccBef[tb]); + tpcOccAft = tb < 0 ? mTBinClOccAft[0] : (tb >= mTBinClOccAft.size() ? mTBinClOccAft.back() : mTBinClOccAft[tb]); + + (*mDBGOut) << "v0" + << "orbit=" << recoData.startIR.orbit << "tfID=" << tfID << "tpcOccBef=" << tpcOccBef << "tpcOccAft=" << tpcOccAft + << "v0Ext=" << v0extVec + << "pv=" << pv + << "\n"; + } + } + tfID++; +} + +bool SVStudySpec::refitV0(const V0ID& id, o2::dataformats::V0& v0, o2::globaltracking::RecoContainer& recoData) +{ + auto seedP = recoData.getTrackParam(id.getProngID(0)); + auto seedN = recoData.getTrackParam(id.getProngID(1)); + bool isTPConly = (id.getProngID(0).getSource() == GTrackID::TPC) || (id.getProngID(1).getSource() == GTrackID::TPC); + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + if (svparam.mTPCTrackPhotonTune && isTPConly) { + mFitterV0.setMaxDZIni(svparam.mTPCTrackMaxDZIni); + mFitterV0.setMaxDXYIni(svparam.mTPCTrackMaxDXYIni); + mFitterV0.setMaxChi2(svparam.mTPCTrackMaxChi2); + mFitterV0.setCollinear(true); + } + int nCand = mFitterV0.process(seedP, seedN); + if (svparam.mTPCTrackPhotonTune && isTPConly) { // restore + // Reset immediately to the defaults + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setCollinear(false); + } + if (nCand == 0) { // discard this pair + return false; + } + const int cand = 0; + if (!mFitterV0.isPropagateTracksToVertexDone(cand) && !mFitterV0.propagateTracksToVertex(cand)) { + return false; + } + const auto& trPProp = mFitterV0.getTrack(0, cand); + const auto& trNProp = mFitterV0.getTrack(1, cand); + std::array pP{}, pN{}; + trPProp.getPxPyPzGlo(pP); + trNProp.getPxPyPzGlo(pN); + std::array pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + auto p2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1] + pV0[2] * pV0[2]; + const auto& pv = recoData.getPrimaryVertex(id.getVertexID()); + const auto v0XYZ = mFitterV0.getPCACandidatePos(cand); + float dx = v0XYZ[0] - pv.getX(), dy = v0XYZ[1] - pv.getY(), dz = v0XYZ[2] - pv.getZ(), prodXYZv0 = dx * pV0[0] + dy * pV0[1] + dz * pV0[2]; + float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0); + new (&v0) o2::dataformats::V0(v0XYZ, pV0, mFitterV0.calcPCACovMatrixFlat(cand), trPProp, trNProp); + v0.setDCA(mFitterV0.getChi2AtPCACandidate(cand)); + v0.setCosPA(cosPA); + return true; +} + +void SVStudySpec::endOfStream(EndOfStreamContext& ec) +{ + mDBGOut.reset(); +} + +void SVStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } +} + +DataProcessorSpec getSVStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcCls, bool useMC) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcCls, false); + dataRequest->requestPrimaryVertices(useMC); + dataRequest->requestSecondaryVertices(useMC); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + auto ggRequest = std::make_shared(true, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + dataRequest->inputs, + true); + bool useTPCcl = srcCls[GTrackID::TPC]; + return DataProcessorSpec{ + "sv-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useTPCcl, useMC)}, + Options{ + {"refit", VariantType::Bool, false, {"refit SVertices"}}, + {"sel-k0", VariantType::Float, -1.f, {"If positive, select K0s with this mass margin"}}, + {"max-eta", VariantType::Float, 1.2f, {"Cut on track eta"}}, + }}; +} + +} // namespace o2::svstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCClusSelector.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCClusSelector.cxx new file mode 100644 index 0000000000000..e5b28fb0fd62b --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCClusSelector.cxx @@ -0,0 +1,117 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// helper class for TPC clusters selection +#include "GlobalTrackingStudy/TPCClusSelector.h" +#include "DataFormatsTPC/ClusterNativeHelper.h" +#include "Framework/Logger.h" +#include +#ifdef WITH_OPENMP +#include +#endif + +using namespace o2::tpc; + +void TPCClusSelector::setNThreads(int n) +{ +#ifndef WITH_OPENMP + if (n > 1) { + LOGP(warn, "No OpenMP"); + } + n = 1; +#endif + mNThreads = n; +} + +std::pair TPCClusSelector::findClustersRange(int sec, int row, float tbmin, float tbmax, const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct) +{ + // find sorted indices of clusters in the [tbmin:tbmax] range, if not found, return {-1,-2} + const auto& vidx = mSectors[sec].rows[row]; + const auto* clarr = tpcClusterIdxStruct.clusters[sec][row]; + // use binary search to find 1st cluster with time >= tb + int ncl = vidx.size(), left = 0, right = ncl; + while (left < right) { + int mid = left + (right - left) / 2; + if (clarr[vidx[mid]].getTime() < tbmin) { + left = mid + 1; + } else { + right = mid; + } + } + if (left == ncl || clarr[vidx[left]].getTime() > tbmax) { + return {-1, -2}; // all clusters have time < tbmin or no clusters in the range [tbmin:tbmax] + } + int idmin = left, idmax = left, idtst = idmin; + // look at smaller times + while (++idtst < ncl && clarr[vidx[idtst]].getTime() <= tbmax) { + idmax = idtst; + } + return {idmin, idmax}; +} + +int TPCClusSelector::findClustersEntries(int sec, int row, float tbmin, float tbmax, float padmin, float padmax, const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct, std::vector* clIDDirect) +{ + // find direct cluster indices for tbmin:tbmas / padmin/padmax range, fill clIDDirect vector if provided + const auto& vidx = mSectors[sec].rows[row]; + const auto* clarr = tpcClusterIdxStruct.clusters[sec][row]; + // use binary search to find 1st cluster with time >= tb + int ncl = vidx.size(), left = 0, right = ncl; + if (clIDDirect) { + clIDDirect->clear(); + } + while (left < right) { + int mid = left + (right - left) / 2; + if (clarr[vidx[mid]].getTime() < tbmin) { + left = mid + 1; + } else { + right = mid; + } + } + if (left == ncl || clarr[vidx[left]].getTime() > tbmax) { + return 0; // all clusters have time < tbmin or no clusters in the range [tbmin:tbmax] + } + int nclf = 0; + while (left < ncl) { + const auto& cl = clarr[vidx[left]]; + if (cl.getTime() > tbmax) { + break; + } + if (cl.getPad() >= padmin && cl.getPad() <= padmax) { + nclf++; + if (clIDDirect) { + clIDDirect->push_back(vidx[left]); + } + } + } + return nclf; +} + +void TPCClusSelector::fill(const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct) +{ + for (int is = 0; is < NSectors; is++) { + auto& sect = mSectors[is]; +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) +#endif + for (int ir = 0; ir < Sector::NRows; ir++) { + size_t ncl = tpcClusterIdxStruct.nClusters[is][ir]; + if (ncl >= 0xffff) { + LOGP(error, "Row {} of sector {} has {} clusters, truncating to {}", ir, is, ncl, int(0xffff)); + ncl = 0xffff; + } + auto& rowidx = sect.rows[ir]; + rowidx.resize(ncl); + std::iota(rowidx.begin(), rowidx.end(), 0); + const auto* clus = tpcClusterIdxStruct.clusters[is][ir]; // C array of clusters + std::sort(rowidx.begin(), rowidx.end(), [&](size_t a, size_t b) { return clus[a].getTime() < clus[b].getTime(); }); + } + } +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCDataFilter.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCDataFilter.cxx new file mode 100644 index 0000000000000..5aad84cf17bbb --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCDataFilter.cxx @@ -0,0 +1,314 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include "DataFormatsTPC/Constants.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsBase/GeometryManager.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "SimulationDataFormat/MCUtils.h" +#include "CommonUtils/NameConf.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "GlobalTrackingStudy/TPCDataFilter.h" +#include "TPCBase/ParameterElectronics.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "Steer/MCKinematicsReader.h" + +namespace o2::global +{ + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; + +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using GTrackID = o2::dataformats::GlobalTrackID; +using TBracket = o2::math_utils::Bracketf_t; + +using timeEst = o2::dataformats::TimeStampWithError; + +class TPCDataFilter final : public Task +{ + public: + enum TrackDecision : char { NA, + REMOVE, + REDUCE, + KEEP }; + + TPCDataFilter(std::shared_ptr dr, GTrackID::mask_t src, bool useMC) + : mDataRequest(dr), mTracksSrc(src), mUseMC(useMC) + { + } + ~TPCDataFilter() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(o2::globaltracking::RecoContainer& recoData); + void sendOutput(ProcessingContext& pc); + + private: + void updateTimeDependentParams(ProcessingContext& pc){}; + std::shared_ptr mDataRequest; + bool mUseMC{false}; ///< MC flag + char mRemMode = TrackDecision::KEEP; + GTrackID::mask_t mTracksSrc{}; + o2::steer::MCKinematicsReader mcReader; // reader of MC information + // + // TPC data + std::vector mClustersLinearFiltered; + std::vector mClustersLinearStatus; + std::vector mTrackStatus; + std::vector mTracksFiltered; + std::vector mTrackClusIdxFiltered; + std::vector mTPCTrkLabelsFiltered; + o2::tpc::ClusterNativeAccess mClusFiltered; + GTrackID::mask_t mAccTrackSources; + float mMinAbsTgl = 0.; + int mMinTPCClusters = 0; +}; + +void TPCDataFilter::init(InitContext& ic) +{ + mRemMode = ic.options().get("suppress-rejected") ? TrackDecision::REMOVE : TrackDecision::REDUCE; + GTrackID::mask_t acceptSourcesTrc = GTrackID::getSourcesMask("ITS,TPC,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + mAccTrackSources = acceptSourcesTrc & GTrackID::getSourcesMask(ic.options().get("accept-track-sources")); + mMinAbsTgl = ic.options().get("min-abs-tgl"); + mMinTPCClusters = ic.options().get("min-tpc-clusters"); +} + +void TPCDataFilter::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(recoData); + + sendOutput(pc); +} + +void TPCDataFilter::sendOutput(ProcessingContext& pc) +{ + + pc.outputs().snapshot(Output{"TPC", "TRACKSF", 0}, mTracksFiltered); + pc.outputs().snapshot(Output{"TPC", "CLUSREFSF", 0}, mTrackClusIdxFiltered); + if (mUseMC) { + pc.outputs().snapshot(Output{"TPC", "TRACKSMCLBLF", 0}, mTPCTrkLabelsFiltered); + } + + o2::tpc::TPCSectorHeader clusterOutputSectorHeader{0}; + clusterOutputSectorHeader.activeSectors = (1ul << o2::tpc::constants::MAXSECTOR) - 1; + for (int i = 0; i < o2::tpc::constants::MAXSECTOR; i++) { + clusterOutputSectorHeader.sectorBits = (1ul << i); + o2::header::DataHeader::SubSpecificationType subspec = i; + char* buffer = pc.outputs().make({o2::header::gDataOriginTPC, "CLUSTERNATIVEF", subspec, {clusterOutputSectorHeader}}, + mClusFiltered.nClustersSector[i] * sizeof(*mClusFiltered.clustersLinear) + sizeof(o2::tpc::ClusterCountIndex)) + .data(); + o2::tpc::ClusterCountIndex* outIndex = reinterpret_cast(buffer); + memset(outIndex, 0, sizeof(*outIndex)); + for (int j = 0; j < o2::tpc::constants::MAXGLOBALPADROW; j++) { + outIndex->nClusters[i][j] = mClusFiltered.nClusters[i][j]; + } + memcpy(buffer + sizeof(*outIndex), mClusFiltered.clusters[i][0], mClusFiltered.nClustersSector[i] * sizeof(*mClusFiltered.clustersLinear)); + + if (mUseMC && mClusFiltered.clustersMCTruth) { + o2::dataformats::MCLabelContainer cont; + for (unsigned int j = 0; j < mClusFiltered.nClustersSector[i]; j++) { + const auto& labels = mClusFiltered.clustersMCTruth->getLabels(mClusFiltered.clusterOffset[i][0] + j); + for (const auto& label : labels) { + cont.addElement(j, label); + } + } + o2::dataformats::ConstMCLabelContainer contflat; + cont.flatten_to(contflat); + pc.outputs().snapshot({o2::header::gDataOriginTPC, "CLNATIVEMCLBLF", subspec, {clusterOutputSectorHeader}}, contflat); + } + } +} + +void TPCDataFilter::process(o2::globaltracking::RecoContainer& recoData) +{ + auto tpcTracks = recoData.getTPCTracks(); + auto tpcTrackClusIdx = recoData.getTPCTracksClusterRefs(); + auto tpcClusterIdxStruct = &recoData.inputsTPCclusters->clusterIndex; + gsl::span tpcTrkLabels; + if (mUseMC) { + tpcTrkLabels = recoData.getTPCTracksMCLabels(); + mTPCTrkLabelsFiltered.clear(); + } + const unsigned int DISCARD = -1U; + + mTracksFiltered.clear(); + mTrackClusIdxFiltered.clear(); + mClustersLinearFiltered.clear(); + mTrackStatus.clear(); + mTrackStatus.resize(tpcTracks.size()); + mClustersLinearStatus.clear(); + mClustersLinearStatus.resize(tpcClusterIdxStruct->nClustersTotal, DISCARD); + + size_t nSelTracks = 0; + auto selTPCTrack = [this, tpcTracks, tpcTrackClusIdx, tpcClusterIdxStruct, &nSelTracks](int itpc, char stat) { + this->mTrackStatus[itpc] = stat; + if (stat != TrackDecision::KEEP) { + return; + } + nSelTracks++; + const auto& trc = tpcTracks[itpc]; + int count = trc.getNClusters(); + const o2::tpc::ClusterNative* cl = nullptr; + for (int ic = count; ic--;) { + const auto cl = &trc.getCluster(tpcTrackClusIdx, ic, *tpcClusterIdxStruct); + size_t offs = std::distance(tpcClusterIdxStruct->clustersLinear, cl); + this->mClustersLinearStatus[offs] = 0; + } + }; + + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + int nv = vtxRefs.size(); + for (int iv = 0; iv < nv; iv++) { + const auto& vtref = vtxRefs[iv]; + for (int is = 0; is < GTrackID::NSources; is++) { + auto dmask = GTrackID::getSourceDetectorsMask(is); + if (!GTrackID::getSourceDetectorsMask(is)[GTrackID::TPC]) { + continue; + } + char decisionSource = mAccTrackSources[is] ? KEEP : mRemMode; + int idMin = vtxRefs[iv].getFirstEntryOfSource(is), idMax = idMin + vtxRefs[iv].getEntriesOfSource(is); + for (int i = idMin; i < idMax; i++) { + auto decision = decisionSource; + auto vid = trackIndex[i]; + auto tpcID = recoData.getTPCContributorGID(vid); + if (vid.isAmbiguous() && mTrackStatus[tpcID] != TrackDecision::NA) { // already processed + continue; + } + if (decisionSource == KEEP) { + const auto& tpcTr = tpcTracks[tpcID.getIndex()]; + if (std::abs(tpcTr.getTgl()) < mMinAbsTgl || tpcTr.getNClusters() < mMinTPCClusters) { + decision = mRemMode; + } + } + selTPCTrack(tpcID, decision); + } + } + } + + // copy filtered clusters and register them in the filtered access struct + int offset = 0; + for (unsigned int i = 0; i < o2::tpc::constants::MAXSECTOR; i++) { + for (unsigned int j = 0; j < o2::tpc::constants::MAXGLOBALPADROW; j++) { + mClusFiltered.nClusters[i][j] = 0; + for (unsigned int ic = 0; ic < tpcClusterIdxStruct->nClusters[i][j]; ic++) { + if (mClustersLinearStatus[offset] != DISCARD) { + mClustersLinearStatus[offset] = mClustersLinearFiltered.size(); + mClustersLinearFiltered.push_back(tpcClusterIdxStruct->clustersLinear[offset]); + mClusFiltered.nClusters[i][j]++; + } + offset++; + } + } + } + mClusFiltered.clustersLinear = mClustersLinearFiltered.data(); + mClusFiltered.setOffsetPtrs(); + + LOGP(info, "Accepted {} tracks out of {} and {} clusters out of {}", nSelTracks, tpcTracks.size(), mClusFiltered.nClustersTotal, tpcClusterIdxStruct->nClustersTotal); + + // register new tracks with updated cluster references + for (size_t itr = 0; itr < tpcTracks.size(); itr++) { + if (mTrackStatus[itr] == REMOVE) { // track is discarded + continue; + } else if (mTrackStatus[itr] == REDUCE) { // fill track by 0s, discard clusters + auto& t = mTracksFiltered.emplace_back(); + memset(&t, 0, sizeof(o2::tpc::TrackTPC)); + if (mUseMC) { + mTPCTrkLabelsFiltered.emplace_back(); + } + } else { // track is accepted + const auto& tor = tpcTracks[itr]; + const auto& cref = tor.getClusterRef(); + mTracksFiltered.push_back(tor); + mTracksFiltered.back().shiftFirstClusterRef(int(mTrackClusIdxFiltered.size()) - tor.getClusterRef().getFirstEntry()); + size_t idx0 = mTrackClusIdxFiltered.size(); + int nclTrack = cref.getEntries(); + mTrackClusIdxFiltered.resize(mTrackClusIdxFiltered.size() + nclTrack + (nclTrack + 1) / 2); + + // see explanations in the TrackTPC::getClusterReference + uint32_t* clIndArr = reinterpret_cast(&mTrackClusIdxFiltered[idx0]); + uint8_t* srIndArr = reinterpret_cast(clIndArr + nclTrack); + for (int ic = 0; ic < nclTrack; ic++) { + uint8_t sectorIndex, rowIndex; + const auto cl = &tor.getCluster(tpcTrackClusIdx, ic, *tpcClusterIdxStruct, sectorIndex, rowIndex); + unsigned int oldLinear = std::distance(tpcClusterIdxStruct->clustersLinear, cl); + unsigned int newLinear = mClustersLinearStatus[oldLinear]; + if (newLinear == DISCARD) { + LOGP(fatal, "discarded cluster {} is selected", oldLinear); + } + clIndArr[ic] = newLinear - mClusFiltered.clusterOffset[sectorIndex][rowIndex]; + srIndArr[ic] = sectorIndex; + srIndArr[ic + nclTrack] = rowIndex; + } + if (mUseMC) { + mTPCTrkLabelsFiltered.emplace_back(tpcTrkLabels[itr]); + } + } + } +} + +void TPCDataFilter::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } +} + +DataProcessorSpec getTPCDataFilter(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) +{ + std::vector outputs; + for (int i = 0; i < o2::tpc::constants::MAXSECTOR; i++) { + outputs.emplace_back(o2::header::gDataOriginTPC, "CLUSTERNATIVEF", i, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back(o2::header::gDataOriginTPC, "CLNATIVEMCLBLF", i, Lifetime::Timeframe); + } + } + outputs.emplace_back("TPC", "TRACKSF", 0, Lifetime::Timeframe); + outputs.emplace_back("TPC", "CLUSREFSF", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back("TPC", "TRACKSMCLBLF", 0, Lifetime::Timeframe); + } + + auto dataRequest = std::make_shared(); + + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcClusters, useMC); + dataRequest->requestPrimaryVertices(useMC); + + return DataProcessorSpec{ + "tpc-data-filter", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, srcTracks, useMC)}, + Options{ + {"suppress-rejected", VariantType::Bool, false, {"Remove suppressed tracks instead of reducing them"}}, + {"min-abs-tgl", VariantType::Float, 0.0f, {"suppress tracks with abs tgL less that this threshold (e.g. noise tracks)"}}, + {"min-tpc-clusters", VariantType::Int, 0, {"suppress tracks less clusters that this threshold (e.g. noise tracks)"}}, + {"accept-track-sources", VariantType::String, std::string{GTrackID::ALL}, {"comma-separated list of track sources to accept"}}}}; +} + +} // namespace o2::global diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx index 8cac4d059fdaa..f9ba67319e18a 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx @@ -14,7 +14,7 @@ #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" #include "TPCCalibration/VDriftHelper.h" -#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCFastTransformPOD.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DetectorsBase/Propagator.h" #include "DetectorsBase/GeometryManager.h" @@ -23,6 +23,7 @@ #include "CommonUtils/NameConf.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "Framework/ControlService.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TPCTrackStudy.h" @@ -46,13 +47,11 @@ using TBracket = o2::math_utils::Bracketf_t; using timeEst = o2::dataformats::TimeStampWithError; -class TPCTrackStudySpec : public Task +class TPCTrackStudySpec final : public Task { public: TPCTrackStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) - { - } + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} ~TPCTrackStudySpec() final = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -65,10 +64,18 @@ class TPCTrackStudySpec : public Task std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; o2::tpc::VDriftHelper mTPCVDriftHelper{}; - o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps{nullptr}; bool mUseMC{false}; ///< MC flag - float mRRef = 0.; + bool mUseGPUModel{false}; + float mXRef = 0.; + int mNMoves = 6; + int mTFStart = 0; + int mTFEnd = 999999999; + int mTFCount = -1; + bool mUseR = false; + bool mRepRef = false; std::unique_ptr mDBGOut; + std::unique_ptr mDBGOutCl; float mITSROFrameLengthMUS = 0.; GTrackID::mask_t mTracksSrc{}; o2::steer::MCKinematicsReader mcReader; // reader of MC information @@ -77,6 +84,7 @@ class TPCTrackStudySpec : public Task gsl::span mTPCTrackClusIdx; ///< input TPC track cluster indices span gsl::span mTPCTracksArray; ///< input TPC tracks span gsl::span mTPCRefitterShMap; ///< externally set TPC clusters sharing map + gsl::span mTPCRefitterOccMap; ///< externally set TPC clusters occupancy map const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices gsl::span mTPCTrkLabels; ///< input TPC Track MC labels std::unique_ptr mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction @@ -85,126 +93,207 @@ class TPCTrackStudySpec : public Task void TPCTrackStudySpec::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mRRef = ic.options().get("target-radius"); - if (mRRef < 0.) { - mRRef = 0.; + mXRef = ic.options().get("target-x"); + mNMoves = std::max(2, ic.options().get("n-moves")); + mUseR = ic.options().get("use-r-as-x"); + mRepRef = ic.options().get("repeat-ini-ref"); + mUseGPUModel = ic.options().get("use-gpu-fitter"); + mTFStart = ic.options().get("tf-start"); + mTFEnd = ic.options().get("tf-end"); + if (mXRef < 0.) { + mXRef = 0.; } mDBGOut = std::make_unique("tpc-trackStudy.root", "recreate"); + if (ic.options().get("dump-clusters")) { + mDBGOutCl = std::make_unique("tpc-trackStudy-cl.root", "recreate"); + } } void TPCTrackStudySpec::run(ProcessingContext& pc) { + mTFCount++; + if (mTFCount < mTFStart || mTFCount > mTFEnd) { + LOGP(info, "Skipping TF {}", mTFCount); + return; + } + o2::globaltracking::RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions process(recoData); + + if (mTFCount > mTFEnd) { + LOGP(info, "Stopping processing after TF {}", mTFCount); + pc.services().get().endOfStream(); + return; + } } void TPCTrackStudySpec::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTPCVDriftHelper.extractCCDBInputs(pc); - o2::tpc::CorrectionMapsLoader::extractCCDBInputs(pc); + auto const& raw = pc.inputs().get("corrMap"); + mTPCCorrMaps = &o2::gpu::TPCFastTransformPOD::get(raw); static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; // none at the moment } - // we may have other params which need to be queried regularly - bool updateMaps = false; - if (mTPCCorrMapsLoader.isUpdated()) { - mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; - } - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, - mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, - mTPCVDriftHelper.getSourceName()); - mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); - } } void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) { static long counter = -1; - counter++; auto prop = o2::base::Propagator::Instance(); + mTPCTracksArray = recoData.getTPCTracks(); + mTPCTrackClusIdx = recoData.getTPCTracksClusterRefs(); + mTPCClusterIdxStruct = &recoData.inputsTPCclusters->clusterIndex; + mTPCRefitterShMap = recoData.clusterShMapTPC; + mTPCRefitterOccMap = recoData.occupancyMapTPC; + + std::vector intRecs; if (mUseMC) { // extract MC tracks const o2::steer::DigitizationContext* digCont = nullptr; if (!mcReader.initFromDigitContext("collisioncontext.root")) { throw std::invalid_argument("initialization of MCKinematicsReader failed"); } digCont = mcReader.getDigitizationContext(); - const auto& intRecs = digCont->getEventRecords(); - - mTPCTracksArray = recoData.getTPCTracks(); - mTPCTrackClusIdx = recoData.getTPCTracksClusterRefs(); - mTPCClusterIdxStruct = &recoData.inputsTPCclusters->clusterIndex; - mTPCRefitterShMap = recoData.clusterShMapTPC; + intRecs = digCont->getEventRecords(); mTPCTrkLabels = recoData.getTPCTracksMCLabels(); + } + if (mTPCTracksArray.size()) { + LOGP(info, "Found {} TPC tracks", mTPCTracksArray.size()); + mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMaps, prop->getNominalBz(), mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); + } + float vdriftTB = mTPCVDriftHelper.getVDriftObject().getVDrift() * o2::tpc::ParameterElectronics::Instance().ZbinWidth; // VDrift expressed in cm/TimeBin + float tpcTBBias = mTPCVDriftHelper.getVDriftObject().getTimeOffset() / (8 * o2::constants::lhc::LHCBunchSpacingMUS); + std::vector clSector, clRow; + std::vector clX, clY, clZ; - mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, &mTPCCorrMapsLoader, prop->getNominalBz(), mTPCTrackClusIdx.data(), mTPCRefitterShMap.data(), nullptr, o2::base::Propagator::Instance()); + auto dumpClusters = [this] { + static int tf = 0; + const auto* corrMap = this->mTPCCorrMaps; + for (int sector = 0; sector < 36; sector++) { + float alp = ((sector % 18) * 20 + 10) * TMath::DegToRad(); + float sn = TMath::Sin(alp), cs = TMath::Cos(alp); + for (int row = 0; row < 152; row++) { + for (int ic = 0; ic < this->mTPCClusterIdxStruct->nClusters[sector][row]; ic++) { + const auto cl = this->mTPCClusterIdxStruct->clusters[sector][row][ic]; + float x, y, z, xG, yG; + corrMap->TransformIdeal(sector, row, cl.getPad(), cl.getTime(), x, y, z, 0); + o2::math_utils::detail::rotateZ(x, y, xG, yG, sn, cs); + LOGP(debug, "tf:{} s:{} r:{} p:{} t:{} qm:{} qt:{} f:{} x:{} y:{} z:{}", tf, sector, row, cl.getPad(), cl.getTime(), cl.getQmax(), cl.getQtot(), cl.getFlags(), x, y, z); + (*mDBGOutCl) << "tpccl" + << "tf=" << tf << "sect=" << sector << "row=" << row << "pad=" << cl.getPad() << "time=" << cl.getTime() << "qmax=" << cl.getQmax() << "qtot=" << cl.getQtot() + << "sigT=" << cl.getSigmaTime() << "sigP=" << cl.getSigmaPad() + << "flags=" << cl.getFlags() + << "x=" << x << "y=" << y << "z=" << z << "xg=" << xG << "yg=" << yG + << "\n"; + } + } + } + tf++; + }; - float vdriftTB = mTPCVDriftHelper.getVDriftObject().getVDrift() * o2::tpc::ParameterElectronics::Instance().ZbinWidth; // VDrift expressed in cm/TimeBin - float tpcTBBias = mTPCVDriftHelper.getVDriftObject().getTimeOffset() / (8 * o2::constants::lhc::LHCBunchSpacingMUS); - float RRef2 = mRRef * mRRef; - const o2::MCTrack* mcTrack = nullptr; - std::vector clSector, clRow; - std::vector clIniX, clIniY, clIniZ, clMovX, clMovY, clMovZ; - for (size_t itr = 0; itr < mTPCTracksArray.size(); itr++) { - auto tr = mTPCTracksArray[itr]; // create track copy + if (mDBGOutCl) { + dumpClusters(); + } - // create refitted copy - auto trf = tr.getOuterParam(); // we refit inward + for (size_t itr = 0; itr < mTPCTracksArray.size(); itr++) { + auto tr = mTPCTracksArray[itr]; // create track copy + int side = 0; + if (tr.hasBothSidesClusters()) { + continue; + } + side = tr.hasASideClustersOnly() ? 1 : -1; + //========================================================================= + // create refitted copy + auto trackRefit = [itr, this](o2::track::TrackParCov& trc, float t) -> bool { float chi2Out = 0; - // impose MC time in TPC timebin and refit inward after resetted covariance - int retVal = mTPCRefitter->RefitTrackAsTrackParCov(trf, mTPCTracksArray[itr].getClusterRef(), tr.getTime0(), &chi2Out, false, true); + int retVal = mUseGPUModel ? this->mTPCRefitter->RefitTrackAsGPU(trc, this->mTPCTracksArray[itr].getClusterRef(), t, &chi2Out, false, true) : this->mTPCRefitter->RefitTrackAsTrackParCov(trc, this->mTPCTracksArray[itr].getClusterRef(), t, &chi2Out, false, true); if (retVal < 0) { - LOGP(warn, "Refit failed ({}) with originaltime0: {} : track#{}[{}]", retVal, tr.getTime0(), counter, ((const o2::track::TrackPar&)tr.getOuterParam()).asString()); - continue; + LOGP(warn, "Refit failed ({}) with time={}: track#{}[{}]", retVal, t, counter, trc.asString()); + return false; } - // propagate original track - if (!tr.rotate(o2::math_utils::sector2Angle(o2::math_utils::angle2Sector(tr.getPhiPos())))) { - continue; + return true; + }; + + auto trackProp = [&tr, itr, prop, this](o2::track::TrackParCov& trc) -> bool { + if (!trc.rotate(tr.getAlpha())) { + LOGP(warn, "Rotation to original track alpha {} failed, track#{}[{}]", tr.getAlpha(), counter, trc.asString()); + return false; } - float curR2 = tr.getX() * tr.getX() + tr.getY() * tr.getY(); - if (curR2 > RRef2) { // try to propagate as close as possible to target radius - float xtgt = 0; - if (!tr.getXatLabR(mRRef, xtgt, prop->getNominalBz(), o2::track::DirInward)) { - xtgt = 0; - } - prop->PropagateToXBxByBz(tr, xtgt); // propagation will not necessarilly converge, but we don't care - if (!tr.rotate(o2::math_utils::sector2Angle(o2::math_utils::angle2Sector(tr.getPhiPos())))) { - continue; - } + float xtgt = this->mXRef; + if (mUseR && !trc.getXatLabR(this->mXRef, xtgt, prop->getNominalBz(), o2::track::DirInward)) { + xtgt = 0; + return false; } - // propagate original/refitted track - if (!trf.rotate(tr.getAlpha())) { - continue; + if (!prop->PropagateToXBxByBz(trc, xtgt)) { + LOGP(warn, "Propagation to X={} failed, track#{}[{}]", xtgt, counter, trc.asString()); + return false; } - curR2 = trf.getX() * trf.getX() + trf.getY() * trf.getY(); - if (curR2 > RRef2) { // try to propagate as close as possible to target radius - float xtgt = 0; - if (!trf.getXatLabR(mRRef, xtgt, prop->getNominalBz(), o2::track::DirInward)) { - xtgt = 0; - } - prop->PropagateToXBxByBz(trf, xtgt); // propagation will not necessarilly converge, but we don't care - // propagate to the same alpha/X as the original track - if (!trf.rotate(tr.getAlpha()) || !prop->PropagateToXBxByBz(trf, tr.getX())) { - continue; - } + return true; + }; + + auto prepClus = [this, &tr, &clSector, &clRow, &clX, &clY, &clZ](float t) { // extract cluster info + clSector.clear(); + clRow.clear(); + clX.clear(); + clY.clear(); + clZ.clear(); + int count = tr.getNClusters(); + const o2::tpc::ClusterNative* cl = nullptr; + for (int ic = count; ic--;) { + uint8_t sector, row; + cl = &tr.getCluster(this->mTPCTrackClusIdx, ic, *this->mTPCClusterIdxStruct, sector, row); + clSector.push_back(sector); + clRow.push_back(row); + float x, y, z; + mTPCCorrMaps->Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, t); // nominal time of the track + clX.push_back(x); + clY.push_back(y); + clZ.push_back(z); } + }; + + //========================================================================= + + auto trf = tr.getOuterParam(); // we refit inward original track + if (!trackRefit(trf, tr.getTime0()) || !trackProp(trf)) { + continue; + } + + // propagate original track + if (!trackProp(tr)) { + continue; + } + prepClus(tr.getTime0()); // original clusters + counter++; + // store results + (*mDBGOut) << "tpcIni" + << "counter=" << counter + << "iniTrack=" << tr + << "iniTrackRef=" << trf + << "side=" << side + << "time=" << tr.getTime0() + << "clSector=" << clSector + << "clRow=" << clRow + << "clX=" << clX + << "clY=" << clY + << "clZ=" << clZ + << "\n"; + + float dz = 0; + + while (mUseMC) { // impose MC time in TPC timebin and refit inward after resetted covariance // extract MC truth + const o2::MCTrack* mcTrack = nullptr; auto lbl = mTPCTrkLabels[itr]; if (!lbl.isValid() || !(mcTrack = mcReader.getTrack(lbl))) { - continue; + break; } long bc = intRecs[lbl.getEventID()].toLong(); // bunch crossing of the interaction float bcTB = bc / 8. + tpcTBBias; // the same in TPC timebins, accounting for the TPC time bias @@ -213,79 +302,78 @@ void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) pxyz{(float)mcTrack->GetStartVertexMomentumX(), (float)mcTrack->GetStartVertexMomentumY(), (float)mcTrack->GetStartVertexMomentumZ()}; TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrack->GetPdgCode()); if (!pPDG) { - continue; + break; } o2::track::TrackPar mctrO2(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); // // propagate it to the alpha/X of the reconstructed track if (!mctrO2.rotate(tr.getAlpha()) || !prop->PropagateToXBxByBz(mctrO2, tr.getX())) { - continue; + break; } - // // now create a properly refitted track with correct time and distortions correction - auto trackRefit = tr.getOuterParam(); // we refit inward - chi2Out = 0; - // impose MC time in TPC timebin and refit inward after resetted covariance - retVal = mTPCRefitter->RefitTrackAsTrackParCov(trackRefit, mTPCTracksArray[itr].getClusterRef(), bcTB, &chi2Out, false, true); - if (retVal < 0) { - LOGP(warn, "Refit failed for #{} ({}), imposed time0: {} conventional time0: {} [{}]", counter, retVal, bcTB, tr.getTime0(), ((o2::track::TrackPar&)trackRefit).asString()); - continue; + { + auto trfm = tr.getOuterParam(); // we refit inward + // impose MC time in TPC timebin and refit inward after resetted covariance + if (!trackRefit(trfm, bcTB) || !trfm.rotate(tr.getAlpha()) || !prop->PropagateToXBxByBz(trfm, tr.getX())) { + LOGP(warn, "Failed to propagate MC-time refitted track#{} [{}] to X/alpha of original track [{}]", counter, trfm.asString(), tr.asString()); + break; + } + // estimate Z shift in case of no-distortions + dz = (tr.getTime0() - bcTB) * vdriftTB; + if (tr.hasCSideClustersOnly()) { + dz = -dz; + } + // + prepClus(bcTB); // clusters for MC time + (*mDBGOut) << "tpcMC" + << "counter=" << counter + << "movTrackRef=" << trfm + << "mcTrack=" << mctrO2 + << "side=" << side + << "imposedTB=" << bcTB + << "dz=" << dz + << "clX=" << clX + << "clY=" << clY + << "clZ=" << clZ + << "\n"; } - // propagate the refitted track to the same X/alpha as original track - if (!trackRefit.rotate(tr.getAlpha()) || !prop->PropagateToXBxByBz(trackRefit, tr.getX())) { - LOGP(warn, "Failed to propagate refitted track#{} [{}] to X/alpha of original track [{}]", counter, trackRefit.asString(), tr.asString()); + break; + } + // refit and store the same track for a few compatible times + float tmin = tr.getTime0() - tr.getDeltaTBwd(); + float tmax = tr.getTime0() + tr.getDeltaTFwd(); + for (int it = 0; it < mNMoves; it++) { + float tb = tmin + it * (tmax - tmin) / (mNMoves - 1); + auto trfm = tr.getOuterParam(); // we refit inward + // impose time in TPC timebin and refit inward after resetted covariance + if (!trackRefit(trfm, tb) || !trfm.rotate(tr.getAlpha()) || !prop->PropagateToXBxByBz(trfm, tr.getX())) { + LOGP(warn, "Failed to propagate time={} refitted track#{} [{}] to X/alpha of original track [{}]", tb, counter, trfm.asString(), tr.asString()); continue; } // estimate Z shift in case of no-distortions - float dz = (tr.getTime0() - bcTB) * vdriftTB; + dz = (tr.getTime0() - tb) * vdriftTB; if (tr.hasCSideClustersOnly()) { dz = -dz; - } else if (tr.hasBothSidesClusters()) { - dz = 0; // CE crossing tracks should not be shifted } - // extract cluster info - clSector.clear(); - clRow.clear(); - clIniX.clear(); - clIniY.clear(); - clIniZ.clear(); - clMovX.clear(); - clMovY.clear(); - clMovZ.clear(); - int count = tr.getNClusters(); - const auto* corrMap = mTPCCorrMapsLoader.getCorrMap(); - const o2::tpc::ClusterNative* cl = nullptr; - for (int ic = count; ic--;) { - uint8_t sector, row; - cl = &tr.getCluster(mTPCTrackClusIdx, ic, *mTPCClusterIdxStruct, sector, row); - clSector.push_back(sector); - clRow.push_back(row); - float x, y, z; - corrMap->Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, tr.getTime0()); // nominal time of the track - clIniX.push_back(x); - clIniY.push_back(y); - clIniZ.push_back(z); - corrMap->Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, bcTB); // shifted time of the track - clMovX.push_back(x); - clMovY.push_back(y); - clMovZ.push_back(z); + // + int mnm = mNMoves - 1; + prepClus(tb); // clusters for MC time + (*mDBGOut) << "tpcMov" + << "counter=" << counter + << "copy=" << it + << "maxCopy=" << mnm + << "movTrackRef=" << trfm; + if (mRepRef) { + (*mDBGOut) << "tpcMov" + << "iniTrackRef=" << trf << "time=" << tr.getTime0(); } - - // store results - (*mDBGOut) << "tpc" - << "iniTrack=" << tr - << "iniTrackRef=" << trf - << "movTrackRef=" << trackRefit - << "mcTrack=" << mctrO2 - << "imposedTB=" << bcTB << "dz=" << dz - << "clSector=" << clSector - << "clRow=" << clRow - << "clIniX=" << clIniX - << "clIniY=" << clIniY - << "clIniZ=" << clIniZ - << "clMovX=" << clMovX - << "clMovY=" << clMovY - << "clMovZ=" << clMovZ + (*mDBGOut) << "tpcMov" + << "side=" << side + << "imposedTB=" << tb + << "dz=" << dz + << "clX=" << clX + << "clY=" << clY + << "clZ=" << clZ << "\n"; } } @@ -294,6 +382,7 @@ void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) void TPCTrackStudySpec::endOfStream(EndOfStreamContext& ec) { mDBGOut.reset(); + mDBGOutCl.reset(); } void TPCTrackStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -304,14 +393,20 @@ void TPCTrackStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } } DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) { std::vector outputs; + Options opts{ + {"target-x", VariantType::Float, 70.f, {"Try to propagate to this radius"}}, + {"n-moves", VariantType::Int, 6, {"Number of moves in allow range"}}, + {"dump-clusters", VariantType::Bool, false, {"dump clusters"}}, + {"tf-start", VariantType::Int, 0, {"1st TF to process"}}, + {"tf-end", VariantType::Int, 999999999, {"last TF to process"}}, + {"use-gpu-fitter", VariantType::Bool, false, {"use GPU track model for refit instead of TrackParCov"}}, + {"repeat-ini-ref", VariantType::Bool, false, {"store ini-refit param with every moved track"}}, + {"use-r-as-x", VariantType::Bool, false, {"Use radius instead of target sector X"}}}; auto dataRequest = std::make_shared(); dataRequest->requestTracks(srcTracks, useMC); @@ -325,14 +420,14 @@ DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs); + dataRequest->inputs.emplace_back("corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe); return DataProcessorSpec{ "tpc-track-study", dataRequest->inputs, outputs, AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, - Options{{"target-radius", VariantType::Float, 70.f, {"Try to propagate to this radius"}}}}; + opts}; } } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackInfoExt.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackInfoExt.cxx new file mode 100644 index 0000000000000..f1415e73c10b6 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackInfoExt.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// class for extended track info (for debugging) + +#include "GlobalTrackingStudy/TrackInfoExt.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx new file mode 100644 index 0000000000000..9637c72589196 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx @@ -0,0 +1,1387 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "ReconstructionDataFormats/V0.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "TPCCalibration/VDriftHelper.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "ITStracking/IOUtils.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "ITSBase/GeometryTGeo.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "SimulationDataFormat/MCUtils.h" +#include "SimulationDataFormat/O2DatabasePDG.h" +#include "SimulationDataFormat/TrackReference.h" +#include "CommonDataFormat/BunchFilling.h" +#include "CommonUtils/NameConf.h" +#include "DataFormatsFT0/RecPoints.h" +#include "DataFormatsITSMFT/TrkClusRef.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "FT0Reconstruction/InteractionTag.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "GlobalTrackingStudy/TrackMCStudy.h" +#include "GlobalTrackingStudy/TrackMCStudyConfig.h" +#include "GlobalTrackingStudy/TrackMCStudyTypes.h" +#include "GlobalTracking/MatchTPCITSParams.h" +#include "TPCBase/ParameterElectronics.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/PrimaryVertexExt.h" +#include "DataFormatsFT0/RecPoints.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ReconstructionDataFormats/DCA.h" +#include "Steer/MCKinematicsReader.h" +#include "DCAFitter/DCAFitterN.h" +#include "DetectorsVertexing/SVertexerParams.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "GPUO2InterfaceRefit.h" +#include "GPUParam.h" +#include "GPUParam.inc" +#include "MathUtils/fit.h" +#include "TPCFastTransformPOD.h" +#include +#include +#include +#include +#include +#include + +// workflow to study relation of reco tracks to MCTruth +// o2-trackmc-study-workflow --device-verbosity 3 -b --run + +namespace o2::trackstudy +{ + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; + +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using VTIndexV = std::pair; +using GTrackID = o2::dataformats::GlobalTrackID; +using TBracket = o2::math_utils::Bracketf_t; + +using timeEst = o2::dataformats::TimeStampWithError; + +class TrackMCStudy final : public Task +{ + public: + TrackMCStudy(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool checkSV) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mCheckSV(checkSV) {} + ~TrackMCStudy() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(const o2::globaltracking::RecoContainer& recoData); + + private: + void processTPCTrackRefs(); + void processITSTracks(const o2::globaltracking::RecoContainer& recoData); + void loadTPCOccMap(const o2::globaltracking::RecoContainer& recoData); + void fillMCClusterInfo(const o2::globaltracking::RecoContainer& recoData); + void prepareITSData(const o2::globaltracking::RecoContainer& recoData); + bool processMCParticle(int src, int ev, int trid); + bool addMCParticle(const MCTrack& mctr, const o2::MCCompLabel& lb, TParticlePDG* pPDG = nullptr); + bool acceptMCCharged(const MCTrack& tr, const o2::MCCompLabel& lb, int followDec = -1); + bool propagateToRefX(o2::track::TrackParCov& trcTPC, o2::track::TrackParCov& trcITS); + bool refitV0(int i, o2::dataformats::V0& v0, const o2::globaltracking::RecoContainer& recoData); + void updateTimeDependentParams(ProcessingContext& pc); + float getDCAYCut(float pt) const; + + const std::vector* mCurrMCTracks = nullptr; + TVector3 mCurrMCVertex; + o2::tpc::VDriftHelper mTPCVDriftHelper{}; + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps{nullptr}; + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mDBGOut; + std::vector mTBinClOcc; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting from the TB = i*mNTPCOccBinLength + std::vector mTBinClOccHist; //< original occupancy + std::vector mIntBC; ///< interaction global BC wrt TF start + std::vector mTPCOcc; ///< TPC occupancy for this interaction time + std::vector mITSOcc; //< N ITS clusters in the ROF containing collision + std::vector> mITSClustersArray; ///< ITS clusters created in run() method from compact clusters + const o2::itsmft::TopologyDictionary* mITSDict = nullptr; ///< cluster patterns dictionary + + bool mCheckSV = false; //< check SV binding (apart from prongs availability) + bool mRecProcStage = false; //< flag that the MC particle was added only at the stage of reco tracks processing + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + float mNTPCOccBinLengthInv = -1.f; + int mVerbose = 0; + float mITSTimeBiasMUS = 0.f; + float mITSROFrameLengthMUS = 0.f; ///< ITS RO frame in mus + float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds + + int mNCheckDecays = 0; + + GTrackID::mask_t mTracksSrc{}; + o2::steer::MCKinematicsReader mcReader; // reader of MC information + std::vector mITSROF; + std::vector mITSROFBracket; + std::vector mDecProdLblPool; // labels of decay products to watch, added to MC map + std::vector mMCVtVec{}; + + struct DecayRef { + o2::MCCompLabel mother{}; + o2::track::TrackPar parent{}; + int pdg = 0; + int daughterFirst = -1; + int daughterLast = -1; + int foundSVID = -1; + }; + std::vector> mDecaysMaps; // for every parent particle to watch, store its label and entries of 1st/last decay product labels in mDecProdLblPool + std::unordered_map mSelMCTracks; + std::unordered_map> mSelTRefIdx; + std::vector mSelTRefs; + o2::vertexing::DCAFitterN<2> mFitterV0; + static constexpr float MaxSnp = 0.9; // max snp of ITS or TPC track at xRef to be matched +}; + +void TrackMCStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mcReader.initFromDigitContext("collisioncontext.root"); + + mDBGOut = std::make_unique("trackMCStudy.root", "recreate"); + mVerbose = ic.options().get("device-verbosity"); + + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + for (int id = 0; id < sizeof(params.decayPDG) / sizeof(int); id++) { + if (params.decayPDG[id] < 0) { + break; + } + mNCheckDecays++; + } + mDecaysMaps.resize(mNCheckDecays); +} + +void TrackMCStudy::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + for (int i = 0; i < mNCheckDecays; i++) { + mDecaysMaps[i].clear(); + } + mDecProdLblPool.clear(); + mMCVtVec.clear(); + mCurrMCTracks = nullptr; + + recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + mRecProcStage = false; + process(recoData); +} + +void TrackMCStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + mTPCVDriftHelper.extractCCDBInputs(pc); + auto const& raw = pc.inputs().get("corrMap"); + mTPCCorrMaps = &o2::gpu::TPCFastTransformPOD::get(raw); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + const auto& alpParamsITS = o2::itsmft::DPLAlpideParam::Instance(); + mITSROFrameLengthMUS = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS) ? alpParamsITS.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingMUS : alpParamsITS.roFrameLengthTrig * 1.e-3; + LOGP(info, "VertexTrackMatcher ITSROFrameLengthMUS:{}", mITSROFrameLengthMUS); + + auto& elParam = o2::tpc::ParameterElectronics::Instance(); + mTPCTBinMUS = elParam.ZbinWidth; + o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)); + if (mCheckSV) { + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + mFitterV0.setBz(o2::base::Propagator::Instance()->getNominalBz()); + mFitterV0.setUseAbsDCA(svparam.useAbsDCA); + mFitterV0.setPropagateToPCA(false); + mFitterV0.setMaxR(svparam.maxRIni); + mFitterV0.setMinParamChange(svparam.minParamChange); + mFitterV0.setMinRelChi2Change(svparam.minRelChi2Change); + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setMatCorrType(o2::base::Propagator::MatCorrType(svparam.matCorr)); + mFitterV0.setUsePropagator(svparam.usePropagator); + mFitterV0.setRefitWithMatCorr(svparam.refitWithMatCorr); + mFitterV0.setMaxStep(svparam.maxStep); + mFitterV0.setMaxSnp(svparam.maxSnp); + mFitterV0.setMinXSeed(svparam.minXSeed); + } + } +} + +void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) +{ + constexpr float SQRT12Inv = 0.288675f; + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + auto pvvec = recoData.getPrimaryVertices(); + auto pvvecLbl = recoData.getPrimaryVertexMCLabels(); + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto prop = o2::base::Propagator::Instance(); + int nv = vtxRefs.size(); + float vdriftTB = mTPCVDriftHelper.getVDriftObject().getVDrift() * o2::tpc::ParameterElectronics::Instance().ZbinWidth; // VDrift expressed in cm/TimeBin + float itsBias = 0.5 * mITSROFrameLengthMUS + o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingMUS; // ITS time is supplied in \mus as beginning of ROF + + prepareITSData(recoData); + loadTPCOccMap(recoData); + auto getITSPatt = [&](GTrackID gid, uint8_t& ncl) { + int8_t patt = 0; + if (gid.getSource() == VTIndex::ITSAB) { + const auto& itsTrf = recoData.getITSABRefs()[gid]; + ncl = itsTrf.getNClusters(); + for (int il = 0; il < 7; il++) { + if (itsTrf.hasHitOnLayer(il)) { + patt |= 0x1 << il; + } + } + patt |= 0x1 << 7; + } else { + const auto& itsTr = recoData.getITSTrack(gid); + for (int il = 0; il < 7; il++) { + if (itsTr.hasHitOnLayer(il)) { + patt |= 0x1 << il; + ncl++; + } + } + } + return patt; + }; + + auto fillTPCClusterInfo = [&recoData](const o2::tpc::TrackTPC& trc, RecTrack& tref) { + if (recoData.inputsTPCclusters) { + uint8_t clSect = 0, clRow = 0, lowestR = -1; + uint32_t clIdx = 0; + const auto clRefs = recoData.getTPCTracksClusterRefs(); + const auto tpcClusAcc = recoData.getTPCClusters(); + const auto shMap = recoData.clusterShMapTPC; + for (int ic = 0; ic < trc.getNClusterReferences(); ic++) { // outside -> inside ordering, but on the sector boundaries backward jumps are possible + trc.getClusterReference(clRefs, ic, clSect, clRow, clIdx); + if (clRow < lowestR) { + tref.rowCountTPC++; + lowestR = clRow; + } + unsigned int absoluteIndex = tpcClusAcc.clusterOffset[clSect][clRow] + clIdx; + if (shMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { + tref.nClTPCShared++; + } + } + tref.lowestPadRow = lowestR; + const auto& clus = tpcClusAcc.clusters[clSect][clRow][clIdx]; + int padFromEdge = int(clus.getPad()), npads = o2::gpu::GPUTPCGeometry::NPads(clRow); + if (padFromEdge > npads / 2) { + padFromEdge = npads - 1 - padFromEdge; + } + tref.padFromEdge = uint8_t(padFromEdge); + trc.getClusterReference(clRefs, 0, clSect, clRow, clIdx); + tref.rowMaxTPC = clRow; + } + }; + + auto flagTPCClusters = [&recoData](const o2::tpc::TrackTPC& trc, o2::MCCompLabel lbTrc) { + if (recoData.inputsTPCclusters) { + const auto clRefs = recoData.getTPCTracksClusterRefs(); + const auto* TPCClMClab = recoData.inputsTPCclusters->clusterIndex.clustersMCTruth; + const auto& TPCClusterIdxStruct = recoData.inputsTPCclusters->clusterIndex; + for (int ic = 0; ic < trc.getNClusterReferences(); ic++) { + uint8_t clSect = 0, clRow = 0; + uint32_t clIdx = 0; + trc.getClusterReference(clRefs, ic, clSect, clRow, clIdx); + auto labels = TPCClMClab->getLabels(clIdx + TPCClusterIdxStruct.clusterOffset[clSect][clRow]); + for (auto& lbl : labels) { + if (lbl == lbTrc) { + const_cast(lbl).setFakeFlag(true); // actually, in this way we are flagging that this cluster was correctly attached + break; + } + } + } + } + }; + + { + const auto* digconst = mcReader.getDigitizationContext(); + const auto& mcEvRecords = digconst->getEventRecords(false); + int ITSTimeBias = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; + int ITSROFLen = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; + unsigned int rofCount = 0; + const auto ITSClusROFRec = recoData.getITSClustersROFRecords(); + for (const auto& mcIR : mcEvRecords) { + long tbc = mcIR.differenceInBC(recoData.startIR); + auto& mcVtx = mMCVtVec.emplace_back(); + mcVtx.ts = tbc * o2::constants::lhc::LHCBunchSpacingMUS + mcIR.getTimeOffsetWrtBC() * 1e-3; + mcVtx.ID = mIntBC.size(); + mIntBC.push_back(tbc); + int occBin = tbc / 8 * mNTPCOccBinLengthInv; + mTPCOcc.push_back(occBin < 0 ? mTBinClOcc[0] : (occBin >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[occBin])); + // fill ITS occupancy + long gbc = mcIR.toLong(); + while (rofCount < ITSClusROFRec.size()) { + long rofbcMin = ITSClusROFRec[rofCount].getBCData().toLong() + ITSTimeBias, rofbcMax = rofbcMin + ITSROFLen; + if (gbc < rofbcMin) { // IRs and ROFs are sorted, so this IR is prior of all ROFs + mITSOcc.push_back(0); + } else if (gbc < rofbcMax) { + mITSOcc.push_back(ITSClusROFRec[rofCount].getNEntries()); + } else { + rofCount++; // test next ROF + continue; + } + break; + } + if (mNTPCOccBinLengthInv > 0.f) { + mcVtx.occTPCV.resize(params.nOccBinsDrift); + int grp = TMath::Max(1, TMath::Nint(params.nTBPerOccBin * mNTPCOccBinLengthInv)); + for (int ib = 0; ib < params.nOccBinsDrift; ib++) { + float smb = 0; + int tbs = occBin + TMath::Nint(ib * params.nTBPerOccBin * mNTPCOccBinLengthInv); + for (int ig = 0; ig < grp; ig++) { + if (tbs >= 0 && tbs < int(mTBinClOccHist.size())) { + smb += mTBinClOccHist[tbs]; + } + tbs++; + } + mcVtx.occTPCV[ib] = smb; + } + } + if (rofCount >= ITSClusROFRec.size()) { + mITSOcc.push_back(0); // IR after the last ROF + } + } + } + // collect interesting MC particle (tracks and parents) + int curSrcMC = 0, curEvMC = 0; + for (curSrcMC = 0; curSrcMC < (int)mcReader.getNSources(); curSrcMC++) { + if (mVerbose > 1) { + LOGP(info, "Source {}", curSrcMC); + } + int nev = mcReader.getNEvents(curSrcMC); + bool okAccVtx = true; + if (nev != (int)mMCVtVec.size()) { + LOGP(debug, "source {} has {} events while {} MC vertices were booked", curSrcMC, nev, mMCVtVec.size()); + okAccVtx = false; + if (nev > (int)mMCVtVec.size()) { // QED + continue; + } + } + for (curEvMC = 0; curEvMC < nev; curEvMC++) { + if (mVerbose > 1) { + LOGP(info, "Event {}", curEvMC); + } + mCurrMCTracks = &mcReader.getTracks(curSrcMC, curEvMC); + const_cast(mcReader.getMCEventHeader(curSrcMC, curEvMC)).GetVertex(mCurrMCVertex); + if (okAccVtx) { + auto& pos = mMCVtVec[curEvMC].pos; + if (pos[2] < -999) { + pos[0] = mCurrMCVertex.X(); + pos[1] = mCurrMCVertex.Y(); + pos[2] = mCurrMCVertex.Z(); + } + } + for (int itr = 0; itr < mCurrMCTracks->size(); itr++) { + processMCParticle(curSrcMC, curEvMC, itr); + } + } + } + if (mVerbose > 0) { + for (int id = 0; id < mNCheckDecays; id++) { + LOGP(info, "Decay PDG={} : {} entries", params.decayPDG[id], mDecaysMaps[id].size()); + } + } + + // add reconstruction info to MC particles. If MC particle was not selected before but was reconstrected, account MC info + mRecProcStage = true; // MC particles accepted only at this stage will be flagged + for (int iv = 0; iv < nv; iv++) { + if (mVerbose > 1) { + LOGP(info, "processing PV {} of {}", iv, nv); + } + o2::MCEventLabel pvLbl; + int pvID = -1; + if (iv < (int)pvvecLbl.size()) { + pvLbl = pvvecLbl[iv]; + pvID = iv; + if (pvLbl.isSet() && pvLbl.getEventID() < mMCVtVec.size()) { + mMCVtVec[pvLbl.getEventID()].recVtx.emplace_back(RecPV{pvvec[iv], pvLbl}); + } + } + const auto& vtref = vtxRefs[iv]; + for (int is = GTrackID::NSources; is--;) { + DetID::mask_t dm = GTrackID::getSourceDetectorsMask(is); + if (!mTracksSrc[is] || !recoData.isTrackSourceLoaded(is) || !(dm[DetID::ITS] || dm[DetID::TPC])) { + continue; + } + int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is); + for (int i = idMin; i < idMax; i++) { + auto vid = trackIndex[i]; + const auto& trc = recoData.getTrackParam(vid); + if (trc.getPt() < params.minPt || std::abs(trc.getTgl()) > params.maxTgl) { + continue; + } + auto lbl = recoData.getTrackMCLabel(vid); + if (lbl.isValid()) { + lbl.setFakeFlag(false); + auto entry = mSelMCTracks.find(lbl); + if (entry == mSelMCTracks.end()) { // add the track which was not added during MC scan + if (lbl.getSourceID() != curSrcMC || lbl.getEventID() != curEvMC) { + curSrcMC = lbl.getSourceID(); + curEvMC = lbl.getEventID(); + mCurrMCTracks = &mcReader.getTracks(curSrcMC, curEvMC); + const_cast(mcReader.getMCEventHeader(curSrcMC, curEvMC)).GetVertex(mCurrMCVertex); + } + if (!acceptMCCharged((*mCurrMCTracks)[lbl.getTrackID()], lbl)) { + continue; + } + entry = mSelMCTracks.find(lbl); + } + auto& trackFamily = entry->second; + if (vid.isAmbiguous()) { // do not repeat ambiguous tracks + if (trackFamily.contains(vid)) { + continue; + } + } + auto& trf = trackFamily.recTracks.emplace_back(); + trf.gid = vid; // account(iv, vid); + trf.pvID = pvID; + trf.pvLabel = pvLbl; + while (dm[DetID::ITS] && dm[DetID::TPC]) { // this track should have both ITS and TPC parts, if ITS was mismatched, fill it to its proper MC track slot + auto gidSet = recoData.getSingleDetectorRefs(vid); + if (!gidSet[GTrackID::ITS].isSourceSet()) { + break; // AB track, nothing to check + } + auto lblITS = recoData.getTrackMCLabel(gidSet[GTrackID::ITS]); + if (lblITS == trackFamily.mcTrackInfo.label) { + break; // correct match, no need for special treatment + } + const auto& trcITSF = recoData.getTrackParam(gidSet[GTrackID::ITS]); + if (trcITSF.getPt() < params.minPt || std::abs(trcITSF.getTgl()) > params.maxTgl) { + break; // ignore this track + } + auto entryOfFake = mSelMCTracks.find(lblITS); + if (entryOfFake == mSelMCTracks.end()) { // this MC track was not selected + break; + } + auto& trackFamilyOfFake = entryOfFake->second; + auto& trfOfFake = trackFamilyOfFake.recTracks.emplace_back(); + trfOfFake.gid = gidSet[GTrackID::ITS]; // account(iv, vid); + break; + } + if (mVerbose > 1) { + LOGP(info, "Matched rec track {} to MC track {}", vid.asString(), entry->first.asString()); + } + } else { + continue; + } + } + } + } + + LOGP(info, "collected {} MC tracks", mSelMCTracks.size()); + if (params.minTPCRefsToExtractClRes > 0 || params.storeTPCTrackRefs) { // prepare MC trackrefs for TPC + processTPCTrackRefs(); + } + + int mcnt = 0; + for (auto& entry : mSelMCTracks) { + auto& trackFam = entry.second; + auto& tracks = trackFam.recTracks; + mcnt++; + if (tracks.empty()) { + continue; + } + if (mVerbose > 1) { + LOGP(info, "Processing MC track#{} {} -> {} reconstructed tracks", mcnt - 1, entry.first.asString(), tracks.size()); + } + // sort according to the gid complexity (in principle, should be already sorted due to the backwards loop over NSources above + std::sort(tracks.begin(), tracks.end(), [](const RecTrack& lhs, const RecTrack& rhs) { + const auto mskL = lhs.gid.getSourceDetectorsMask(); + const auto mskR = rhs.gid.getSourceDetectorsMask(); + bool itstpcL = mskL[DetID::ITS] && mskL[DetID::TPC], itstpcR = mskR[DetID::ITS] && mskR[DetID::TPC]; + if (itstpcL && !itstpcR) { // to avoid TPC/TRD or TPC/TOF shadowing ITS/TPC + return true; + } + return lhs.gid.getSource() > rhs.gid.getSource(); + }); + if (params.storeTPCTrackRefs) { + auto rft = mSelTRefIdx.find(entry.first); + if (rft != mSelTRefIdx.end()) { + auto rfent = rft->second; + for (int irf = rfent.first; irf < rfent.second; irf++) { + trackFam.mcTrackInfo.trackRefsTPC.push_back(mSelTRefs[irf]); + } + } + } + // fill track params + int tcnt = 0; + for (auto& tref : tracks) { + if (tref.gid.isSourceSet()) { + auto gidSet = recoData.getSingleDetectorRefs(tref.gid); + tref.track = recoData.getTrackParam(tref.gid); + if (recoData.getTrackMCLabel(tref.gid).isFake()) { + tref.flags |= RecTrack::FakeGLO; + } + auto msk = tref.gid.getSourceDetectorsMask(); + if (msk[DetID::ITS]) { + if (gidSet[GTrackID::ITS].isSourceSet()) { // has ITS track rather than AB tracklet + tref.pattITS = getITSPatt(gidSet[GTrackID::ITS], tref.nClITS); + if (trackFam.entITS < 0) { + trackFam.entITS = tcnt; + } + auto lblITS = recoData.getTrackMCLabel(gidSet[GTrackID::ITS]); + if (lblITS.isFake()) { + tref.flags |= RecTrack::FakeITS; + } + if (lblITS == trackFam.mcTrackInfo.label) { + trackFam.entITSFound = tcnt; + } + } else { // AB ITS tracklet + tref.pattITS = getITSPatt(gidSet[GTrackID::ITSAB], tref.nClITS); + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSAB]).isFake()) { + tref.flags |= RecTrack::FakeITS; + } + } + if (msk[DetID::TPC]) { + if (trackFam.entITSTPC < 0) { // has both ITS and TPC contribution + trackFam.entITSTPC = tcnt; + } + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPC]).isFake()) { + tref.flags |= RecTrack::FakeITSTPC; + } + + if (msk[DetID::TRD]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPCTRD]).isFake()) { + tref.flags |= RecTrack::FakeTRD; + } + if (msk[DetID::TOF]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPCTRDTOF]).isFake()) { + tref.flags |= RecTrack::FakeTOF; + } + } + } else { + if (msk[DetID::TOF]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPCTOF]).isFake()) { + tref.flags |= RecTrack::FakeTOF; + } + } + } + } + } + if (msk[DetID::TPC]) { + const auto& trtpc = recoData.getTPCTrack(gidSet[GTrackID::TPC]); + tref.nClTPC = trtpc.getNClusters(); + if (trtpc.hasBothSidesClusters()) { + tref.flags |= RecTrack::HASACSides; + } + fillTPCClusterInfo(trtpc, tref); + flagTPCClusters(trtpc, entry.first); + if (trackFam.entTPC < 0) { + trackFam.entTPC = tcnt; + trackFam.tpcT0 = trtpc.getTime0(); + } + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPC]).isFake()) { + tref.flags |= RecTrack::FakeTPC; + } + if (!msk[DetID::ITS]) { + if (msk[DetID::TRD]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPCTRD]).isFake()) { + tref.flags |= RecTrack::FakeTRD; + } + if (msk[DetID::TOF]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPCTRDTOF]).isFake()) { + tref.flags |= RecTrack::FakeTOF; + } + } + } else { + if (msk[DetID::TOF]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPCTOF]).isFake()) { + tref.flags |= RecTrack::FakeTOF; + } + } + } + } + } + float ts = 0, terr = 0; + if (tref.gid.getSource() != GTrackID::ITS) { + recoData.getTrackTime(tref.gid, ts, terr); + tref.ts = timeEst{ts, terr}; + } else { + const auto& itsBra = mITSROFBracket[mITSROF[tref.gid.getIndex()]]; + tref.ts = timeEst{itsBra.mean(), itsBra.delta() * SQRT12Inv}; + } + } else { + LOGP(info, "Invalid entry {} of {} getTrackMCLabel {}", tcnt, tracks.size(), tref.gid.asString()); + } + tcnt++; + } + if (trackFam.entITS > -1 && trackFam.entTPC > -1) { // ITS and TPC were found but matching failed + auto vidITS = recoData.getITSContributorGID(tracks[trackFam.entITS].gid); + auto vidTPC = recoData.getTPCContributorGID(tracks[trackFam.entTPC].gid); + auto trcTPC = recoData.getTrackParam(vidTPC); + auto trcITS = recoData.getTrackParamOut(vidITS); + if (propagateToRefX(trcTPC, trcITS)) { + trackFam.trackITSProp = trcITS; + trackFam.trackTPCProp = trcTPC; + } else { + trackFam.trackITSProp.invalidate(); + trackFam.trackTPCProp.invalidate(); + } + } else { + trackFam.trackITSProp.invalidate(); + trackFam.trackTPCProp.invalidate(); + } + } + + // SVertices (V0s) + if (mCheckSV) { + auto v0s = recoData.getV0sIdx(); + auto prpr = [](o2::trackstudy::TrackFamily& f) { + std::string s; + s += fmt::format(" par {} Ntpccl={} Nitscl={} ", f.mcTrackInfo.pdgParent, f.mcTrackInfo.nTPCCl, f.mcTrackInfo.nITSCl); + for (auto& t : f.recTracks) { + s += t.gid.asString(); + s += " "; + } + return s; + }; + for (int svID; svID < (int)v0s.size(); svID++) { + const auto& v0idx = v0s[svID]; + int nOKProngs = 0, realMCSVID = -1; + int8_t decTypeID = -1; + for (int ipr = 0; ipr < v0idx.getNProngs(); ipr++) { + auto mcl = recoData.getTrackMCLabel(v0idx.getProngID(ipr)); // was this MC particle selected? + auto itl = mSelMCTracks.find(mcl); + if (itl == mSelMCTracks.end()) { + nOKProngs = -1; // was not selected as interesting one, ignore + break; + } + auto& trackFamily = itl->second; + int decayParentIndex = trackFamily.mcTrackInfo.parentEntry; + if (decayParentIndex < 0) { // does not come from decay + break; + } + if (ipr == 0) { + realMCSVID = decayParentIndex; + decTypeID = trackFamily.mcTrackInfo.parentDecID; + nOKProngs = 1; + LOGP(debug, "Prong{} {} comes from {}/{}", ipr, prpr(trackFamily), decTypeID, realMCSVID); + continue; + } + if (realMCSVID != decayParentIndex || decTypeID != trackFamily.mcTrackInfo.parentDecID) { + break; + } + LOGP(debug, "Prong{} {} comes from {}/{}", ipr, prpr(trackFamily), decTypeID, realMCSVID); + nOKProngs++; + } + if (nOKProngs == v0idx.getNProngs()) { // all prongs are from the decay of MC parent which deemed to be interesting, flag it + LOGP(debug, "Decay {}/{} was found", decTypeID, realMCSVID); + mDecaysMaps[decTypeID][realMCSVID].foundSVID = svID; + } + } + } + + // collect ITS/TPC cluster info for selected MC particles + fillMCClusterInfo(recoData); + + // single tracks + for (auto& entry : mSelMCTracks) { + auto& trackFam = entry.second; + (*mDBGOut) << "tracks" << "tr=" << trackFam << "\n"; + } + + // decays + std::vector decFam; + for (int id = 0; id < mNCheckDecays; id++) { + std::string decTreeName = fmt::format("dec{}", params.decayPDG[id]); + for (const auto& dec : mDecaysMaps[id]) { + decFam.clear(); + bool skip = false; + for (int idd = dec.daughterFirst; idd <= dec.daughterLast; idd++) { + auto dtLbl = mDecProdLblPool[idd]; // daughter MC label + const auto& dtFamily = mSelMCTracks[dtLbl]; + if (dtFamily.mcTrackInfo.pdgParent != dec.pdg) { + LOGP(error, "{}-th decay (pdg={}): {} in {}:{} range refers to MC track with pdgParent = {}", id, params.decayPDG[id], idd, dec.daughterFirst, dec.daughterLast, dtFamily.mcTrackInfo.pdgParent); + skip = true; + break; + } + decFam.push_back(dtFamily); + } + if (!skip) { + o2::dataformats::V0 v0; + if (dec.foundSVID >= 0 && !refitV0(dec.foundSVID, v0, recoData)) { + v0.invalidate(); + } + (*mDBGOut) << decTreeName.c_str() << "pdgPar=" << dec.pdg << "trPar=" << dec.parent << "prod=" << decFam << "found=" << dec.foundSVID << "sv=" << v0 << "\n"; + } + } + } + + for (auto& mcVtx : mMCVtVec) { // sort rec.vertices in mult. order + std::sort(mcVtx.recVtx.begin(), mcVtx.recVtx.end(), [](const RecPV& lhs, const RecPV& rhs) { + return lhs.pv.getNContributors() > rhs.pv.getNContributors(); + }); + (*mDBGOut) << "mcVtxTree" << "mcVtx=" << mcVtx << "\n"; + } + + if (params.storeITSInfo) { + processITSTracks(recoData); + } +} + +void TrackMCStudy::processTPCTrackRefs() +{ + constexpr float alpsec[18] = {0.174533, 0.523599, 0.872665, 1.221730, 1.570796, 1.919862, 2.268928, 2.617994, 2.967060, 3.316126, 3.665191, 4.014257, 4.363323, 4.712389, 5.061455, 5.410521, 5.759587, 6.108652}; + constexpr float sinAlp[18] = {0.173648, 0.500000, 0.766044, 0.939693, 1.000000, 0.939693, 0.766044, 0.500000, 0.173648, -0.173648, -0.500000, -0.766044, -0.939693, -1.000000, -0.939693, -0.766044, -0.500000, -0.173648}; + constexpr float cosAlp[18] = {0.984808, 0.866025, 0.642788, 0.342020, 0.000000, -0.342020, -0.642788, -0.866025, -0.984808, -0.984808, -0.866025, -0.642788, -0.342020, -0.000000, 0.342020, 0.642788, 0.866025, 0.984808}; + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + for (auto& entry : mSelMCTracks) { + auto lb = entry.first; + auto trspan = mcReader.getTrackRefs(lb.getSourceID(), lb.getEventID(), lb.getTrackID()); + int q = entry.second.mcTrackInfo.track.getCharge(); + if (q * q != 1) { + continue; + } + int ref0entry = mSelTRefs.size(), nrefsSel = 0; + for (const auto& trf : trspan) { + if (trf.getDetectorId() != 1) { // process TPC only + continue; + } + float pT = std::sqrt(trf.Px() * trf.Px() + trf.Py() * trf.Py()); + if (pT < 0.05) { + continue; + } + float secX, secY, phi = std::atan2(trf.Y(), trf.X()); + int sector = o2::math_utils::angle2Sector(phi); + o2::math_utils::rotateZInv(trf.X(), trf.Y(), secX, secY, sinAlp[sector], cosAlp[sector]); // sector coordinates + float phiPt = std::atan2(trf.Py(), trf.Px()); + o2::math_utils::bringTo02Pi(phiPt); + auto dphiPt = phiPt - alpsec[sector]; + if (dphiPt > o2::constants::math::PI) { // account for wraps + dphiPt -= o2::constants::math::TwoPI; + } else if (dphiPt < -o2::constants::math::PI) { + dphiPt += o2::constants::math::TwoPI; + } else if (std::abs(dphiPt) > o2::constants::math::PIHalf * 0.8) { + continue; // ignore backward going or parallel to padrows tracks + } + float tgL = trf.Pz() / pT; + std::array pars = {secY, trf.Z(), std::sin(dphiPt), tgL, q / pT}; + auto& refTrack = mSelTRefs.emplace_back(secX, alpsec[sector], pars); + refTrack.setUserField(uint16_t(sector)); + nrefsSel++; + } + if (nrefsSel < params.minTPCRefsToExtractClRes) { + mSelTRefs.resize(ref0entry); // discard unused tracks + continue; + } else { + mSelTRefIdx[lb] = std::make_pair(ref0entry, ref0entry + nrefsSel); + } + } +} + +void TrackMCStudy::fillMCClusterInfo(const o2::globaltracking::RecoContainer& recoData) +{ + // TPC clusters info + const auto& TPCClusterIdxStruct = recoData.inputsTPCclusters->clusterIndex; + const auto* TPCClMClab = recoData.inputsTPCclusters->clusterIndex.clustersMCTruth; + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + + ClResTPC clRes{}; + for (uint8_t row = 0; row < 152; row++) { // we need to go in increasing row, so this should be the outer loop + for (uint8_t sector = 0; sector < 36; sector++) { + unsigned int offs = TPCClusterIdxStruct.clusterOffset[sector][row]; + for (unsigned int icl0 = 0; icl0 < TPCClusterIdxStruct.nClusters[sector][row]; icl0++) { + const auto labels = TPCClMClab->getLabels(icl0 + offs); + int ncontLb = 0; // number of real contrubutors to this label (w/o noise) + for (const auto& lbl : labels) { + if (!lbl.isValid()) { + continue; + } + ncontLb++; + } + const auto& clus = TPCClusterIdxStruct.clusters[sector][row][icl0]; + int tbinH = int(clus.getTime() * mNTPCOccBinLengthInv); // time bin converted to slot of the occ. histo + clRes.contTracks.clear(); + bool doClusRes = (params.minTPCRefsToExtractClRes > 0) && (params.rejectClustersResStat <= 0. || gRandom->Rndm() < params.rejectClustersResStat); + for (auto lbl : labels) { + bool corrAttach = lbl.isFake(); // was this flagged in the flagTPCClusters called from process ? + lbl.setFakeFlag(false); + auto entry = mSelMCTracks.find(lbl); + if (entry == mSelMCTracks.end()) { // not selected + continue; + } + auto& mctr = entry->second.mcTrackInfo; + mctr.nTPCCl++; + if (row > mctr.maxTPCRow) { + mctr.maxTPCRow = row; + mctr.maxTPCRowSect = sector; + mctr.nUsedPadRows++; + } else if (row == 0 && mctr.nUsedPadRows == 0) { + mctr.nUsedPadRows++; + } + if (row < mctr.minTPCRow) { + mctr.minTPCRow = row; + mctr.minTPCRowSect = sector; + } + if (mctr.minTPCRowSect == sector && row > mctr.maxTPCRowInner) { + mctr.maxTPCRowInner = row; + } + if (ncontLb > 1) { + mctr.nTPCClShared++; + } + // try to extract ideal track position + if (doClusRes) { + auto entTRefIDsIt = mSelTRefIdx.find(lbl); + if (entTRefIDsIt == mSelTRefIdx.end()) { + continue; + } + float xc, yc, zc; + mTPCCorrMaps->Transform(sector, row, clus.getPad(), clus.getTime(), xc, yc, zc, mctr.bcInTF / 8.); // nominal time of the track + + const auto& entTRefIDs = entTRefIDsIt->second; + // find bracketing TRef params + int entIDBelow = -1, entIDAbove = -1; + float xBelow = -1e6, xAbove = 1e6; + + for (int entID = entTRefIDs.first; entID < entTRefIDs.second; entID++) { + const auto& refTr = mSelTRefs[entID]; + if (refTr.getUserField() != sector % 18) { + continue; + } + if ((refTr.getX() < xc) && (refTr.getX() > xBelow) && (refTr.getX() > xc - params.maxTPCRefExtrap)) { + xBelow = refTr.getX(); + entIDBelow = entID; + } + if ((refTr.getX() > xc) && (refTr.getX() < xAbove) && (refTr.getX() < xc + params.maxTPCRefExtrap)) { + xAbove = refTr.getX(); + entIDAbove = entID; + } + } + if ((entIDBelow < 0 && entIDAbove < 0) || (params.requireTopBottomRefs && (entIDBelow < 0 || entIDAbove < 0))) { + continue; + } + auto prop = o2::base::Propagator::Instance(); + o2::track::TrackPar tparAbove, tparBelow; + bool okBelow = entIDBelow >= 0 && prop->PropagateToXBxByBz((tparBelow = mSelTRefs[entIDBelow]), xc, 0.99, 2.); + bool okAbove = entIDAbove >= 0 && prop->PropagateToXBxByBz((tparAbove = mSelTRefs[entIDAbove]), xc, 0.99, 2.); + if ((!okBelow && !okAbove) || (params.requireTopBottomRefs && (!okBelow || !okAbove))) { + continue; + } + + int nmeas = 0; + auto& clCont = clRes.contTracks.emplace_back(); + clCont.corrAttach = corrAttach; + if (okBelow) { + clCont.below = {mSelTRefs[entIDBelow].getX(), tparBelow.getY(), tparBelow.getZ()}; + clCont.snp += tparBelow.getSnp(); + clCont.tgl += tparBelow.getTgl(); + clCont.q2pt += tparBelow.getQ2Pt(); + nmeas++; + } + if (okAbove) { + clCont.above = {mSelTRefs[entIDAbove].getX(), tparAbove.getY(), tparAbove.getZ()}; + clCont.snp += tparAbove.getSnp(); + clCont.tgl += tparAbove.getTgl(); + clCont.q2pt += tparAbove.getQ2Pt(); + nmeas++; + } + if (nmeas) { + if (clRes.contTracks.size() == 1) { + int occBin = mctr.bcInTF / 8 * mNTPCOccBinLengthInv; + clRes.occ = occBin < 0 ? mTBinClOcc[0] : (occBin >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[occBin]); + } + clCont.xyz = {xc, yc, zc}; + if (nmeas > 1) { + clCont.snp *= 0.5; + clCont.tgl *= 0.5; + clCont.q2pt *= 0.5; + } + } else { + clRes.contTracks.pop_back(); + } + } + } + if (clRes.getNCont()) { + clRes.sect = sector; + clRes.row = row; + clRes.qtot = clus.getQtot(); + clRes.qmax = clus.getQmax(); + clRes.flags = clus.getFlags(); + clRes.sigmaTimePacked = clus.sigmaTimePacked; + clRes.sigmaPadPacked = clus.sigmaPadPacked; + clRes.ncont = ncontLb; + clRes.sortCont(); + + if (tbinH < 0) { + tbinH = 0; + } else if (tbinH >= int(mTBinClOccHist.size())) { + tbinH = (int)mTBinClOccHist.size() - 1; + } + clRes.occBin = mTBinClOccHist[tbinH]; + + (*mDBGOut) << "clres" << "clr=" << clRes << "\n"; + } + } + } + } + // fill ITS cluster info + const auto* mcITSClusters = recoData.getITSClustersMCLabels(); + const auto& ITSClusters = recoData.getITSClusters(); + for (unsigned int icl = 0; icl < ITSClusters.size(); icl++) { + const auto labels = mcITSClusters->getLabels(icl); + for (const auto& lbl : labels) { + auto entry = mSelMCTracks.find(lbl); + if (entry == mSelMCTracks.end()) { // not selected + continue; + } + auto& mctr = entry->second.mcTrackInfo; + mctr.nITSCl++; + mctr.pattITSCl |= 0x1 << o2::itsmft::ChipMappingITS::getLayer(ITSClusters[icl].getChipID()); + } + } +} + +bool TrackMCStudy::propagateToRefX(o2::track::TrackParCov& trcTPC, o2::track::TrackParCov& trcITS) +{ + bool refReached = false; + constexpr float TgHalfSector = 0.17632698f; + const auto& par = o2::globaltracking::MatchTPCITSParams::Instance(); + int trialsLeft = 2; + while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trcTPC, par.XMatchingRef, MaxSnp, 2., par.matCorr)) { + if (refReached) { + break; + } + // make sure the track is indeed within the sector defined by alpha + if (fabs(trcTPC.getY()) < par.XMatchingRef * TgHalfSector) { + refReached = true; + break; // ok, within + } + if (!trialsLeft--) { + break; + } + auto alphaNew = o2::math_utils::angle2Alpha(trcTPC.getPhiPos()); + if (!trcTPC.rotate(alphaNew) != 0) { + break; // failed (RS: check effect on matching tracks to neighbouring sector) + } + } + if (!refReached) { + return false; + } + refReached = false; + float alp = trcTPC.getAlpha(); + if (!trcITS.rotate(alp) != 0 || !o2::base::Propagator::Instance()->PropagateToXBxByBz(trcITS, par.XMatchingRef, MaxSnp, 2., par.matCorr)) { + return false; + } + return true; +} + +void TrackMCStudy::endOfStream(EndOfStreamContext& ec) +{ + mDBGOut.reset(); +} + +void TrackMCStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { + LOG(info) << "ITS Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); + mITSTimeBiasMUS = par.roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; + mITSROFrameLengthMUS = par.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mITSDict = (const o2::itsmft::TopologyDictionary*)obj; + return; + } +} + +//_____________________________________________________ +void TrackMCStudy::prepareITSData(const o2::globaltracking::RecoContainer& recoData) +{ + const auto ITSTracksArray = recoData.getITSTracks(); + const auto ITSTrackROFRec = recoData.getITSTracksROFRecords(); + int nROFs = ITSTrackROFRec.size(); + mITSROF.clear(); + mITSROFBracket.clear(); + mITSROF.reserve(ITSTracksArray.size()); + mITSROFBracket.reserve(ITSTracksArray.size()); + for (int irof = 0; irof < nROFs; irof++) { + const auto& rofRec = ITSTrackROFRec[irof]; + long nBC = rofRec.getBCData().differenceInBC(recoData.startIR); + float tMin = nBC * o2::constants::lhc::LHCBunchSpacingMUS + mITSTimeBiasMUS; + float tMax = tMin + mITSROFrameLengthMUS; + mITSROFBracket.emplace_back(tMin, tMax); + for (int it = 0; it < rofRec.getNEntries(); it++) { + mITSROF.push_back(irof); + } + } +} +/* +float TrackMCStudy::getDCAYCut(float pt) const +{ + static TF1 fun("dcayvspt", mDCAYFormula.c_str(), 0, 20); + return fun.Eval(pt); +} +*/ + +bool TrackMCStudy::processMCParticle(int src, int ev, int trid) +{ + const auto& mcPart = (*mCurrMCTracks)[trid]; + int pdg = mcPart.GetPdgCode(); + bool res = false; + while (true) { + auto lbl = o2::MCCompLabel(trid, ev, src); + int decay = -1; // is this decay to watch? + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + if (mcPart.T() < params.decayMotherMaxT) { + for (int id = 0; id < mNCheckDecays; id++) { + if (params.decayPDG[id] == std::abs(pdg)) { + decay = id; + break; + } + } + if (decay >= 0) { // check if decay and kinematics is acceptable + auto& decayPool = mDecaysMaps[decay]; + int idd0 = mcPart.getFirstDaughterTrackId(), idd1 = mcPart.getLastDaughterTrackId(); // we want only charged and trackable daughters + int dtStart = mDecProdLblPool.size(), dtEnd = -1; + if (idd0 < 0) { + break; + } + for (int idd = idd0; idd <= idd1; idd++) { + const auto& product = (*mCurrMCTracks)[idd]; + auto lbld = o2::MCCompLabel(idd, ev, src); + if (!acceptMCCharged(product, lbld, decay)) { + decay = -1; // discard decay + mDecProdLblPool.resize(dtStart); + break; + } + mDecProdLblPool.push_back(lbld); // register prong entry and label + } + if (decay >= 0) { + // account decay + dtEnd = mDecProdLblPool.size(); + for (int dtid = dtStart; dtid < dtEnd; dtid++) { // flag selected decay parent entry in the prongs MCs + mSelMCTracks[mDecProdLblPool[dtid]].mcTrackInfo.parentEntry = decayPool.size(); + mSelMCTracks[mDecProdLblPool[dtid]].mcTrackInfo.parentDecID = int8_t(decay); + } + dtEnd--; + std::array xyz{(float)mcPart.GetStartVertexCoordinatesX(), (float)mcPart.GetStartVertexCoordinatesY(), (float)mcPart.GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)mcPart.GetStartVertexMomentumX(), (float)mcPart.GetStartVertexMomentumY(), (float)mcPart.GetStartVertexMomentumZ()}; + decayPool.emplace_back(DecayRef{lbl, + o2::track::TrackPar(xyz, pxyz, TMath::Nint(O2DatabasePDG::Instance()->GetParticle(mcPart.GetPdgCode())->Charge() / 3), false), + mcPart.GetPdgCode(), dtStart, dtEnd}); + if (mVerbose > 1) { + LOGP(info, "Adding MC parent pdg={} {}, with prongs in {}:{} range", pdg, lbl.asString(), dtStart, dtEnd); + } + res = true; // Accept! + } + break; + } + } + // check if this is a charged which should be processed but was not accounted as a decay product + if (mSelMCTracks.find(lbl) == mSelMCTracks.end()) { + res = acceptMCCharged(mcPart, lbl); + } + break; + } + return res; +} + +bool TrackMCStudy::acceptMCCharged(const MCTrack& tr, const o2::MCCompLabel& lb, int followDecay) +{ + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + if (tr.GetPt() < params.minPtMC || + std::abs(tr.GetTgl()) > params.maxTglMC || + tr.R2() > params.maxRMC * params.maxRMC) { + if (mVerbose > 1 && followDecay > -1) { + LOGP(info, "rejecting decay {} prong : pdg={}, pT={}, tgL={}, r={}", followDecay, tr.GetPdgCode(), tr.GetPt(), tr.GetTgl(), std::sqrt(tr.R2())); + } + return false; + } + float dx = tr.GetStartVertexCoordinatesX() - mCurrMCVertex.X(), dy = tr.GetStartVertexCoordinatesY() - mCurrMCVertex.Y(), dz = tr.GetStartVertexCoordinatesZ() - mCurrMCVertex.Z(); + float r2 = dx * dx + dy * dy; + float posTgl2 = r2 > 1 && std::abs(dz) < 20 ? dz * dz / r2 : 0; + if (posTgl2 > params.maxPosTglMC * params.maxPosTglMC) { + if (mVerbose > 1 && followDecay > -1) { + LOGP(info, "rejecting decay {} prong : pdg={}, pT={}, tgL={}, dr={}, dz={} r={}, z={}, posTgl={}", followDecay, tr.GetPdgCode(), tr.GetPt(), tr.GetTgl(), std::sqrt(r2), dz, std::sqrt(tr.R2()), tr.GetStartVertexCoordinatesZ(), std::sqrt(posTgl2)); + } + return false; + } + if (params.requireITSorTPCTrackRefs) { + auto trspan = mcReader.getTrackRefs(lb.getSourceID(), lb.getEventID(), lb.getTrackID()); + bool ok = false; + for (const auto& trf : trspan) { + if (trf.getDetectorId() == DetID::ITS || trf.getDetectorId() == DetID::TPC) { + ok = true; + break; + } + } + if (!ok) { + return false; + } + } + TParticlePDG* pPDG = O2DatabasePDG::Instance()->GetParticle(tr.GetPdgCode()); + if (!pPDG) { + LOGP(debug, "Unknown particle {}", tr.GetPdgCode()); + return false; + } + if (pPDG->Charge() == 0.) { + return false; + } + return addMCParticle(tr, lb, pPDG); +} + +bool TrackMCStudy::addMCParticle(const MCTrack& mcPart, const o2::MCCompLabel& lb, TParticlePDG* pPDG) +{ + std::array xyz{(float)mcPart.GetStartVertexCoordinatesX(), (float)mcPart.GetStartVertexCoordinatesY(), (float)mcPart.GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)mcPart.GetStartVertexMomentumX(), (float)mcPart.GetStartVertexMomentumY(), (float)mcPart.GetStartVertexMomentumZ()}; + if (!pPDG && !(pPDG = O2DatabasePDG::Instance()->GetParticle(mcPart.GetPdgCode()))) { + LOGP(debug, "Unknown particle {}", mcPart.GetPdgCode()); + return false; + } + auto& mcEntry = mSelMCTracks[lb]; + mcEntry.mcTrackInfo.pdg = mcPart.GetPdgCode(); + mcEntry.mcTrackInfo.track = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), true); + mcEntry.mcTrackInfo.label = lb; + mcEntry.mcTrackInfo.bcInTF = mIntBC[lb.getEventID()]; + mcEntry.mcTrackInfo.occTPC = mTPCOcc[lb.getEventID()]; + mcEntry.mcTrackInfo.occITS = mITSOcc[lb.getEventID()]; + mcEntry.mcTrackInfo.occTPCV = mMCVtVec[lb.getEventID()].occTPCV; + if (mRecProcStage) { + mcEntry.mcTrackInfo.setAddedAtRecStage(); + } + if (o2::mcutils::MCTrackNavigator::isPhysicalPrimary(mcPart, *mCurrMCTracks)) { + mcEntry.mcTrackInfo.setPrimary(); + } + int moth = -1; + o2::MCCompLabel mclbPar; + if ((moth = mcPart.getMotherTrackId()) >= 0) { + const auto& mcPartPar = (*mCurrMCTracks)[moth]; + mcEntry.mcTrackInfo.pdgParent = mcPartPar.GetPdgCode(); + } + if (mcPart.isPrimary() && mcReader.getNEvents(lb.getSourceID()) == mMCVtVec.size()) { + mMCVtVec[lb.getEventID()].nTrackSel++; + } + if (mVerbose > 1) { + LOGP(info, "Adding charged MC pdg={} {} ", mcPart.GetPdgCode(), lb.asString()); + } + return true; +} + +bool TrackMCStudy::refitV0(int i, o2::dataformats::V0& v0, const o2::globaltracking::RecoContainer& recoData) +{ + const auto& id = recoData.getV0sIdx()[i]; + auto seedP = recoData.getTrackParam(id.getProngID(0)); + auto seedN = recoData.getTrackParam(id.getProngID(1)); + bool isTPConly = (id.getProngID(0).getSource() == GTrackID::TPC) || (id.getProngID(1).getSource() == GTrackID::TPC); + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + if (svparam.mTPCTrackPhotonTune && isTPConly) { + mFitterV0.setMaxDZIni(svparam.mTPCTrackMaxDZIni); + mFitterV0.setMaxDXYIni(svparam.mTPCTrackMaxDXYIni); + mFitterV0.setMaxChi2(svparam.mTPCTrackMaxChi2); + mFitterV0.setCollinear(true); + } + int nCand = mFitterV0.process(seedP, seedN); + if (svparam.mTPCTrackPhotonTune && isTPConly) { // restore + // Reset immediately to the defaults + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setCollinear(false); + } + if (nCand == 0) { // discard this pair + return false; + } + const int cand = 0; + if (!mFitterV0.isPropagateTracksToVertexDone(cand) && !mFitterV0.propagateTracksToVertex(cand)) { + return false; + } + const auto& trPProp = mFitterV0.getTrack(0, cand); + const auto& trNProp = mFitterV0.getTrack(1, cand); + std::array pP{}, pN{}; + trPProp.getPxPyPzGlo(pP); + trNProp.getPxPyPzGlo(pN); + std::array pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + auto p2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1] + pV0[2] * pV0[2]; + const auto& pv = recoData.getPrimaryVertex(id.getVertexID()); + const auto v0XYZ = mFitterV0.getPCACandidatePos(cand); + float dx = v0XYZ[0] - pv.getX(), dy = v0XYZ[1] - pv.getY(), dz = v0XYZ[2] - pv.getZ(), prodXYZv0 = dx * pV0[0] + dy * pV0[1] + dz * pV0[2]; + float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0); + new (&v0) o2::dataformats::V0(v0XYZ, pV0, mFitterV0.calcPCACovMatrixFlat(cand), trPProp, trNProp); + v0.setDCA(mFitterV0.getChi2AtPCACandidate(cand)); + v0.setCosPA(cosPA); + return true; +} + +void TrackMCStudy::loadTPCOccMap(const o2::globaltracking::RecoContainer& recoData) +{ + auto NHBPerTF = o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + const auto& TPCOccMap = recoData.occupancyMapTPC; + auto prop = o2::base::Propagator::Instance(); + auto TPCRefitter = std::make_unique(&recoData.inputsTPCclusters->clusterIndex, mTPCCorrMaps, prop->getNominalBz(), + recoData.getTPCTracksClusterRefs().data(), 0, recoData.clusterShMapTPC.data(), TPCOccMap.data(), TPCOccMap.size(), nullptr, prop); + mNTPCOccBinLength = TPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; + mTBinClOcc.clear(); + if (mNTPCOccBinLength > 1 && TPCOccMap.size()) { + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + int nTPCBins = NHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0; + int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); + mTBinClOcc.resize(nTPCOccBins); + mTBinClOccHist.resize(nTPCOccBins); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + for (int i = 0; i < nTPCOccBins; i++) { + mTBinClOccHist[i] = TPCRefitter->getParam()->GetUnscaledMult(tb); + tb += mNTPCOccBinLength; + } + for (int i = nTPCOccBins; i--;) { + sm += mTBinClOccHist[i]; + if (i + sumBins < nTPCOccBins) { + sm -= mTBinClOccHist[i + sumBins]; + } + mTBinClOcc[i] = sm; + } + } else { + mTBinClOcc.resize(1); + mTBinClOccHist.resize(1); + } +} + +void TrackMCStudy::processITSTracks(const o2::globaltracking::RecoContainer& recoData) +{ + if (!mITSDict) { + LOGP(warn, "ITS data is not loaded"); + return; + } + const auto itsTracks = recoData.getITSTracks(); + const auto itsLbls = recoData.getITSTracksMCLabels(); + const auto itsClRefs = recoData.getITSTracksClusterRefs(); + const auto clusITS = recoData.getITSClusters(); + const auto patterns = recoData.getITSClustersPatterns(); + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + auto pattIt = patterns.begin(); + mITSClustersArray.clear(); + mITSClustersArray.reserve(clusITS.size()); + + o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict); + auto geom = o2::its::GeometryTGeo::Instance(); + int ntr = itsLbls.size(); + LOGP(info, "We have {} ITS clusters and the number of patterns is {}, ITSdict:{} NMCLabels: {}", clusITS.size(), patterns.size(), mITSDict != nullptr, itsLbls.size()); + + std::vector evord(ntr); + std::iota(evord.begin(), evord.end(), 0); + std::sort(evord.begin(), evord.end(), [&](int i, int j) { return itsLbls[i] < itsLbls[j]; }); + std::vector outHitInfo; + std::array cl2arr{}; + + for (int itr0 = 0; itr0 < ntr; itr0++) { + auto itr = evord[itr0]; + const auto& itsTr = itsTracks[itr]; + const auto& itsLb = itsLbls[itr]; + // LOGP(info,"proc {} {} {}",itr0, itr, itsLb.asString()); + int nCl = itsTr.getNClusters(); + if (itsLb.isFake() || nCl < params.minITSClForITSoutput) { + continue; + } + auto entrySel = mSelMCTracks.find(itsLb); + if (entrySel == mSelMCTracks.end()) { + continue; + } + outHitInfo.clear(); + cl2arr.fill(-1); + auto clEntry = itsTr.getFirstClusterEntry(); + for (int iCl = nCl; iCl--;) { // clusters are stored from outer to inner layers + const auto& cls = mITSClustersArray[itsClRefs[clEntry + iCl]]; + int hpos = outHitInfo.size(); + auto& hinf = outHitInfo.emplace_back(); + hinf.clus = cls; + hinf.clus.setCount(geom->getLayer(cls.getSensorID())); + geom->getSensorXAlphaRefPlane(cls.getSensorID(), hinf.chipX, hinf.chipAlpha); + cl2arr[hinf.clus.getCount()] = hpos; // to facilitate finding the cluster of the layer + } + auto trspan = mcReader.getTrackRefs(itsLb.getSourceID(), itsLb.getEventID(), itsLb.getTrackID()); + int ilrc = -1, nrefAcc = 0; + for (const auto& trf : trspan) { + if (trf.getDetectorId() != 0) { // process ITS only + continue; + } + int lrt = trf.getUserId(); // layer of the reference, but there might be multiple hits on the same layer + int clEnt = cl2arr[lrt]; + if (clEnt < 0) { + continue; + } + auto& hinf = outHitInfo[clEnt]; + float traX, traY; + o2::math_utils::rotateZInv(trf.X(), trf.Y(), traX, traY, std::sin(hinf.chipAlpha), std::cos(hinf.chipAlpha)); // tracking coordinates of the reference + if (hinf.trefXT < 1 || std::abs(traX - hinf.chipX) < std::abs(hinf.trefXT - hinf.chipX)) { + if (hinf.trefXT < 1) { + nrefAcc++; + } + hinf.tref = trf; + hinf.trefXT = traX; + hinf.trefYT = traY; + } + } + (*mDBGOut) << "itsTree" << "hits=" << outHitInfo << "trIn=" << ((o2::track::TrackParCov&)itsTr) << "trOut=" << itsTr.getParamOut() << "mcTr=" << entrySel->second.mcTrackInfo.track << "mcPDG=" << entrySel->second.mcTrackInfo.pdg << "nTrefs=" << nrefAcc << "\n"; + } +} + +DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool checkSV) +{ + std::vector outputs; + Options opts{ + {"device-verbosity", VariantType::Int, 0, {"Verbosity level"}}, + {"dcay-vs-pt", VariantType::String, "0.0105 + 0.0350 / pow(x, 1.1)", {"Formula for global tracks DCAy vs pT cut"}}, + {"min-tpc-clusters", VariantType::Int, 60, {"Cut on TPC clusters"}}, + {"max-tpc-dcay", VariantType::Float, 2.f, {"Cut on TPC dcaY"}}, + {"max-tpc-dcaz", VariantType::Float, 2.f, {"Cut on TPC dcaZ"}}, + {"min-x-prop", VariantType::Float, 6.f, {"track should be propagated to this X at least"}}}; + auto dataRequest = std::make_shared(); + bool useMC = true; + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcClusters, useMC); + dataRequest->requestPrimaryVertices(useMC); + if (checkSV) { + dataRequest->requestSecondaryVertices(useMC); + } + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + dataRequest->inputs.emplace_back("corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + + return DataProcessorSpec{ + "track-mc-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, checkSV)}, + opts}; +} + +} // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyConfig.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyConfig.cxx new file mode 100644 index 0000000000000..300e69c514ae3 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyConfig.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/TrackMCStudyConfig.h" + +O2ParamImpl(o2::trackstudy::TrackMCStudyConfig); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyTypes.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyTypes.cxx new file mode 100644 index 0000000000000..b6236b7bf0e73 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyTypes.cxx @@ -0,0 +1,142 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/TrackMCStudyTypes.h" + +namespace o2::trackstudy +{ + +RecTrack TrackFamily::dummyRecTrack; + +// get longest number of clusters on consecutive layers +int MCTrackInfo::getNITSClusCont() const +{ + if (nITSCl < 2) { + return nITSCl; + } + int longest = 0, current = 0; + for (int i = 0; i < 7; i++) { + if (pattITSCl & (0x1 << i)) { + if (++current > longest) { + longest = current; + } + } else { + current = 0; + } + } + return longest; +} + +// check how many clusters ITS-TPC afterburner could see (consecutively occupied layers starting from the last one) +int MCTrackInfo::getNITSClusForAB() const +{ + int ncl = 0; + if (nITSCl) { + for (int i = 6; i > 2; i--) { + if (pattITSCl & (0x1 << i)) { + ncl++; + } else { + break; + } + } + } + return ncl; +} + +// lowest ITS layer with cluster +int MCTrackInfo::getLowestITSLayer() const +{ + if (nITSCl) { + for (int i = 0; i < 7; i++) { + if (pattITSCl & (0x1 << i)) { + return i; + } + } + } + return -1; +} + +// highest ITS layer with cluster +int MCTrackInfo::getHighestITSLayer() const +{ + if (nITSCl) { + for (int i = 7; i--;) { + if (pattITSCl & (0x1 << i)) { + return i; + } + } + } + return -1; +} + +o2::track::TrackPar MCTrackInfo::getTrackParTPC(float b, float x) const +{ + o2::track::TrackPar t(track); + int ntri = 0; + while (ntri < 2) { + int sector0 = o2::math_utils::angle2Sector(t.getAlpha()); + if (!t.propagateParamTo(x, b)) { + t.invalidate(); + break; + } + int sector = o2::math_utils::angle2Sector(t.getPhiPos()); + float alpha = o2::math_utils::sector2Angle(sector); + if (!t.rotateParam(alpha)) { + t.invalidate(); + break; + } + if (sector != sector0) { + ntri++; + continue; + } + break; + } + // printf("%s ->\n%s <-\n",track.asString().c_str(), t.asString().c_str()); + return t; +} + +float MCTrackInfo::getTrackParTPCPar(int i, float b, float x) const +{ + auto t = getTrackParTPC(b, x); + return t.isValid() ? t.getParam(i) : -999.; +} + +float MCTrackInfo::getTrackParTPCPhiSec(float b, float x) const +{ + auto t = getTrackParTPC(b, x); + return t.isValid() ? std::atan2(t.getY(), t.getX()) : -999.; +} + +int TrackFamily::getLongestTPCTrackEntry() const +{ + int n = -1, ncl = 0; + int ntr = recTracks.size(); + for (int i = 0; i < ntr; i++) { + if (recTracks[i].nClTPC > ncl) { + ncl = recTracks[i].nClTPC; + n = i; + } + } + return n; +} + +int TrackFamily::getNTPCClones() const +{ + int n = 0; + for (auto& t : recTracks) { + if (t.nClTPC > 0) { + n++; + } + } + return n; +} + +} // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index 3a0290a9c0b0d..38079d36a2e84 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -12,9 +12,11 @@ #include #include #include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsITSMFT/TrkClusRef.h" #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" #include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsCalibration/MeanVertexObject.h" #include "DetectorsBase/Propagator.h" #include "DetectorsBase/GeometryManager.h" #include "SimulationDataFormat/MCEventLabel.h" @@ -24,17 +26,31 @@ #include "DataFormatsFT0/RecPoints.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackingStudy.h" +#include "GlobalTrackingStudy/TrackInfoExt.h" +#include "GlobalTrackingStudy/TrackMCStudyTypes.h" #include "TPCBase/ParameterElectronics.h" #include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/PrimaryVertexExt.h" +#include "DataFormatsFT0/RecPoints.h" #include "CommonUtils/TreeStreamRedirector.h" #include "ReconstructionDataFormats/VtxTrackRef.h" #include "ReconstructionDataFormats/DCA.h" +#include "TPCCalibration/VDriftHelper.h" +#include "GPUO2InterfaceRefit.h" +#include "GPUO2ExternalUser.h" // Needed for propper settings in GPUParam.h +#include "GPUParam.h" +#include "GPUParam.inc" +#include "GPUTPCGeometry.h" #include "Steer/MCKinematicsReader.h" +#include "MathUtils/fit.h" +#include "TPCFastTransformPOD.h" +#include namespace o2::trackstudy { @@ -51,7 +67,7 @@ using TBracket = o2::math_utils::Bracketf_t; using timeEst = o2::dataformats::TimeStampWithError; -class TrackingStudySpec : public Task +class TrackingStudySpec final : public Task { public: TrackingStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) @@ -65,19 +81,71 @@ class TrackingStudySpec : public Task private: void updateTimeDependentParams(ProcessingContext& pc); + float getDCAYCut(float pt) const; + float getDCAZCut(float pt) const; std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; + o2::tpc::VDriftHelper mTPCVDriftHelper{}; + const o2::gpu::TPCFastTransformPOD* mTPCCorrMaps{nullptr}; bool mUseMC{false}; ///< MC flag std::unique_ptr mDBGOut; - float mITSROFrameLengthMUS = 0.; + std::unique_ptr mDBGOutVtx; + std::unique_ptr mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction + std::vector mMltHistTB, mTBinClOccAft, mTBinClOccBef, mTBinClOccWgh; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting/preceding from the TB = i*mNTPCOccBinLength + std::unique_ptr mOccWghFun; + float mITSROFrameLengthMUS = 0.f; + float mTPCTBinMUS = 0.f; // TPC bin in microseconds + float mTPCTBinMUSInv = 0.f; + int mMaxNeighbours = 3; + float mMaxVTTimeDiff = 80.; // \mus + float mTPCDCAYCut = 2.; + float mTPCDCAZCut = 2.; + float mMinX = 46.; + float mMaxEta = 0.8; + float mMinPt = 0.1; + int mNOccBinsDrift = 10; + int mMinTPCClusters = 60; + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + int mNHBPerTF = 0; + float mNTPCOccBinLengthInv; + bool mStoreWithITSOnly = false; + bool mDoPairsCorr = false; + std::string mDCAYFormula = "0.0105 + 0.0350 / pow(x, 1.1)"; + std::string mDCAZFormula = "0.0105 + 0.0350 / pow(x, 1.1)"; GTrackID::mask_t mTracksSrc{}; + o2::dataformats::MeanVertexObject mMeanVtx{}; o2::steer::MCKinematicsReader mcReader; // reader of MC information }; void TrackingStudySpec::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mDBGOut = std::make_unique("trackStudy.root", "recreate"); + int lane = ic.services().get().inputTimesliceId; + int maxLanes = ic.services().get().maxInputTimeslices; + std::string dbgnm = maxLanes == 1 ? "trackStudy.root" : fmt::format("trackStudy_{}.root", lane); + mDBGOut = std::make_unique(dbgnm.c_str(), "recreate"); + dbgnm = maxLanes == 1 ? "trackStudyVtx.root" : fmt::format("trackStudyVtx_{}.root", lane); + mDBGOutVtx = std::make_unique(dbgnm.c_str(), "recreate"); + mStoreWithITSOnly = ic.options().get("with-its-only"); + mMaxVTTimeDiff = ic.options().get("max-vtx-timediff"); + mMaxNeighbours = ic.options().get("max-vtx-neighbours"); + mTPCDCAYCut = ic.options().get("max-tpc-dcay"); + mTPCDCAZCut = ic.options().get("max-tpc-dcaz"); + mMinX = ic.options().get("min-x-prop"); + mMaxEta = ic.options().get("max-eta"); + mMinPt = ic.options().get("min-pt"); + mMinTPCClusters = ic.options().get("min-tpc-clusters"); + mDCAYFormula = ic.options().get("dcay-vs-pt"); + mDCAZFormula = ic.options().get("dcaz-vs-pt"); + mDoPairsCorr = ic.options().get("pair-correlations"); + mNOccBinsDrift = ic.options().get("noccbins"); + if (mNOccBinsDrift < 3) { + mNOccBinsDrift = 3; + } + auto str = ic.options().get("occ-weight-fun"); + if (!str.empty()) { + mOccWghFun = std::make_unique("occFun", str.c_str(), -100., 100.); + } } void TrackingStudySpec::run(ProcessingContext& pc) @@ -85,12 +153,58 @@ void TrackingStudySpec::run(ProcessingContext& pc) o2::globaltracking::RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + if (recoData.inputsTPCclusters) { + mTPCRefitter = std::make_unique(&recoData.inputsTPCclusters->clusterIndex, mTPCCorrMaps, o2::base::Propagator::Instance()->getNominalBz(), + recoData.getTPCTracksClusterRefs().data(), 0, recoData.clusterShMapTPC.data(), recoData.occupancyMapTPC.data(), + recoData.occupancyMapTPC.size(), nullptr, o2::base::Propagator::Instance()); + mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; + mTBinClOccBef.clear(); + mTBinClOccAft.clear(); + mTBinClOccWgh.clear(); + } + + // prepare TPC occupancy data + if (mNTPCOccBinLength > 1 && recoData.occupancyMapTPC.size()) { + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + int nTPCBins = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0; + int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); + mTBinClOccAft.resize(nTPCOccBins); + mTBinClOccBef.resize(nTPCOccBins); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + mMltHistTB.resize(nTPCOccBins); + for (int i = 0; i < nTPCOccBins; i++) { + mMltHistTB[i] = mTPCRefitter->getParam()->GetUnscaledMult(tb); + tb += mNTPCOccBinLength; + } + for (int i = nTPCOccBins; i--;) { + sm += mMltHistTB[i]; + if (i + sumBins < nTPCOccBins) { + sm -= mMltHistTB[i + sumBins]; + } + mTBinClOccAft[i] = sm; + } + sm = 0; + for (int i = 0; i < nTPCOccBins; i++) { + sm += mMltHistTB[i]; + if (i - sumBins > 0) { + sm -= mMltHistTB[i - sumBins]; + } + mTBinClOccBef[i] = sm; + } + } else { + mTBinClOccBef.resize(1); + mTBinClOccAft.resize(1); + } + process(recoData); } void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); + mTPCVDriftHelper.extractCCDBInputs(pc); + auto const& raw = pc.inputs().get("corrMap"); + mTPCCorrMaps = &o2::gpu::TPCFastTransformPOD::get(raw); static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; @@ -102,6 +216,11 @@ void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) } else { mITSROFrameLengthMUS = alpParams.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; // ITS ROFrame duration in \mus } + pc.inputs().get("meanvtx"); + mNHBPerTF = o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + auto& elParam = o2::tpc::ParameterElectronics::Instance(); + mTPCTBinMUS = elParam.ZbinWidth; // TPC bin in microseconds + mTPCTBinMUSInv = 1. / mTPCTBinMUS; } } @@ -111,36 +230,475 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs auto prop = o2::base::Propagator::Instance(); + auto FITInfo = recoData.getFT0RecPoints(); + static int TFCount = 0; + int nv = vtxRefs.size(); + o2::dataformats::PrimaryVertexExt pveDummy; + o2::dataformats::PrimaryVertexExt vtxDummy(mMeanVtx.getPos(), {}, {}, 0); + std::vector pveVec(nv); + std::vector tpcOccAftV, tpcOccBefV; + pveVec.back() = vtxDummy; + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); + float tBiasITS = alpParams.roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingMUS; + const o2::ft0::InteractionTag& ft0Params = o2::ft0::InteractionTag::Instance(); + std::vector trcExtVec; + std::vector trcPairsVec; + auto vdrift = mTPCVDriftHelper.getVDriftObject().getVDrift(); + float maxDriftTB = 250.f / vdrift / (o2::constants::lhc::LHCBunchSpacingMUS * 8); + int groupOcc = std::ceil(maxDriftTB / mNOccBinsDrift / mNTPCOccBinLength); + + bool tpcTrackOK = recoData.isTrackSourceLoaded(GTrackID::TPC); + + auto fillTPCClInfo = [&recoData, this](const o2::tpc::TrackTPC& trc, o2::dataformats::TrackInfoExt& trExt, float timestampTB = -1e9) { + const auto clRefs = recoData.getTPCTracksClusterRefs(); + const auto tpcClusAcc = recoData.getTPCClusters(); + const auto shMap = recoData.clusterShMapTPC; + + if (recoData.inputsTPCclusters) { + uint8_t clSect = 0, clRow = 0, lowestR = -1; + uint32_t clIdx = 0; + for (int ic = 0; ic < trc.getNClusterReferences(); ic++) { // outside -> inside ordering, but on the sector boundaries backward jumps are possible + trc.getClusterReference(clRefs, ic, clSect, clRow, clIdx); + if (clRow < lowestR) { + trExt.rowCountTPC++; + lowestR = clRow; + } + unsigned int absoluteIndex = tpcClusAcc.clusterOffset[clSect][clRow] + clIdx; + if (shMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { + trExt.nClTPCShared++; + } + } + trExt.rowMinTPC = lowestR; + const auto& clus = tpcClusAcc.clusters[clSect][clRow][clIdx]; + trExt.padFromEdge = uint8_t(clus.getPad()); + int npads = o2::gpu::GPUTPCGeometry::NPads(lowestR); + if (trExt.padFromEdge > npads / 2) { + trExt.padFromEdge = npads - 1 - trExt.padFromEdge; + } + this->mTPCCorrMaps->Transform(clSect, clRow, clus.getPad(), clus.getTime(), trExt.innerTPCPos0[0], trExt.innerTPCPos0[1], trExt.innerTPCPos0[2], trc.getTime0()); // nominal time of the track + if (timestampTB > -1e8) { + this->mTPCCorrMaps->Transform(clSect, clRow, clus.getPad(), clus.getTime(), trExt.innerTPCPos[0], trExt.innerTPCPos[1], trExt.innerTPCPos[2], timestampTB); // time assigned from the global track track + } else { + trExt.innerTPCPos = trExt.innerTPCPos0; + } + trc.getClusterReference(clRefs, 0, clSect, clRow, clIdx); + trExt.rowMaxTPC = clRow; + } + }; + + auto getTPCPairSharing = [&recoData, this](const o2::tpc::TrackTPC& trc0, const o2::tpc::TrackTPC& trc1) { + const auto clRefs = recoData.getTPCTracksClusterRefs(); + uint8_t nsh = 0, nshRows = 0, lastSharedRow = -1; + if (recoData.inputsTPCclusters) { + uint8_t clSect0 = 0, clRow0 = 0, clSect1 = 0, clRow1 = 0; + uint32_t clIdx0 = 0, clIdx1 = 0; + int ic1Start = 0; + for (int ic0 = 0; ic0 < trc0.getNClusterReferences(); ic0++) { // outside -> inside, but on the sector boundaries backward jumps are possible + trc0.getClusterReference(clRefs, ic0, clSect0, clRow0, clIdx0); + for (int ic1 = ic1Start; ic1 < trc1.getNClusterReferences(); ic1++) { // outside -> inside, but on the sector boundaries backward jumps are possible + trc1.getClusterReference(clRefs, ic1, clSect1, clRow1, clIdx1); + if (clRow1 > clRow0) { + ic1Start = ic1 + 1; + continue; // catch up ic0 + } + if (clRow1 == clRow0) { + if (clSect0 == clSect1 && clIdx0 == clIdx1) { + nsh++; + if (lastSharedRow != clRow0) { + lastSharedRow = clRow0; + nshRows++; + } + ic1Start = ic1 + 1; + break; // check next ic0 + } + } + } + } + } + return std::make_pair(nsh, nshRows); + }; + + auto assignRecTrack = [&recoData, this](const o2::dataformats::TrackInfoExt& src, o2::trackstudy::RecTrack& dst) { + dst.track = src.track; + dst.gid = src.gid; + dst.ts.setTimeStamp(src.ttime); + dst.ts.setTimeStampError(src.ttimeE); + dst.nClITS = src.nClITS; + dst.nClTPC = src.nClTPC; + dst.pattITS = src.pattITS; + if (src.q2ptITS == 0. && dst.nClITS > 0) { + dst.pattITS |= 0x1 << 7; + } + dst.lowestPadRow = src.rowMinTPC; + if (this->mUseMC) { + auto gidSet = recoData.getSingleDetectorRefs(src.gid); + if (recoData.getTrackMCLabel(src.gid).isFake()) { + dst.flags |= RecTrack::FakeGLO; + } + auto msk = src.gid.getSourceDetectorsMask(); + if (msk[DetID::ITS]) { + if (gidSet[GTrackID::ITS].isSourceSet()) { // has ITS track rather than AB tracklet + auto lblITS = recoData.getTrackMCLabel(gidSet[GTrackID::ITS]); + if (lblITS.isFake()) { + dst.flags |= RecTrack::FakeITS; + } + } else { // AB ITS tracklet + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSAB]).isFake()) { + dst.flags |= RecTrack::FakeITS; + } + } + if (msk[DetID::TPC]) { // has both ITS and TPC contribution + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPC]).isFake()) { + dst.flags |= RecTrack::FakeITSTPC; + } + } + } + if (msk[DetID::TPC]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPC]).isFake()) { + dst.flags |= RecTrack::FakeTPC; + } + } + } + }; + tpcOccAftV.resize(mNOccBinsDrift); + tpcOccBefV.resize(mNOccBinsDrift); - int nv = vtxRefs.size() - 1; for (int iv = 0; iv < nv; iv++) { + LOGP(debug, "processing PV {} of {}", iv, nv); const auto& vtref = vtxRefs[iv]; - auto pv = pvvec[iv]; - for (int is = 0; is < GTrackID::NSources; is++) { - if (!GTrackID::getSourceDetectorsMask(is)[GTrackID::ITS]) { - continue; + if (iv != nv - 1) { + auto& pve = pveVec[iv]; + static_cast(pve) = pvvec[iv]; + if (mUseMC) { + pve.mcLb = recoData.getPrimaryVertexMCLabel(iv); + } + // find best matching FT0 signal + float bestTimeDiff = 1000, bestTime = -999; + int bestFTID = -1; + if (mTracksSrc[GTrackID::FT0]) { + for (int ift0 = vtref.getFirstEntryOfSource(GTrackID::FT0); ift0 < vtref.getFirstEntryOfSource(GTrackID::FT0) + vtref.getEntriesOfSource(GTrackID::FT0); ift0++) { + const auto& ft0 = FITInfo[trackIndex[ift0]]; + if (ft0Params.isSelected(ft0)) { + auto fitTime = ft0.getInteractionRecord().differenceInBCMUS(recoData.startIR); + if (std::abs(fitTime - pve.getTimeStamp().getTimeStamp()) < bestTimeDiff) { + bestTimeDiff = fitTime - pve.getTimeStamp().getTimeStamp(); + bestFTID = trackIndex[ift0]; + } + } + } + } else { + LOGP(warn, "FT0 is not requested, cannot set complete vertex info"); + } + if (bestFTID >= 0) { + pve.FT0A = FITInfo[bestFTID].getTrigger().getAmplA(); + pve.FT0C = FITInfo[bestFTID].getTrigger().getAmplC(); + pve.FT0Time = double(FITInfo[bestFTID].getInteractionRecord().differenceInBCMUS(recoData.startIR)) + FITInfo[bestFTID].getCollisionTimeMean() * 1e-6; // time in \mus } - int idMin = vtxRefs[iv].getFirstEntryOfSource(is), idMax = idMin + vtxRefs[iv].getEntriesOfSource(is); + pve.VtxID = iv; + } + trcExtVec.clear(); + trcPairsVec.clear(); + float q2ptITS, q2ptTPC, q2ptITSTPC, q2ptITSTPCTRD; + for (int is = 0; is < GTrackID::NSources; is++) { + DetID::mask_t dm = GTrackID::getSourceDetectorsMask(is); + bool skipTracks = !mTracksSrc[is] || !recoData.isTrackSourceLoaded(is) || !(dm[DetID::ITS] || dm[DetID::TPC]); + int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is); for (int i = idMin; i < idMax; i++) { auto vid = trackIndex[i]; bool pvCont = vid.isPVContributor(); + if (pvCont) { + pveVec[iv].nSrc[is]++; + } + if (skipTracks) { + continue; + } + GTrackID tpcTrID; + const o2::tpc::TrackTPC* tpcTr = nullptr; + int nclTPC = 0; + if (dm[DetID::TPC] && tpcTrackOK) { + tpcTrID = recoData.getTPCContributorGID(vid); + tpcTr = &recoData.getTPCTrack(tpcTrID); + nclTPC = tpcTr->getNClusters(); + if (nclTPC < mMinTPCClusters) { + continue; + } + } bool ambig = vid.isAmbiguous(); auto trc = recoData.getTrackParam(vid); + if (fabs(trc.getEta()) > mMaxEta) { + continue; + } + if (iv < nv - 1 && is == GTrackID::TPC && tpcTr && !tpcTr->hasBothSidesClusters()) { // for unconstrained TPC tracks correct track Z + float corz = vdrift * (tpcTr->getTime0() * mTPCTBinMUS - pvvec[iv].getTimeStamp().getTimeStamp()); + if (tpcTr->hasASideClustersOnly()) { + corz = -corz; // A-side + } + trc.setZ(trc.getZ() + corz); + } float xmin = trc.getX(); o2::dataformats::DCA dca; - if (!prop->propagateToDCA(pv, trc, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &dca)) { + if (!prop->propagateToDCA(iv == nv - 1 ? vtxDummy : pvvec[iv], trc, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &dca)) { + continue; + } + bool hasITS = GTrackID::getSourceDetectorsMask(is)[GTrackID::ITS]; + if (std::abs(dca.getY()) > (hasITS ? getDCAYCut(trc.getPt()) : mTPCDCAYCut) || + std::abs(dca.getZ()) > (hasITS ? getDCAZCut(trc.getPt()) : mTPCDCAZCut)) { continue; } - (*mDBGOut) << "dca" - << "gid=" << vid << "pv=" << pv << "trc=" << trc << "pvCont=" << pvCont << "ambig=" << ambig << "dca=" << dca << "xmin=" << xmin << "\n"; + if (trc.getPt() < mMinPt) { + continue; + } + if (iv != nv - 1) { + pveVec[iv].nSrcA[is]++; + if (ambig) { + pveVec[iv].nSrcAU[is]++; + } + } + if (!hasITS && mStoreWithITSOnly) { + continue; + } + { + auto& trcExt = trcExtVec.emplace_back(); + recoData.getTrackTime(vid, trcExt.ttime, trcExt.ttimeE); + trcExt.track = trc; + trcExt.hashIU = trc.hash(); + trcExt.dca = dca; + trcExt.gid = vid; + trcExt.xmin = xmin; + trcExt.dcaTPC.set(-999.f, -999.f); + + if (tpcTr) { + float tsuse = trcExt.ttime / (8 * o2::constants::lhc::LHCBunchSpacingMUS); + if (tpcTr->hasASideClusters()) { + trcExt.setTPCA(); + } + if (tpcTr->hasCSideClusters()) { + trcExt.setTPCC(); + } + if (is == GTrackID::TPC) { + trcExt.dcaTPC = dca; + tsuse = -1e9; + } else { + o2::track::TrackParCov tmpTPC(*tpcTr); + if (iv < nv - 1 && is == GTrackID::TPC && tpcTr && !tpcTr->hasBothSidesClusters()) { // for unconstrained TPC tracks correct track Z + float corz = vdrift * (tpcTr->getTime0() * mTPCTBinMUS - pvvec[iv].getTimeStamp().getTimeStamp()); + if (tpcTr->hasASideClustersOnly()) { + corz = -corz; // A-side + } + tmpTPC.setZ(tmpTPC.getZ() + corz); + } + if (!prop->propagateToDCA(iv == nv - 1 ? vtxDummy : pvvec[iv], tmpTPC, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &trcExt.dcaTPC)) { + trcExt.dcaTPC.set(-999.f, -999.f); + } + } + fillTPCClInfo(*tpcTr, trcExt, tsuse); + trcExt.chi2TPC = tpcTr->getChi2(); + } + auto gidRefs = recoData.getSingleDetectorRefs(vid); + if (gidRefs[GTrackID::ITS].isIndexSet()) { + const auto& itsTr = recoData.getITSTrack(gidRefs[GTrackID::ITS]); + trcExt.q2ptITS = itsTr.getQ2Pt(); + trcExt.nClITS = itsTr.getNClusters(); + for (int il = 0; il < 7; il++) { + if (itsTr.hasHitOnLayer(il)) { + trcExt.pattITS |= 0x1 << il; + } + } + } else if (gidRefs[GTrackID::ITSAB].isIndexSet()) { + const auto& itsTrf = recoData.getITSABRefs()[gidRefs[GTrackID::ITSAB]]; + trcExt.nClITS = itsTrf.getNClusters(); + for (int il = 0; il < 7; il++) { + if (itsTrf.hasHitOnLayer(il)) { + trcExt.pattITS |= 0x1 << il; + } + } + } + if (gidRefs[GTrackID::TPC].isIndexSet()) { + trcExt.q2ptTPC = recoData.getTrackParam(gidRefs[GTrackID::TPC]).getQ2Pt(); + trcExt.nClTPC = nclTPC; + } + if (gidRefs[GTrackID::ITSTPC].isIndexSet()) { + const auto& trTPCITS = recoData.getTPCITSTrack(gidRefs[GTrackID::ITSTPC]); + trcExt.q2ptITSTPC = trTPCITS.getQ2Pt(); + trcExt.chi2ITSTPC = trTPCITS.getChi2Match(); + } + if (gidRefs[GTrackID::TRD].isIndexSet()) { + trcExt.q2ptITSTPCTRD = recoData.getTrackParam(gidRefs[GTrackID::TRD]).getQ2Pt(); + } + if (gidRefs[GTrackID::TOF].isIndexSet()) { + trcExt.infoTOF = recoData.getTOFMatch(vid); + } + } + } + } + float tpcOccBef = 0., tpcOccAft = 0.; + if (iv != nv - 1) { + int tb = pveVec[iv].getTimeStamp().getTimeStamp() * mTPCTBinMUSInv * mNTPCOccBinLengthInv; + tpcOccBef = tb < 0 ? mTBinClOccBef[0] : (tb >= mTBinClOccBef.size() ? mTBinClOccBef.back() : mTBinClOccBef[tb]); + tpcOccAft = tb < 0 ? mTBinClOccAft[0] : (tb >= mTBinClOccAft.size() ? mTBinClOccAft.back() : mTBinClOccAft[tb]); + int tbc = pveVec[iv].getTimeStamp().getTimeStamp() * mTPCTBinMUSInv * mNTPCOccBinLengthInv - groupOcc / 2.; + for (int iob = 0; iob < mNOccBinsDrift; iob++) { + float sm = 0; + for (int ig = 0; ig < groupOcc; ig++) { + int ocb = tbc + ig + groupOcc * iob; + if (ocb < 0 || ocb >= (int)mMltHistTB.size()) { + sm = -1; + break; + } + sm += mMltHistTB[ocb]; + } + tpcOccAftV[iob] = sm; + // + sm = 0; + for (int ig = 0; ig < groupOcc; ig++) { + int ocb = tbc + ig - groupOcc * iob; + if (ocb < 0 || ocb >= (int)mMltHistTB.size()) { + sm = -1; + break; + } + sm += mMltHistTB[ocb]; + } + tpcOccBefV[iob] = sm; + } + } + (*mDBGOut) << "trpv" + << "orbit=" << recoData.startIR.orbit << "tfID=" << TFCount + << "tpcOccBef=" << tpcOccBef << "tpcOccAft=" << tpcOccAft + << "tpcOccBefV=" << tpcOccBefV << "tpcOccAftV=" << tpcOccAftV + << "pve=" << pveVec[iv] << "trc=" << trcExtVec << "\n"; + + if (mDoPairsCorr) { + for (int it0 = 0; it0 < (int)trcExtVec.size(); it0++) { + const auto& tr0 = trcExtVec[it0]; + if (tr0.nClTPC < 1) { + continue; + } + for (int it1 = it0 + 1; it1 < (int)trcExtVec.size(); it1++) { + const auto& tr1 = trcExtVec[it1]; + if (tr1.nClTPC < 1) { + continue; + } + + if (std::abs(tr0.track.getTgl() - tr1.track.getTgl()) > 0.25) { + continue; + } + auto dphi = tr0.track.getPhi() - tr1.track.getPhi(); + if (dphi < -o2::constants::math::PI) { + dphi += o2::constants::math::TwoPI; + } else if (dphi > o2::constants::math::PI) { + dphi -= o2::constants::math::TwoPI; + } + if (std::abs(dphi) > 0.25) { + continue; + } + auto& pr = trcPairsVec.emplace_back(); + assignRecTrack(tr0, pr.tr0); + assignRecTrack(tr1, pr.tr1); + auto shinfo = getTPCPairSharing(recoData.getTPCTrack(recoData.getTPCContributorGID(tr0.gid)), recoData.getTPCTrack(recoData.getTPCContributorGID(tr1.gid))); + pr.nshTPC = shinfo.first; + pr.nshTPCRow = shinfo.second; + } + } + (*mDBGOut) << "pairs" << "pr=" << trcPairsVec << "\n"; + } + } + + int nvtot = mMaxNeighbours < 0 ? -1 : (int)pveVec.size(); + + auto insSlot = [maxSlots = mMaxNeighbours](std::vector& vc, float v, int slot, std::vector& vid, int id) { + for (int i = maxSlots - 1; i > slot; i--) { + std::swap(vc[i], vc[i - 1]); + std::swap(vid[i], vid[i - 1]); + } + vc[slot] = v; + vid[slot] = id; + }; + + std::vector pveT; // neighbours in time + std::vector pveZ; // neighbours in Z + std::vector idT(mMaxNeighbours), idZ(mMaxNeighbours); + std::vector dT(mMaxNeighbours), dZ(mMaxNeighbours); + for (int cnt = 0; cnt < nvtot; cnt++) { + pveT.clear(); // neighbours in time + pveZ.clear(); // neighbours in Z + + const auto& pve = pveVec[cnt]; + float tv = pve.getTimeStamp().getTimeStamp(); + for (int i = 0; i < mMaxNeighbours; i++) { + idT[i] = idZ[i] = -1; + dT[i] = mMaxVTTimeDiff; + dZ[i] = 1e9; + } + int cntM = cnt - 1, cntP = cnt + 1; + for (; cntM >= 0; cntM--) { // backward + const auto& vt = pveVec[cntM]; + auto dtime = std::abs(tv - vt.getTimeStamp().getTimeStamp()); + if (dtime > mMaxVTTimeDiff) { + continue; + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (dT[i] > dtime) { + insSlot(dT, dtime, i, idT, cntM); + break; + } + } + auto dz = std::abs(pve.getZ() - vt.getZ()); + for (int i = 0; i < mMaxNeighbours; i++) { + if (dZ[i] > dz) { + insSlot(dZ, dz, i, idZ, cntM); + break; + } + } + } + for (; cntP < nvtot; cntP++) { // forward + const auto& vt = pveVec[cntP]; + auto dtime = std::abs(tv - vt.getTimeStamp().getTimeStamp()); + if (dtime > mMaxVTTimeDiff) { + continue; + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (dT[i] > dtime) { + insSlot(dT, dtime, i, idT, cntP); + break; + } + } + auto dz = std::abs(pve.getZ() - vt.getZ()); + for (int i = 0; i < mMaxNeighbours; i++) { + if (dZ[i] > dz) { + insSlot(dZ, dz, i, idZ, cntP); + break; + } + } + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (idT[i] != -1) { + pveT.push_back(pveVec[idT[i]]); + } else { + break; + } + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (idZ[i] != -1) { + pveZ.push_back(pveVec[idZ[i]]); + } else { + break; } } + (*mDBGOutVtx) << "pvExt" + << "pve=" << pve + << "pveT=" << pveT + << "pveZ=" << pveZ + << "tfID=" << TFCount + << "\n"; } + + TFCount++; } void TrackingStudySpec::endOfStream(EndOfStreamContext& ec) { mDBGOut.reset(); + mDBGOutVtx.reset(); } void TrackingStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -148,6 +706,26 @@ void TrackingStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { return; } + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mMeanVtx = *(const o2::dataformats::MeanVertexObject*)obj; + return; + } +} + +float TrackingStudySpec::getDCAYCut(float pt) const +{ + static TF1 fun("dcayvspt", mDCAYFormula.c_str(), 0, 20); + return fun.Eval(pt); +} + +float TrackingStudySpec::getDCAZCut(float pt) const +{ + static TF1 fun("dcazvspt", mDCAZFormula.c_str(), 0, 20); + return fun.Eval(pt); } DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) @@ -157,7 +735,8 @@ DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas dataRequest->requestTracks(srcTracks, useMC); dataRequest->requestClusters(srcClusters, useMC); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true true, // GRPLHCIF @@ -167,12 +746,31 @@ DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas dataRequest->inputs, true); + Options opts{ + {"max-vtx-neighbours", VariantType::Int, 3, {"Max PV neighbours fill, no PV study if < 0"}}, + {"max-vtx-timediff", VariantType::Float, 90.f, {"Max PV time difference to consider"}}, + {"dcay-vs-pt", VariantType::String, "0.0105 + 0.0350 / pow(x, 1.1)", {"Formula for global tracks DCAy vs pT cut"}}, + {"dcaz-vs-pt", VariantType::String, "0.0105 + 0.0350 / pow(x, 1.1)", {"Formula for global tracks DCAy vs pT cut"}}, + {"min-tpc-clusters", VariantType::Int, 60, {"Cut on TPC clusters"}}, + {"max-tpc-dcay", VariantType::Float, 5.f, {"Cut on TPC dcaY"}}, + {"max-tpc-dcaz", VariantType::Float, 5.f, {"Cut on TPC dcaZ"}}, + {"max-eta", VariantType::Float, 1.0f, {"Cut on track eta"}}, + {"min-pt", VariantType::Float, 0.1f, {"Cut on track pT"}}, + {"with-its-only", VariantType::Bool, false, {"Store tracks with ITS only"}}, + {"pair-correlations", VariantType::Bool, false, {"Do pairs correlation"}}, + {"occ-weight-fun", VariantType::String, "(x>=-40&&x<-5) ? (1./1225*pow(x+40,2)) : ((x>-5&&x<15) ? 1. : ((x>=15&&x<40) ? (-0.4/25*x+1.24 ) : ( (x>40&&x<100) ? -0.4/60*x+0.6+0.8/3 : 0)))", {"Occupancy weighting f-n vs time in musec"}}, + {"noccbins", VariantType::Int, 10, {"Number of occupancy bins per full drift time"}}, + {"min-x-prop", VariantType::Float, 100.f, {"track should be propagated to this X at least"}}, + }; + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + dataRequest->inputs.emplace_back("corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe); + return DataProcessorSpec{ "track-study", dataRequest->inputs, outputs, AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, - Options{}}; + opts}; } } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/V0Ext.cxx b/Detectors/GlobalTrackingWorkflow/study/src/V0Ext.cxx new file mode 100644 index 0000000000000..0ae0fd3bda96a --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/V0Ext.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// class for extended V0 info (for debugging) + +#include "GlobalTrackingStudy/V0Ext.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx new file mode 100644 index 0000000000000..0791d72474ad3 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx @@ -0,0 +1,86 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/CheckResidSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"draw-external-only", VariantType::Bool, false, {"just draw content of comma-separated list of histomanagers from checkresid.ext_hm_list"}}, + {"postproc-external-only", VariantType::Bool, false, {"just post-process raw content of comma-separated list of histomanagers from checkresid.ext_hm_list"}}, + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, "ITS", {"comma-separated list of cluster sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + bool drawOnly = configcontext.options().get("draw-external-only"); + bool postProcOnly = configcontext.options().get("postproc-external-only"); + GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,TPC,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t allowedSourcesClus = GID::getSourcesMask("ITS"); + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); + + if (!drawOnly) { + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, false); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, false); // P-vertex is always needed + } else { + allowedSourcesTrc = {}; + allowedSourcesClus = {}; + } + specs.emplace_back(o2::checkresid::getCheckResidSpec(srcTrc, srcCls, drawOnly, postProcOnly)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + if (!drawOnly) { + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + } + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx new file mode 100644 index 0000000000000..5d89ba9b7eabf --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx @@ -0,0 +1,74 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/ITSOffsStudy.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"track-sources", VariantType::String, std::string{"ITS,ITS-TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC,ITS-TPC-TRD"}, {"comma-separated list of track sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); + o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root"); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,ITS-TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC,ITS-TPC-TRD"); + GID::mask_t allowedSourcesClus = GID::getSourcesMask("TOF"); + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + srcTrc |= GID::getSourcesMask("ITS"); + GID::mask_t srcCls = allowedSourcesClus; + + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, false); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, false); // P-vertex is always needed + specs.emplace_back(o2::trackstudy::getITSOffsStudy(srcTrc, srcCls)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx new file mode 100644 index 0000000000000..7e104b82f4854 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx @@ -0,0 +1,78 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/SVStudy.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"ignore-tpc-occ", VariantType::Bool, false, {"do not fill TPC occupancy (needs TPC clusters)"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,TPC,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + auto useMC = !configcontext.options().get("disable-mc"); + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + GID::mask_t srcCls{}; + bool fillTPCOcc = !configcontext.options().get("ignore-tpc-occ"); + if (fillTPCOcc) { + srcCls = srcCls | GID::getSourcesMask("TPC"); + } + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed + o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); // S-vertex is always needed + specs.emplace_back(o2::svstudy::getSVStudySpec(srcTrc, srcCls, useMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tpc-data-filter-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tpc-data-filter-workflow.cxx new file mode 100644 index 0000000000000..53fc434afe0a2 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/tpc-data-filter-workflow.cxx @@ -0,0 +1,75 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/TPCDataFilter.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +using namespace o2::framework; + +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of cluster sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + GID::mask_t allowedSourcesTrc = GID::getSourcesMask("TPC,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t allowedSourcesClus = GID::getSourcesMask("TPC"); + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + auto useMC = !configcontext.options().get("disable-mc"); + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); + specs.emplace_back(o2::global::getTPCDataFilter(srcTrc, srcCls, useMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx index f8ecc6883462b..e255295d7665f 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx @@ -20,6 +20,8 @@ #include "DetectorsBase/DPLWorkflowUtils.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "DetectorsRaw/HBFUtilsInitializer.h" +#include "TPCCalibration/CorrectionMapsOptions.h" +#include "TPCWorkflow/TPCScalerSpec.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -41,6 +43,7 @@ void customize(std::vector& workflowOptions) {"cluster-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of cluster sources to use"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::tpc::CorrectionMapsOptions::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -58,17 +61,19 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - auto useMC = !configcontext.options().get("disable-mc"); - if (!useMC) { - LOG(info) << "this task requires MC info, enforcing it"; - useMC = true; - } + auto sclOpt = o2::tpc::CorrectionMapsOptions::parseGlobalOptions(configcontext.options()); GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); - + if (sclOpt.requestCTPLumi) { + srcTrc = srcTrc | GID::getSourcesMask("CTP"); + srcCls = srcCls | GID::getSourcesMask("CTP"); + } o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); + } specs.emplace_back(o2::trackstudy::getTPCTrackStudySpec(srcTrc, srcCls, useMC)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit diff --git a/Detectors/GlobalTrackingWorkflow/study/src/track-dump-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/track-dump-workflow.cxx new file mode 100644 index 0000000000000..fd8715c10891f --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/track-dump-workflow.cxx @@ -0,0 +1,74 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/DumpTracks.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of cluster sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root"); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,MFT,TPC,MCH,MCH-MID,MFT-MCH,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); //,EMC,PHS,CPV,FT0,FV0,FDD"); + GID::mask_t allowedSourcesClus = GID::getSourcesMask(""); + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + bool useMC = false; + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); + + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed + specs.emplace_back(o2::trackstudy::getDumpTracksSpec(srcTrc, srcCls, useMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx new file mode 100644 index 0000000000000..50cc768bdc98d --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx @@ -0,0 +1,94 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/TrackMCStudy.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "TPCCalibration/CorrectionMapsOptions.h" +#include "TPCWorkflow/TPCScalerSpec.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of cluster sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"ignore-sv-check", VariantType::Bool, false, {"disable check for SV combinatorics"}}, + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation, never use it"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); + o2::tpc::CorrectionMapsOptions::addGlobalOptions(options); + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + auto useMC = !configcontext.options().get("disable-mc"); + auto checkSV = !configcontext.options().get("ignore-sv-check"); + if (!useMC) { + throw std::runtime_error("MC cannot be disabled for this workflow"); + } + auto sclOpt = o2::tpc::CorrectionMapsOptions::parseGlobalOptions(configcontext.options()); + GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,TPC,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t allowedSourcesClus = GID::getSourcesMask("ITS,TPC"); + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); + srcCls |= GID::getSourcesMask("ITS,TPC"); + if (sclOpt.requestCTPLumi) { + srcTrc = srcTrc | GID::getSourcesMask("CTP"); + srcCls = srcCls | GID::getSourcesMask("CTP"); + } + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, true); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, true); // P-vertex is always needed + if (checkSV) { + o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); + } + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); + } + + specs.emplace_back(o2::trackstudy::getTrackMCStudySpec(srcTrc, srcCls, checkSV)); + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx index 2e9e67ea7251b..fa69d9f2808e0 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx @@ -20,6 +20,9 @@ #include "DetectorsBase/DPLWorkflowUtils.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "DetectorsRaw/HBFUtilsInitializer.h" +#include "TPCCalibration/CorrectionMapsOptions.h" +#include "TPCWorkflow/TPCScalerSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -38,9 +41,11 @@ void customize(std::vector& workflowOptions) std::vector options{ {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, - {"cluster-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of cluster sources to use"}}, + {"cluster-sources", VariantType::String, "TPC,TOF", {"comma-separated list of cluster sources to use"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); + o2::tpc::CorrectionMapsOptions::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -58,14 +63,20 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - + auto sclOpt = o2::tpc::CorrectionMapsOptions::parseGlobalOptions(configcontext.options()); auto useMC = !configcontext.options().get("disable-mc"); GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); - + if (sclOpt.requestCTPLumi) { + srcCls = srcCls | GID::getSourcesMask("CTP"); + } + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); + } o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed + specs.emplace_back(o2::trackstudy::getTrackingStudySpec(srcTrc, srcCls, useMC)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx deleted file mode 100644 index 18b26355ff5ec..0000000000000 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TOFWorkflow/RecoWorkflowSpec.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/DataRefUtils.h" -#include "Framework/Lifetime.h" -#include "Framework/Task.h" -#include "Framework/SerializationMethods.h" -#include "Headers/DataHeader.h" -#include "DataFormatsTOF/Cluster.h" -#include "GlobalTracking/MatchTOF.h" -#include "ReconstructionDataFormats/TrackTPCITS.h" -#include "DetectorsBase/GeometryManager.h" -#include "DetectorsBase/Propagator.h" -#include "DetectorsBase/GRPGeomHelper.h" -#include "CommonUtils/NameConf.h" -#include -#include "TStopwatch.h" -#include "TPCCalibration/VDriftHelper.h" - -// from FIT -#include "DataFormatsFT0/RecPoints.h" - -#include // for make_shared, make_unique, unique_ptr -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace tof -{ - -// use the tasking system of DPL -// just need to implement 2 special methods init + run (there is no need to inherit from anything) -class TOFDPLRecoWorkflowTask -{ - using evIdx = o2::dataformats::EvIndex; - using MatchOutputType = std::vector; - - bool mUseMC = true; - bool mUseFIT = false; - - public: - explicit TOFDPLRecoWorkflowTask(std::shared_ptr gr, bool useMC, bool useFIT) : mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT) {} - - void init(framework::InitContext& ic) - { - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mTimer.Stop(); - mTimer.Reset(); - } - - void run(framework::ProcessingContext& pc) - { - mTimer.Start(false); - updateTimeDependentParams(pc); - //>>>---------- attach input data --------------->>> - const auto clustersRO = pc.inputs().get>("tofcluster"); - const auto tracksRO = pc.inputs().get>("globaltrack"); - - if (mUseFIT) { - // Note: the particular variable will go out of scope, but the span is passed by copy to the - // worker and the underlying memory is valid throughout the whole computation - auto recPoints = std::move(pc.inputs().get>("fitrecpoints")); - mMatcher.setFITRecPoints(recPoints); - LOG(info) << "TOF Reco Workflow pulled " << recPoints.size() << " FIT RecPoints"; - } - - // we do a copy of the input but we are looking for a way to avoid it (current problem in conversion form unique_ptr to *) - - gsl::span itstpclab; - o2::dataformats::MCTruthContainer toflab; - if (mUseMC) { - const auto toflabel = pc.inputs().get*>("tofclusterlabel"); - itstpclab = pc.inputs().get>("itstpclabel"); - toflab = std::move(*toflabel); - } - - mMatcher.run(tracksRO, clustersRO, toflab, itstpclab); - - // in run_match_tof aggiugnere esplicitamente la chiamata a fill del tree (nella classe MatchTOF) e il metodo per leggere i vettori di output - - //... - // LOG(info) << "TOF CLUSTERER : TRANSFORMED " << digits->size() - // << " DIGITS TO " << mClustersArray.size() << " CLUSTERS"; - - // send matching-info - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPC", 0, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector()); - if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMATCHTOF", 0, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector()); - } - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe}, mMatcher.getCalibVector()); - mTimer.Stop(); - } - - void endOfStream(EndOfStreamContext& ec) - { - LOGF(info, "TOF Matching total timing: Cpu: %.3e Real: %.3e s in %d slots", - mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); - } - - void updateTimeDependentParams(ProcessingContext& pc) - { - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - mTPCVDriftHelper.extractCCDBInputs(pc); - static bool initOnceDone = false; - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - // put here init-once stuff - } - // we may have other params which need to be queried regularly - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getSourceName()); - mMatcher.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); - mTPCVDriftHelper.acknowledgeUpdate(); - } - } - - void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) - { - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { - return; - } - } - - private: - o2::globaltracking::MatchTOF mMatcher; ///< Cluster finder - std::shared_ptr mGGCCDBRequest; - o2::tpc::VDriftHelper mTPCVDriftHelper{}; - TStopwatch mTimer; -}; - -o2::framework::DataProcessorSpec getTOFRecoWorkflowSpec(bool useMC, bool useFIT) -{ - std::vector inputs; - std::vector outputs; - inputs.emplace_back("tofcluster", o2::header::gDataOriginTOF, "CLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("globaltrack", "GLO", "TPCITS", 0, Lifetime::Timeframe); - if (useMC) { - inputs.emplace_back("tofclusterlabel", o2::header::gDataOriginTOF, "CLUSTERSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("itstpclabel", "GLO", "TPCITS_MC", 0, Lifetime::Timeframe); - } - - if (useFIT) { - inputs.emplace_back("fitrecpoints", o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); - } - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - o2::base::GRPGeomRequest::Aligned, // geometry - inputs, - true); - o2::tpc::VDriftHelper::requestCCDBInputs(inputs); - - outputs.emplace_back(o2::header::gDataOriginTOF, "MTC_ITSTPC", 0, Lifetime::Timeframe); - if (useMC) { - outputs.emplace_back(o2::header::gDataOriginTOF, "MCMATCHTOF", 0, Lifetime::Timeframe); - } - outputs.emplace_back(o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe); - - return DataProcessorSpec{ - "TOFRecoWorkflow", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC, useFIT)}, - Options{ - {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}}}; -} - -} // end namespace tof -} // end namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx index 14186497fc77a..27a48d93dac8b 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx @@ -74,8 +74,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) auto listname = cfgc.options().get("collection-infile"); auto toftpc = cfgc.options().get("tpc-matches"); - char* stringTBP = new char[listname.size()]; - sprintf(stringTBP, "%s", listname.c_str()); + char* stringTBP = new char[listname.size() + 1]; + snprintf(stringTBP, listname.size() + 1, "%s", listname.c_str()); // the lane configuration defines the subspecification ids to be distributed among the lanes. // auto tofSectors = o2::RangeTokenizer::tokenize(cfgc.options().get("tof-sectors")); diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx index a25788ddeddc0..7c8e41a92f931 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx @@ -32,6 +32,7 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" #include "Framework/CompletionPolicyHelpers.h" +#include "TOFWorkflowIO/TOFCalibWriterSpec.h" #include #include @@ -59,6 +60,7 @@ void customize(std::vector& workflowOptions) {"tof-sectors", o2::framework::VariantType::String, "0-17", {"TOF sector range, e.g. 5-7,8,9 ,TBI"}}, {"tof-lanes", o2::framework::VariantType::Int, 1, {"number of parallel lanes up to the matcher, TBI"}}, {"use-ccdb", o2::framework::VariantType::Bool, false, {"enable access to ccdb tof calibration objects"}}, + {"local-cmp", o2::framework::VariantType::Bool, false, {"if compressor is running in the same chain (e.g. epn)"}}, {"input-desc", o2::framework::VariantType::String, "CRAWDATA", {"Input specs description string"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, @@ -67,8 +69,10 @@ void customize(std::vector& workflowOptions) {"disable-row-writing", o2::framework::VariantType::Bool, false, {"disable ROW in Digit writing"}}, {"write-decoding-errors", o2::framework::VariantType::Bool, false, {"trace errors in digits output when decoding"}}, {"ignore-dist-stf", VariantType::Bool, false, {"do not subscribe to FLP/DISTSUBTIMEFRAME/0 message (no lost TF recovery)"}}, + {"orbits-per-tf", VariantType::Int, -1, {"default(-1) from GRP/CCDB, a valid value is required to run compressor for epn"}}, {"calib-cluster", VariantType::Bool, false, {"to enable calib info production from clusters"}}, {"for-calib", VariantType::Bool, false, {"to disable check on problematic, otherwise masked for new calibrations"}}, + {"calib-dia", VariantType::Bool, false, {"enabling writing calib for diagnostics"}}, {"cosmics", VariantType::Bool, false, {"to enable cosmics utils"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -141,8 +145,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) auto isCalibFromCluster = cfgc.options().get("calib-cluster"); auto isCosmics = cfgc.options().get("cosmics"); auto ignoreDistStf = cfgc.options().get("ignore-dist-stf"); + auto localCmp = cfgc.options().get("local-cmp"); + auto orbitPerTF = cfgc.options().get("orbits-per-tf"); auto ccdb_url = o2::base::NameConf::getCCDBServer(); auto isForCalib = cfgc.options().get("for-calib"); + auto writingDia = cfgc.options().get("calib-dia"); LOG(debug) << "TOF RECO WORKFLOW configuration"; LOG(debug) << "TOF input = " << cfgc.options().get("input-type"); @@ -158,6 +165,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) LOG(debug) << "TOF disable-root-output = " << disableRootOutput; LOG(debug) << "TOF conet-mode = " << conetmode; LOG(debug) << "TOF ignore Dist Stf = " << ignoreDistStf; + LOG(debug) << "Is tof compressor in the chain = " << localCmp; + if (orbitPerTF == -1) { + LOG(debug) << "Orbit per TF read from CCDB"; + } else { + LOG(debug) << "Orbit per TF = " << orbitPerTF; + } LOG(debug) << "TOF disable-row-writing = " << disableROWwriting; LOG(debug) << "TOF write-decoding-errors = " << writeerr; @@ -177,7 +190,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) } else if (rawinput) { LOG(debug) << "Insert TOF Compressed Raw Decoder"; auto inputDesc = cfgc.options().get("input-desc"); - specs.emplace_back(o2::tof::getCompressedDecodingSpec(inputDesc, conetmode, !ignoreDistStf)); + specs.emplace_back(o2::tof::getCompressedDecodingSpec(inputDesc, conetmode, !ignoreDistStf, orbitPerTF, localCmp)); useMC = 0; } if ((!dgtinput || disableRootInput) && writedigit && !disableRootOutput) { @@ -195,6 +208,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) } } + if (writingDia) { + specs.emplace_back(o2::tof::getTOFCalibWriterSpec("o2calib_tof.root", false, true, true)); + } + LOG(debug) << "Number of active devices = " << specs.size(); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt index c8db0209d4471..09ec6081b06b8 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt @@ -9,6 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) + o2_add_library(TPCInterpolationWorkflow SOURCES src/TPCInterpolationSpec.cxx src/TPCResidualWriterSpec.cxx diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h index fbc3cf640ec8c..83dbb1bd0f5fe 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h @@ -22,6 +22,7 @@ #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "TPCCalibration/VDriftHelper.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" using namespace o2::framework; @@ -37,7 +38,8 @@ namespace tpc class TPCInterpolationDPL : public Task { public: - TPCInterpolationDPL(std::shared_ptr dr, std::shared_ptr gr, bool useMC, bool processITSTPConly, bool sendTrackData) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC), mProcessITSTPConly(processITSTPConly), mSendTrackData(sendTrackData) {} + TPCInterpolationDPL(std::shared_ptr dr, o2::dataformats::GlobalTrackID::mask_t src, o2::dataformats::GlobalTrackID::mask_t srcMap, std::shared_ptr gr, bool useMC, + bool processITSTPConly, bool sendTrackData, bool debugOutput, bool extDetResid) : mDataRequest(dr), mSources(src), mSourcesMap(srcMap), mGGCCDBRequest(gr), mUseMC(useMC), mProcessITSTPConly(processITSTPConly), mSendTrackData(sendTrackData), mDebugOutput(debugOutput), mExtDetResid(extDetResid) {} ~TPCInterpolationDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -50,16 +52,23 @@ class TPCInterpolationDPL : public Task std::shared_ptr mDataRequest; ///< steers the input std::shared_ptr mGGCCDBRequest; o2::tpc::VDriftHelper mTPCVDriftHelper{}; + const o2::itsmft::TopologyDictionary* mITSDict = nullptr; ///< cluster patterns dictionary + o2::dataformats::GlobalTrackID::mask_t mSources{}; ///< which input sources are configured + o2::dataformats::GlobalTrackID::mask_t mSourcesMap{}; ///< possible subset of mSources specifically for map creation bool mUseMC{false}; ///< MC flag bool mProcessITSTPConly{false}; ///< should also tracks without outer point (ITS-TPC only) be processed? bool mProcessSeeds{false}; ///< process not only most complete track, but also its shorter parts + bool mDebugOutput{false}; ///< add more information to the output (track points of ITS, TRD and TOF) + bool mExtDetResid{true}; ///< produce unbinned residuals for external detectors bool mSendTrackData{false}; ///< if true, not only the clusters but also corresponding track data will be sent uint32_t mSlotLength{600u}; ///< the length of one calibration slot required to calculate max number of tracks per TF + int mMatCorr{2}; ///< the material correction to be used for track interpolation TStopwatch mTimer; }; /// create a processor spec -framework::DataProcessorSpec getTPCInterpolationSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool processITSTPConly, bool sendTrackData); +framework::DataProcessorSpec getTPCInterpolationSpec(o2::dataformats::GlobalTrackID::mask_t srcCls, o2::dataformats::GlobalTrackID::mask_t srcVtx, o2::dataformats::GlobalTrackID::mask_t srcTrk, + o2::dataformats::GlobalTrackID::mask_t srcTrkMap, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput, bool extDetResid); } // namespace tpc } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h index b71bdc001dd53..99f20e390a09a 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h @@ -19,6 +19,7 @@ #include "DetectorsCalibration/Utils.h" #include "SpacePoints/TrackResiduals.h" #include "SpacePoints/ResidualAggregator.h" +#include "TPCCalibration/VDriftHelper.h" #include "CommonUtils/MemFileHelper.h" #include "Framework/Task.h" #include "Framework/ConfigParamRegistry.h" @@ -101,22 +102,35 @@ class ResidualAggregatorDevice : public o2::framework::Task void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { - o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + return; + } } void run(o2::framework::ProcessingContext& pc) final { + const auto& tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged) { + // new run is starting + LOG(info) << "New run start detected"; + mRunStopRequested = false; + mInitDone = false; + } + if (mRunStopRequested) { + return; + } auto runStartTime = std::chrono::high_resolution_clock::now(); o2::globaltracking::RecoContainer recoCont; recoCont.collectData(pc, *mDataRequest); updateTimeDependentParams(pc); std::chrono::duration ccdbUpdateTime = std::chrono::high_resolution_clock::now() - runStartTime; - // assume that the orbit reset time (given here in ms) can change within a run - auto orbitResetTime = o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(); - - // we always require the unbinned residuals and the associated track references + // we always require the unbinned residuals and the associated detector info and track references auto residualsData = pc.inputs().get>("unbinnedRes"); + auto residualsDataDet = pc.inputs().get>("detinfoRes"); auto trackRefs = pc.inputs().get>("trackRefs"); // track data input is optional @@ -132,37 +146,57 @@ class ResidualAggregatorDevice : public o2::framework::Task using lumiDataType = std::decay_t(""))>; std::optional lumiInput; if (mCTPInput) { - recoCont.getCTPLumi(); lumiInput = recoCont.getCTPLumi(); lumi = &lumiInput.value(); } o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mAggregator->getCurrentTFInfo()); - LOG(info) << "Processing TF " << mAggregator->getCurrentTFInfo().tfCounter << " with " << trkData->size() << " tracks and " << residualsData.size() << " unbinned residuals associated to them"; - mAggregator->process(residualsData, trackRefs, orbitResetTime, trkDataPtr, lumi); + LOG(detail) << "Processing TF " << mAggregator->getCurrentTFInfo().tfCounter << " with " << trkData->size() << " tracks and " << residualsData.size() << " unbinned residuals associated to them"; + mAggregator->process(residualsData, residualsDataDet, trackRefs, trkDataPtr, lumi); std::chrono::duration runDuration = std::chrono::high_resolution_clock::now() - runStartTime; - LOGP(info, "Duration for run method: {} ms. From this taken for time dependent param update: {} ms", + LOGP(debug, "Duration for run method: {} ms. From this taken for time dependent param update: {} ms", std::chrono::duration_cast(runDuration).count(), std::chrono::duration_cast(ccdbUpdateTime).count()); + if (pc.transitionState() == TransitionHandlingState::Requested) { + LOG(info) << "Run stop requested, finalizing"; + mRunStopRequested = true; + mAggregator->checkSlotsToFinalize(); + mAggregator.reset(); + LOG(info) << "Finalizing done"; + } } void endOfStream(o2::framework::EndOfStreamContext& ec) final { + if (mRunStopRequested) { + return; + } LOG(info) << "Finalizing calibration for end of stream"; mAggregator->checkSlotsToFinalize(); mAggregator.reset(); // must invoke destructor manually here, otherwise we get a segfault + LOG(info) << "Finalizing done for end of stream"; } private: void updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); - static bool initOnceDone = false; - if (!initOnceDone) { - initOnceDone = true; + mTPCVDriftHelper.extractCCDBInputs(pc); + if (mTPCVDriftHelper.isUpdated()) { + LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, + mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, + mTPCVDriftHelper.getSourceName()); + mAggregator->setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); + mTPCVDriftHelper.acknowledgeUpdate(); + } + if (!mInitDone) { mAggregator->setDataTakingContext(pc.services().get()); + mAggregator->setOrbitResetTime(o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS()); + mInitDone = true; } } + o2::tpc::VDriftHelper mTPCVDriftHelper{}; std::unique_ptr mAggregator; ///< the TimeSlotCalibration device std::shared_ptr mCCDBRequest; std::shared_ptr mDataRequest; ///< optional CTP input @@ -171,6 +205,8 @@ class ResidualAggregatorDevice : public o2::framework::Task bool mWriteBinnedResiduals{false}; ///< flag, whether to write binned residuals to output file bool mWriteUnbinnedResiduals{false}; ///< flag, whether to write unbinned residuals to output file bool mWriteTrackData{false}; ///< flag, whether to write track data to output file + bool mRunStopRequested{false}; ///< flag in case the run was stopped + bool mInitDone{false}; ///< flag whether initialization was done for current run }; } // namespace calibration @@ -185,7 +221,9 @@ DataProcessorSpec getTPCResidualAggregatorSpec(bool trackInput, bool ctpInput, b dataRequest->requestClusters(GID::getSourcesMask("CTP"), false); } auto& inputs = dataRequest->inputs; + o2::tpc::VDriftHelper::requestCCDBInputs(inputs); inputs.emplace_back("unbinnedRes", "GLO", "UNBINNEDRES"); + inputs.emplace_back("detinfoRes", "GLO", "DETINFORES"); inputs.emplace_back("trackRefs", "GLO", "TRKREFS"); if (trackInput) { inputs.emplace_back("trkData", "GLO", "TRKDATA"); @@ -196,7 +234,8 @@ DataProcessorSpec getTPCResidualAggregatorSpec(bool trackInput, bool ctpInput, b false, // GRPMagField false, // askMatLUT o2::base::GRPGeomRequest::None, // geometry - inputs); + inputs, + true); return DataProcessorSpec{ "residual-aggregator", inputs, diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualWriterSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualWriterSpec.h index 5405b55fa90a5..3a4492d666ad2 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualWriterSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualWriterSpec.h @@ -22,7 +22,7 @@ namespace tpc { /// create a processor spec -framework::DataProcessorSpec getTPCResidualWriterSpec(bool writeTrackData); +framework::DataProcessorSpec getTPCResidualWriterSpec(bool writeTrackData, bool debugOutput); } // namespace tpc } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h index 6c40bb355eb21..724151c90576f 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h @@ -43,6 +43,7 @@ class TPCUnbinnedResidualReader : public o2::framework::Task std::string mInFileName; std::string mInTreeName; std::vector mUnbinnedResid, *mUnbinnedResidPtr = &mUnbinnedResid; + std::vector mDetInfoUnbRes, *mDetInfoUnbResPtr = &mDetInfoUnbRes; std::vector mTrackData, *mTrackDataPtr = &mTrackData; std::vector mTrackDataCompact, *mTrackDataCompactPtr = &mTrackDataCompact; }; diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx index 3f83e586140cf..fe2677416edc2 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx @@ -15,6 +15,7 @@ #include #include "DataFormatsITS/TrackITS.h" +#include "ITSBase/GeometryTGeo.h" #include "ReconstructionDataFormats/TrackTPCITS.h" #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTPC/ClusterNative.h" @@ -31,6 +32,7 @@ #include "SpacePoints/SpacePointsCalibConfParam.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/ControlService.h" +#include "Framework/DeviceSpec.h" using namespace o2::framework; using namespace o2::globaltracking; @@ -50,6 +52,13 @@ void TPCInterpolationDPL::init(InitContext& ic) o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mSlotLength = ic.options().get("sec-per-slot"); mProcessSeeds = ic.options().get("process-seeds"); + mMatCorr = ic.options().get("matCorrType"); + if (mProcessSeeds && mSources != mSourcesMap) { + LOG(fatal) << "process-seeds option is not compatible with using different track sources for vDrift and map extraction"; + } + int lane = ic.services().get().inputTimesliceId; + int maxLanes = ic.services().get().maxInputTimeslices; + mInterpolation.setLane(lane, maxLanes); } void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) @@ -61,17 +70,22 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) initOnceDone = true; // other init-once stuff const auto& param = SpacePointsCalibConfParam::Instance(); - mInterpolation.init(); + mInterpolation.setSqrtS(o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS()); + mInterpolation.setNHBPerTF(o2::base::GRPGeomHelper::getNHBFPerTF()); + mInterpolation.init(mSources, mSourcesMap); + if (mProcessITSTPConly) { + mInterpolation.setProcessITSTPConly(); + } int nTfs = mSlotLength / (o2::base::GRPGeomHelper::getNHBFPerTF() * o2::constants::lhc::LHCOrbitMUS * 1e-6); bool limitTracks = (param.maxTracksPerCalibSlot < 0) ? false : true; int nTracksPerTfMax = (nTfs > 0 && limitTracks) ? param.maxTracksPerCalibSlot / nTfs : -1; if (nTracksPerTfMax > 0) { LOGP(info, "We will stop processing tracks after validating {} tracks per TF, since we want to accumulate {} tracks for a slot with {} TFs", nTracksPerTfMax, param.maxTracksPerCalibSlot, nTfs); - if (param.additionalTracksITSTPC > 0) { - int nITSTPCadd = param.additionalTracksITSTPC / nTfs; - LOGP(info, "In addition up to {} ITS-TPC tracks are processed per TF", nITSTPCadd); - mInterpolation.setAddITSTPCTracksPerTF(nITSTPCadd); + if (param.additionalTracksMap > 0) { + int nTracksAdditional = param.additionalTracksMap / nTfs; + LOGP(info, "In addition up to {} additional tracks are processed per TF", nTracksAdditional); + mInterpolation.setAddTracksForMapPerTF(nTracksAdditional); } } else if (nTracksPerTfMax < 0) { LOG(info) << "The number of processed tracks per TF is not limited"; @@ -79,6 +93,16 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) LOG(error) << "No tracks will be processed. maxTracksPerCalibSlot must be greater than slot length in TFs"; } mInterpolation.setMaxTracksPerTF(nTracksPerTfMax); + mInterpolation.setMatCorr(static_cast(mMatCorr)); + if (mProcessSeeds) { + mInterpolation.setProcessSeeds(); + } + o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)); + mInterpolation.setExtDetResid(mExtDetResid); + mInterpolation.setITSClusterDictionary(mITSDict); + if (mDebugOutput) { + mInterpolation.setDumpTrackPoints(); + } } // we may have other params which need to be queried regularly if (mTPCVDriftHelper.isUpdated()) { @@ -99,133 +123,43 @@ void TPCInterpolationDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mITSDict = (const o2::itsmft::TopologyDictionary*)obj; + return; + } } void TPCInterpolationDPL::run(ProcessingContext& pc) { - LOG(info) << "TPC Interpolation Workflow initialized. Start processing..."; mTimer.Start(false); RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); updateTimeDependentParams(pc); - const auto& param = SpacePointsCalibConfParam::Instance(); - - // load the input tracks - std::vector gidTables; - std::vector seeds; - std::vector trkTimes; - std::vector gids; - std::unordered_map trkCounters; - // make sure the map has entries for every possible track input type - trkCounters.insert(std::make_pair(GTrackID::Source::ITSTPCTRDTOF, 0)); - trkCounters.insert(std::make_pair(GTrackID::Source::ITSTPCTRD, 0)); - trkCounters.insert(std::make_pair(GTrackID::Source::ITSTPCTOF, 0)); - trkCounters.insert(std::make_pair(GTrackID::Source::ITSTPC, 0)); - - // so that the flags can be used inside the lambda - bool processITSTPConly = mProcessITSTPConly; - bool processSeeds = mProcessSeeds; - // the creator goes from most complete track (ITS-TPC-TRD-TOF) to least complete one (ITS-TPC) - auto creator = [&gidTables, &seeds, &trkTimes, &recoData, &processITSTPConly, &processSeeds, &gids, ¶m, &trkCounters](auto& _tr, GTrackID _origID, float t0, float tErr) { - if constexpr (std::is_base_of_v>) { - bool trackGood = true; - bool hasOuterPoint = false; - auto gidTable = recoData.getSingleDetectorRefs(_origID); - if (!gidTable[GTrackID::ITS].isIndexSet() || !gidTable[GTrackID::TPC].isIndexSet()) { - // ITS and TPC track is always needed. At this stage ITS afterburner tracks are also rejected - return true; - } - if (gidTable[GTrackID::TRD].isIndexSet()) { - // TRD specific cuts - const auto& trdTrk = recoData.getITSTPCTRDTrack(gidTable[GTrackID::ITSTPCTRD]); - if (trdTrk.getNtracklets() < param.minTRDNTrklts) { - trackGood = false; - } - hasOuterPoint = true; - } - if (gidTable[GTrackID::TOF].isIndexSet()) { - // TOF specific cuts (if any) - hasOuterPoint = true; - } - const auto itstpcTrk = &recoData.getTPCITSTrack(gidTable[GTrackID::ITSTPC]); - const auto itsTrk = &recoData.getITSTrack(gidTable[GTrackID::ITS]); - const auto tpcTrk = &recoData.getTPCTrack(gidTable[GTrackID::TPC]); - // apply track quality cuts - if (itsTrk->getChi2() / itsTrk->getNumberOfClusters() > param.maxITSChi2 || tpcTrk->getChi2() / tpcTrk->getNClusterReferences() > param.maxTPCChi2) { - // reduced chi2 cut is the same for all track types - trackGood = false; - } - if (!hasOuterPoint) { - // ITS-TPC track (does not have outer points in TRD or TOF) - if (!processITSTPConly) { - return true; - } - if (itsTrk->getNumberOfClusters() < param.minITSNClsNoOuterPoint || tpcTrk->getNClusterReferences() < param.minTPCNClsNoOuterPoint) { - trackGood = false; - } - if (itsTrk->getPt() < param.minPtNoOuterPoint) { - trackGood = false; - } - } else { - if (itsTrk->getNumberOfClusters() < param.minITSNCls || tpcTrk->getNClusterReferences() < param.minTPCNCls) { - trackGood = false; - } - } - if (trackGood) { - trkTimes.push_back(t0); - seeds.emplace_back(itsTrk->getParamOut()); // FIXME: should this not be a refit of the ITS track? - gidTables.emplace_back(gidTable); - gids.push_back(_origID); - trkCounters[_origID.getSource()] += 1; - } - if (processSeeds || (gidTable[GTrackID::TRD].isIndexSet() && gidTable[GTrackID::TOF].isIndexSet())) { - // for ITS-TPC-TRD-TOF tracks we are interested also in the ITS-TPC-TRD part - // we want to have both available, so return false for the full barrell tracks - return false; - } else { - return true; - } - } else { - return false; - } - }; - recoData.createTracksVariadic(creator); // create track sample considered for interpolation - LOGP(info, "Created {} seeds. {} ITS-TPC-TRD-TOF, {} ITS-TPC-TRD, {} ITS-TPC-TOF, {} ITS-TPC", - seeds.size(), trkCounters.at(GTrackID::Source::ITSTPCTRDTOF), trkCounters.at(GTrackID::Source::ITSTPCTRD), - trkCounters.at(GTrackID::Source::ITSTPCTOF), trkCounters.at(GTrackID::Source::ITSTPC)); - - if (mUseMC) { - // possibly MC labels will be used to check filtering procedure performance before interpolation - // not yet implemented - } - - mInterpolation.process(recoData, gids, gidTables, seeds, trkTimes, trkCounters); + mInterpolation.prepareInputTrackSample(recoData); + mInterpolation.process(); mTimer.Stop(); LOGF(info, "TPC interpolation timing: Cpu: %.3e Real: %.3e s", mTimer.CpuTime(), mTimer.RealTime()); - - if (param.writeUnfiltered) { - // these are the residuals and tracks before outlier rejection; they are not used in production - pc.outputs().snapshot(Output{"GLO", "TPCINT_RES", 0, Lifetime::Timeframe}, mInterpolation.getClusterResidualsUnfiltered()); - if (mSendTrackData) { - pc.outputs().snapshot(Output{"GLO", "TPCINT_TRK", 0, Lifetime::Timeframe}, mInterpolation.getReferenceTracksUnfiltered()); - } - } - pc.outputs().snapshot(Output{"GLO", "UNBINNEDRES", 0, Lifetime::Timeframe}, mInterpolation.getClusterResiduals()); - pc.outputs().snapshot(Output{"GLO", "TRKREFS", 0, Lifetime::Timeframe}, mInterpolation.getTrackDataCompact()); + pc.outputs().snapshot(Output{"GLO", "UNBINNEDRES", 0}, mInterpolation.getClusterResiduals()); + pc.outputs().snapshot(Output{"GLO", "DETINFORES", 0}, mInterpolation.getClusterResidualsDetInfo()); + pc.outputs().snapshot(Output{"GLO", "TRKREFS", 0}, mInterpolation.getTrackDataCompact()); if (mSendTrackData) { - pc.outputs().snapshot(Output{"GLO", "TRKDATA", 0, Lifetime::Timeframe}, mInterpolation.getReferenceTracks()); + pc.outputs().snapshot(Output{"GLO", "TRKDATA", 0}, mInterpolation.getReferenceTracks()); + } + if (mDebugOutput) { + pc.outputs().snapshot(Output{"GLO", "TRKDATAEXT", 0}, mInterpolation.getTrackDataExtended()); } - mInterpolation.reset(); } void TPCInterpolationDPL::endOfStream(EndOfStreamContext& ec) { + mInterpolation.finalize(); LOGF(info, "TPC residuals extraction total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t src, bool useMC, bool processITSTPConly, bool sendTrackData) +DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mask_t srcVtx, GTrackID::mask_t srcTrk, GTrackID::mask_t srcTrkMap, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput, bool extDetResid) { auto dataRequest = std::make_shared(); std::vector outputs; @@ -234,37 +168,37 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t src, bool useMC, bool LOG(fatal) << "MC usage must be disabled for this workflow, since it is not yet implemented"; } - dataRequest->requestTracks(src, useMC); - dataRequest->requestClusters(src, useMC); + dataRequest->requestTracks(srcVtx, useMC); + dataRequest->requestClusters(srcCls, useMC); + dataRequest->requestPrimaryVertices(useMC); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true - false, // GRPLHCIF + true, // GRPLHCIF true, // GRPMagField true, // askMatLUT o2::base::GRPGeomRequest::Aligned, // geometry dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - if (SpacePointsCalibConfParam::Instance().writeUnfiltered) { - outputs.emplace_back("GLO", "TPCINT_TRK", 0, Lifetime::Timeframe); - if (sendTrackData) { - outputs.emplace_back("GLO", "TPCINT_RES", 0, Lifetime::Timeframe); - } - } outputs.emplace_back("GLO", "UNBINNEDRES", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "DETINFORES", 0, Lifetime::Timeframe); outputs.emplace_back("GLO", "TRKREFS", 0, Lifetime::Timeframe); if (sendTrackData) { outputs.emplace_back("GLO", "TRKDATA", 0, Lifetime::Timeframe); } + if (debugOutput) { + outputs.emplace_back("GLO", "TRKDATAEXT", 0, Lifetime::Timeframe); + } return DataProcessorSpec{ "tpc-track-interpolation", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, processITSTPConly, sendTrackData)}, + AlgorithmSpec{adaptFromTask(dataRequest, srcTrk, srcTrkMap, ggRequest, useMC, processITSTPConly, sendTrackData, debugOutput, extDetResid)}, Options{ - {"sec-per-slot", VariantType::UInt32, 600u, {"number of seconds per calibration time slot (put 0 for infinite slot length)"}}, + {"matCorrType", VariantType::Int, 2, {"material correction type (definition in Propagator.h)"}}, + {"sec-per-slot", VariantType::UInt32, 300u, {"number of seconds per calibration time slot (put 0 for infinite slot length)"}}, {"process-seeds", VariantType::Bool, false, {"do not remove duplicates, e.g. for ITS-TPC-TRD track also process its seeding ITS-TPC part"}}}}; } diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualReaderSpec.cxx index 23a6c8fd06694..b3040d99bc4f2 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualReaderSpec.cxx @@ -251,7 +251,7 @@ void TPCResidualReader::run(ProcessingContext& pc) mTrackResiduals.closeOutputFile(); // FIXME remove when map output is handled properly // const auto& voxResArray = mTrackResiduals.getVoxelResults(); // array with one vector of results per sector - // pc.outputs().snapshot(Output{"GLO", "VOXELRESULTS", 0, Lifetime::Timeframe}, voxResArray); // send results as one large vector? + // pc.outputs().snapshot(Output{"GLO", "VOXELRESULTS", 0}, voxResArray); // send results as one large vector? pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx index e6269aca5c904..68c9b3abfe4ef 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx @@ -29,17 +29,17 @@ namespace tpc template using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; -DataProcessorSpec getTPCResidualWriterSpec(bool writeTrackData) +DataProcessorSpec getTPCResidualWriterSpec(bool writeTrackData, bool debugOutput) { bool writeUnfiltered = SpacePointsCalibConfParam::Instance().writeUnfiltered; return MakeRootTreeWriterSpec("tpc-residuals-writer", "o2residuals_tpc.root", "residualsTPC", - BranchDefinition>{InputSpec{"tracksUnfiltered", "GLO", "TPCINT_TRK", 0}, "tracksUnfiltered", ((writeUnfiltered && writeTrackData) ? 1 : 0)}, - BranchDefinition>{InputSpec{"residualsUnfiltered", "GLO", "TPCINT_RES", 0}, "residualsUnfiltered", (writeUnfiltered ? 1 : 0)}, BranchDefinition>{InputSpec{"residuals", "GLO", "UNBINNEDRES"}, "residuals"}, + BranchDefinition>{InputSpec{"detInfo", "GLO", "DETINFORES"}, "detInfo"}, BranchDefinition>{InputSpec{"trackRefs", "GLO", "TRKREFS"}, "trackRefs"}, - BranchDefinition>{InputSpec{"tracks", "GLO", "TRKDATA"}, "tracks", (writeTrackData ? 1 : 0)})(); + BranchDefinition>{InputSpec{"tracks", "GLO", "TRKDATA"}, "tracks", (writeTrackData ? 1 : 0)}, + BranchDefinition>{InputSpec{"trackExt", "GLO", "TRKDATAEXT"}, "trackExt", (debugOutput ? 1 : 0)})(); } } // namespace tpc diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx index af50b32bc8bba..c2dae375731a4 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx @@ -44,6 +44,11 @@ void TPCUnbinnedResidualReader::connectTree() assert(mTreeIn); mTreeIn->SetBranchAddress("residuals", &mUnbinnedResidPtr); mTreeIn->SetBranchAddress("trackRefs", &mTrackDataCompactPtr); + if (mTreeIn->GetBranch("detInfo")) { + mTreeIn->SetBranchAddress("detInfo", &mDetInfoUnbResPtr); + } else { + LOGP(warn, "No detInfo branch found in the unbinned residuals tree, empty vector will be sent"); + } if (mTrackInput) { mTreeIn->SetBranchAddress("tracks", &mTrackDataPtr); } @@ -56,11 +61,12 @@ void TPCUnbinnedResidualReader::run(ProcessingContext& pc) assert(currEntry < mTreeIn->GetEntries()); // this should not happen mTreeIn->GetEntry(currEntry); LOG(info) << "Pushing " << mUnbinnedResid.size() << " unbinned residuals at entry " << currEntry; - pc.outputs().snapshot(Output{"GLO", "UNBINNEDRES", 0, Lifetime::Timeframe}, mUnbinnedResid); - pc.outputs().snapshot(Output{"GLO", "TRKREFS", 0, Lifetime::Timeframe}, mTrackDataCompact); + pc.outputs().snapshot(Output{"GLO", "UNBINNEDRES", 0}, mUnbinnedResid); + pc.outputs().snapshot(Output{"GLO", "TRKREFS", 0}, mTrackDataCompact); + pc.outputs().snapshot(Output{"GLO", "DETINFORES", 0}, mDetInfoUnbRes); if (mTrackInput) { LOG(info) << "Pushing " << mTrackData.size() << " reference tracks for these residuals"; - pc.outputs().snapshot(Output{"GLO", "TRKDATA", 0, Lifetime::Timeframe}, mTrackData); + pc.outputs().snapshot(Output{"GLO", "TRKDATA", 0}, mTrackData); } if (mTreeIn->GetReadEntry() + 1 >= mTreeIn->GetEntries()) { @@ -73,6 +79,7 @@ DataProcessorSpec getUnbinnedTPCResidualsReaderSpec(bool trkInput) { std::vector outputs; outputs.emplace_back("GLO", "UNBINNEDRES", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "DETINFORES", 0, Lifetime::Timeframe); outputs.emplace_back("GLO", "TRKREFS", 0, Lifetime::Timeframe); if (trkInput) { outputs.emplace_back("GLO", "TRKDATA", 0, Lifetime::Timeframe); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx index 7660468fb789f..65a79a8635a49 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx @@ -19,6 +19,7 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "TPCInterpolationWorkflow/TPCInterpolationSpec.h" #include "TPCInterpolationWorkflow/TPCResidualWriterSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -37,10 +38,14 @@ void customize(std::vector& workflowOptions) {"disable-root-input", VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", VariantType::Bool, false, {"disable root-files output writers"}}, {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, - {"enable-itsonly", VariantType::Bool, false, {"process tracks without outer point (ITS-TPC only)"}}, - {"tracking-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use for tracking"}}, + {"vtx-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources used for the vertex finding"}}, + {"tracking-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use for track inter-/extrapolation"}}, + {"tracking-sources-map-extraction", VariantType::String, std::string{GID::ALL}, {"can be subset of \"tracking-sources\""}}, {"send-track-data", VariantType::Bool, false, {"Send also the track information to the aggregator"}}, + {"debug-output", VariantType::Bool, false, {"Dump extended tracking information for debugging"}}, + {"skip-ext-det-residuals", VariantType::Bool, false, {"Do not produce residuals for external detectors"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -65,24 +70,52 @@ void customize(std::vector& policies) WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { WorkflowSpec specs; - GID::mask_t allowedSources = GID::getSourcesMask("ITS,TPC,TRD,TOF,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t allowedSources = GID::getSourcesMask("ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t srcVtx = allowedSources & GID::getSourcesMask(configcontext.options().get("vtx-sources")); + GID::mask_t srcTracks = allowedSources & GID::getSourcesMask(configcontext.options().get("tracking-sources")); + GID::mask_t srcTracksMap = allowedSources & GID::getSourcesMask(configcontext.options().get("tracking-sources-map-extraction")); + if (srcTracks.count() > srcVtx.count()) { + LOGP(error, "More sources configured for inter-/extrapolation: {} than for vertexing: {}. Additional sources will be ignored", GID::getSourcesNames(srcTracks), GID::getSourcesNames(srcVtx)); + srcTracks &= srcVtx; + } + srcTracksMap &= srcVtx; + if (((srcTracksMap | srcTracks) ^ srcTracks).any()) { + LOGP(fatal, "tracking-sources-map-extraction ({}) must be a subset of tracking-sources ({}).", GID::getSourcesNames(srcTracksMap), GID::getSourcesNames(srcTracks)); + } else if (srcTracksMap != srcTracks) { + LOGP(info, "Will extract residual from different track types. For vDrift from {} and for distortion map from {}", GID::getSourcesNames(srcTracks), GID::getSourcesNames(srcTracksMap)); + } else { + LOGP(info, "Only a single track source is defined for residuals extraction: {}", GID::getSourcesNames(srcTracks)); + } + LOG(debug) << "Data sources for inter-/extrapolation: " << GID::getSourcesNames(srcTracks); + // check first if ITS-TPC tracks were specifically requested from command line + bool processITSTPConly = srcTracks[GID::ITSTPC]; + srcTracks |= GID::getSourcesMask("ITS,TPC,ITS-TPC"); // now add them in any case + srcTracksMap |= GID::getSourcesMask("ITS,TPC,ITS-TPC"); + srcVtx |= srcTracks; + GID::mask_t srcClusters = srcTracks; + if (srcTracks[GID::ITSTPCTRD] || srcTracks[GID::ITSTPCTRDTOF]) { + srcClusters |= GID::getSourcesMask("TRD"); + } + if (srcTracks[GID::ITSTPCTOF] || srcTracks[GID::ITSTPCTRDTOF]) { + srcClusters |= GID::getSourcesMask("TOF"); + } // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2tpcinterpolation-workflow_configuration.ini"); auto useMC = !configcontext.options().get("disable-mc"); useMC = false; // force disabling MC as long as it is not implemented - auto processITSTPConly = configcontext.options().get("enable-itsonly"); auto sendTrackData = configcontext.options().get("send-track-data"); - GID::mask_t src = allowedSources & GID::getSourcesMask(configcontext.options().get("tracking-sources")); - LOG(info) << "Data sources: " << GID::getSourcesNames(src); + auto debugOutput = configcontext.options().get("debug-output"); + auto extDetResid = !configcontext.options().get("skip-ext-det-residuals"); - specs.emplace_back(o2::tpc::getTPCInterpolationSpec(src, useMC, processITSTPConly, sendTrackData)); + specs.emplace_back(o2::tpc::getTPCInterpolationSpec(srcClusters, srcVtx, srcTracks, srcTracksMap, useMC, processITSTPConly, sendTrackData, debugOutput, extDetResid)); if (!configcontext.options().get("disable-root-output")) { - specs.emplace_back(o2::tpc::getTPCResidualWriterSpec(sendTrackData)); + specs.emplace_back(o2::tpc::getTPCResidualWriterSpec(sendTrackData, debugOutput)); } - o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, src, src, src, useMC); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcClusters, srcVtx, srcVtx, useMC); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx index 402dd5608f61e..20e37c3bcc3b4 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx @@ -14,19 +14,27 @@ #include "TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h" #include "TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + // we need to add workflow options before including Framework/runDataProcessing void customize(std::vector& workflowOptions) { std::vector options{ - {"output-type", VariantType::String, "binnedResid", {"Comma separated list of outputs (without spaces). Valid strings: unbinnedResid, binnedResid, trackParams"}}, + {"output-type", VariantType::String, "unbinnedResid,trackParams", {"Comma separated list of outputs (without spaces). Valid strings: unbinnedResid, binnedResid, trackParams"}}, {"enable-track-input", VariantType::Bool, false, {"Whether to expect track data from interpolation workflow"}}, {"enable-ctp", VariantType::Bool, false, {"Subscribe to lumi info from CTP"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input readers"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -43,7 +51,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) bool writeUnbinnedResiduals = false; bool writeBinnedResiduals = false; bool writeTrackData = false; - auto outputType = configcontext.options().get("output-type"); + auto outputType = configcontext.options().get("output-type"); std::vector outputTypes; size_t pos = 0; while ((pos = outputType.find(",")) != std::string::npos) { @@ -79,5 +87,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, maskClusters, maskNone, maskNone, false); } + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + return specs; } diff --git a/Detectors/HMPID/README.md b/Detectors/HMPID/README.md index 775f2ad24d77c..3a744b72812ea 100644 --- a/Detectors/HMPID/README.md +++ b/Detectors/HMPID/README.md @@ -8,4 +8,5 @@ This is a top page for the HMPID detector documentation. diff --git a/Detectors/HMPID/base/include/HMPIDBase/Param.h b/Detectors/HMPID/base/include/HMPIDBase/Param.h index 9d5b044e155cf..ce895dd678c8b 100644 --- a/Detectors/HMPID/base/include/HMPIDBase/Param.h +++ b/Detectors/HMPID/base/include/HMPIDBase/Param.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -67,8 +67,10 @@ class Param kPadSigmaMasked = 20 }; // One can go up to 5 sigma cut, overflow is protected in AliHMPIDCalib static float r2d() { return 57.2957795; } - static float sizePadX() { return fgCellX; } // pad size x, [cm] - static float sizePadY() { return fgCellY; } // pad size y, [cm] + static float sizePadX() { return fgCellX; } // pad size x, [cm] + static float sizePadY() { return fgCellY; } // pad size y, [cm] + static float sizeHalfPadX() { return fgHalfCellX; } // half pad size x, [cm] + static float sizeHalfPadY() { return fgHalfCellY; } // half pad size y, [cm] static float sizePcX() { return fgPcX; } // PC size x static float sizePcY() { return fgPcY; } // PC size y @@ -100,8 +102,8 @@ class Param bool getInstType() const { return fgInstanceType; } // return if the instance is from geom or ideal - inline static bool isInDead(float x, float y); // is the point in dead area? - inline static bool isDeadPad(Int_t padx, Int_t pady, Int_t ch); // is a dead pad? + static bool isInDead(float x, float y); // is the point in dead area? + static bool isDeadPad(Int_t padx, Int_t pady, Int_t ch); // is a dead pad? inline void setChStatus(Int_t ch, bool status = kTRUE); inline void setSectStatus(Int_t ch, Int_t sect, bool status); @@ -281,8 +283,8 @@ class Param static Int_t fgThreshold; // sigma Cut static bool fgInstanceType; // kTRUE if from geomatry kFALSE if from ideal geometry - static float fgCellX, fgCellY, fgPcX, fgPcY, fgAllX, fgAllY; // definition of HMPID geometric parameters - Param(bool noGeo); // default ctor is protected to enforce it to be singleton + static float fgCellX, fgCellY, fgHalfCellX, fgHalfCellY, fgPcX, fgPcY, fgAllX, fgAllY; // definition of HMPID geometric parameters + Param(bool noGeo); // default ctor is protected to enforce it to be singleton static Param* fgInstance; // static pointer to instance of Param singleton diff --git a/Detectors/HMPID/base/src/Param.cxx b/Detectors/HMPID/base/src/Param.cxx index 9696067801fd6..3aa8ac0a220e7 100644 --- a/Detectors/HMPID/base/src/Param.cxx +++ b/Detectors/HMPID/base/src/Param.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -48,6 +48,8 @@ bool Param::fgMapPad[160][144][7]; float Param::fgCellX = 0.; float Param::fgCellY = 0.; +float Param::fgHalfCellX = 0.; +float Param::fgHalfCellY = 0.; float Param::fgPcX = 0; float Param::fgPcY = 0; @@ -57,14 +59,14 @@ float Param::fgAllY = 0; bool Param::fgInstanceType = kTRUE; -Param* Param::fgInstance = nullptr; //singleton pointer +Param* Param::fgInstance = nullptr; // singleton pointer Int_t Param::fgNSigmas = 4; Int_t Param::fgThreshold = 4; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mTemp(25) -//just set a refractive index for C6F14 at ephot=6.675 eV @ T=25 C +// just set a refractive index for C6F14 at ephot=6.675 eV @ T=25 C { // Here all the intitializition is taken place when Param::Instance() is invoked for the first time. // In particular, matrices to be used for LORS<->MARS trasnformations are initialized from TGeo structure. @@ -90,12 +92,12 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT } } */ - mRefIdx = meanIdxRad(); //initialization of the running ref. index of freon + mRefIdx = meanIdxRad(); // initialization of the running ref. index of freon float dead = 2.6; // cm of the dead zones between PCs-> See 2CRC2099P1 if (noGeo == kTRUE) { - fgInstanceType = kFALSE; //instance from ideal geometry, no actual geom is present + fgInstanceType = kFALSE; // instance from ideal geometry, no actual geom is present } if (noGeo == kFALSE && !gGeoManager) { @@ -107,6 +109,8 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT fgCellX = 0.8; fgCellY = 0.84; + fgHalfCellX = fgCellX * 0.5; + fgHalfCellY = fgCellY * 0.5; if (!noGeo == kTRUE) { TGeoVolume* pCellVol = gGeoManager->GetVolume("Hcel"); @@ -148,7 +152,7 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT for (Int_t ich = kMinCh; ich <= kMaxCh; ich++) { for (Int_t padx = 0; padx < 160; padx++) { for (Int_t pady = 0; pady < 144; pady++) { - fgMapPad[padx][pady][ich] = kTRUE; //init all the pads are active at the beginning.... + fgMapPad[padx][pady][ich] = kTRUE; // init all the pads are active at the beginning.... } } } @@ -157,7 +161,7 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT if (gGeoManager && gGeoManager->IsClosed()) { TGeoPNEntry* pne = gGeoManager->GetAlignableEntry(Form("/HMPID/Chamber%i", i)); if (!pne) { - //AliErrorClass(Form("The symbolic volume %s does not correspond to any physical entry!",Form("HMPID_%i",i))); + // AliErrorClass(Form("The symbolic volume %s does not correspond to any physical entry!",Form("HMPID_%i",i))); mM[i] = new TGeoHMatrix; idealPosition(i, mM[i]); } else { @@ -175,7 +179,7 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT } } fgInstance = this; -} //ctor +} // ctor //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::print(Option_t* opt) const { @@ -184,7 +188,7 @@ void Param::print(Option_t* opt) const for (Int_t i = 0; i < 7; i++) { mM[i]->Print(opt); } -} //Print() +} // Print() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::idealPosition(Int_t iCh, TGeoHMatrix* pMatrix) { @@ -201,46 +205,46 @@ void Param::idealPosition(Int_t iCh, TGeoHMatrix* pMatrix) case 0: pMatrix->RotateY(kAngHor); pMatrix->RotateZ(-kAngVer); - break; //right and down + break; // right and down case 1: pMatrix->RotateZ(-kAngVer); - break; //down + break; // down case 2: pMatrix->RotateY(kAngHor); - break; //right + break; // right case 3: - break; //no rotation + break; // no rotation case 4: pMatrix->RotateY(-kAngHor); - break; //left + break; // left case 5: pMatrix->RotateZ(kAngVer); - break; //up + break; // up case 6: pMatrix->RotateY(-kAngHor); pMatrix->RotateZ(kAngVer); - break; //left and up + break; // left and up } - pMatrix->RotateZ(kAngCom); //apply common rotation in XY plane + pMatrix->RotateZ(kAngCom); // apply common rotation in XY plane } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ /*Int_t Param::Stack(Int_t evt,Int_t tid) { // Prints some useful info from stack // Arguments: evt - event number. if not -1 print info only for that event -// tid - track id. if not -1 then print it and all it's mothers if any +// tid - track id. if not -1 then print it and all it's mothers if any // Returns: mother tid of the given tid if any - AliRunLoader *pAL=AliRunLoader::Open(); + AliRunLoader *pAL=AliRunLoader::Open(); if(pAL->LoadHeader()) return -1; if(pAL->LoadKinematics()) return -1; - + Int_t mtid=-1; Int_t iNevt=pAL->GetNumberOfEvents(); - + for(Int_t iEvt=0;iEvtGetEvent(iEvt); - AliStack *pStack=pAL->Stack(); + pAL->GetEvent(iEvt); + AliStack *pStack=pAL->Stack(); if(tid==-1){ //print all tids for this event for(Int_t i=0;iGetNtrack();i++) pStack->Particle(i)->Print(); Printf("totally %i tracks including %i primaries for event %i out of %i event(s)", @@ -253,8 +257,8 @@ void Param::idealPosition(Int_t iCh, TGeoHMatrix* pMatrix) while((tid=pTrack->GetFirstMother()) >= 0){ pTrack=pStack->Particle(tid); str+=" from ";str+=pTrack->GetName(); - } - }//if(tid==-1) + } + }//if(tid==-1) }//events loop pAL->UnloadHeader(); pAL->UnloadKinematics(); return mtid; @@ -263,15 +267,15 @@ void Param::idealPosition(Int_t iCh, TGeoHMatrix* pMatrix) /*Int_t Param::StackCount(Int_t pid,Int_t evt) { // Counts total number of particles of given sort (including secondary) for a given event - AliRunLoader *pAL=AliRunLoader::Open(); - pAL->GetEvent(evt); + AliRunLoader *pAL=AliRunLoader::Open(); + pAL->GetEvent(evt); if(pAL->LoadHeader()) return 0; if(pAL->LoadKinematics()) return 0; AliStack *pStack=pAL->Stack(); - + Int_t iCnt=0; for(Int_t i=0;iGetNtrack();i++) if(pStack->Particle(i)->GetPdgCode()==pid) iCnt++; - + pAL->UnloadHeader(); pAL->UnloadKinematics(); return iCnt; }*/ @@ -290,7 +294,7 @@ double Param::sigma2(double trkTheta, double trkPhi, double ckovTh, double ckovP double trkBeta = 1. / (TMath::Cos(ckovTh) * getRefIdx()); if (trkBeta > 1) { - trkBeta = 1; //protection against bad measured thetaCer + trkBeta = 1; // protection against bad measured thetaCer } if (trkBeta < 0) { trkBeta = 0.0001; // @@ -337,7 +341,7 @@ double Param::sigLoc(double trkTheta, double trkPhi, double thetaC, double phiC, // formula (7) pag.4 double dtdyc = kk * (k * (cosfd * sinf + cost * sinfd * cosf) + (alpha * e / (betaM * betaM)) * sint * sinfd); - double errX = 0.2, errY = 0.25; //end of page 7 + double errX = 0.2, errY = 0.25; // end of page 7 return TMath::Sqrt(errX * errX * dtdxc * dtdxc + errY * errY * dtdyc * dtdyc); } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -365,7 +369,7 @@ double Param::sigCrom(double trkTheta, double trkPhi, double thetaC, double phiC double f = 0.0172 * (7.75 - 5.635) / TMath::Sqrt(24.); return f * dtdn; -} //SigCrom() +} // SigCrom() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ double Param::sigGeom(double trkTheta, double trkPhi, double thetaC, double phiC, double betaM) { @@ -404,7 +408,7 @@ double Param::sigGeom(double trkTheta, double trkPhi, double thetaC, double phiC double trErr = radThick() / (TMath::Sqrt(12.) * cost); return trErr * dtdT; -} //SigGeom() +} // SigGeom() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ double Param::sigmaCorrFact(Int_t iPart, double occupancy) { @@ -437,10 +441,10 @@ Param* Param::instance() // Arguments: none // Returns: pointer to the instance of AliHMPIDParam or 0 if no geometry if (!fgInstance) { - new Param(kFALSE); //default setting for reconstruction, if no geometry.root -> AliFatal + new Param(kFALSE); // default setting for reconstruction, if no geometry.root -> AliFatal } return fgInstance; -} //Instance() +} // Instance() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Param* Param::instanceNoGeo() @@ -449,10 +453,10 @@ Param* Param::instanceNoGeo() // Arguments: none // Returns: pointer to the instance of AliHMPIDParam or 0 if no geometry if (!fgInstance) { - new Param(kTRUE); //to avoid AliFatal, for MOOD and displays, use ideal geometry parameters + new Param(kTRUE); // to avoid AliFatal, for MOOD and displays, use ideal geometry parameters } return fgInstance; -} //Instance() +} // Instance() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bool Param::isInDead(float x, float y) { @@ -461,7 +465,7 @@ bool Param::isInDead(float x, float y) // Returns: 1 if not in sensitive zone for (Int_t iPc = 0; iPc < 6; iPc++) { if (x >= fgkMinPcX[iPc] && x <= fgkMaxPcX[iPc] && y >= fgkMinPcY[iPc] && y <= fgkMaxPcY[iPc]) { - return kFALSE; //in current pc + return kFALSE; // in current pc } } @@ -475,7 +479,7 @@ bool Param::isDeadPad(Int_t padx, Int_t pady, Int_t ch) // Returns: kTRUE if dead, kFALSE if active if (fgMapPad[padx - 1][pady - 1][ch]) { - return kFALSE; //current pad active + return kFALSE; // current pad active } return kTRUE; @@ -490,25 +494,25 @@ void Param::lors2Pad(float x, float y, Int_t& pc, Int_t& px, Int_t& py) if (x > fgkMinPcX[0] && x < fgkMaxPcX[0]) { pc = 0; px = Int_t(x / sizePadX()); - } //PC 0 or 2 or 4 + } // PC 0 or 2 or 4 else if (x > fgkMinPcX[1] && x < fgkMaxPcX[1]) { pc = 1; px = Int_t((x - fgkMinPcX[1]) / sizePadX()); - } //PC 1 or 3 or 5 + } // PC 1 or 3 or 5 else { return; } if (y > fgkMinPcY[0] && y < fgkMaxPcY[0]) { py = Int_t(y / sizePadY()); - } //PC 0 or 1 + } // PC 0 or 1 else if (y > fgkMinPcY[2] && y < fgkMaxPcY[2]) { pc += 2; py = Int_t((y - fgkMinPcY[2]) / sizePadY()); - } //PC 2 or 3 + } // PC 2 or 3 else if (y > fgkMinPcY[4] && y < fgkMaxPcY[4]) { pc += 4; py = Int_t((y - fgkMinPcY[4]) / sizePadY()); - } //PC 4 or 5 + } // PC 4 or 5 else { return; } @@ -516,9 +520,9 @@ void Param::lors2Pad(float x, float y, Int_t& pc, Int_t& px, Int_t& py) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Int_t Param::inHVSector(float y) { - //Calculate the HV sector corresponding to the cluster position - //Arguments: y - //Returns the HV sector in the single module + // Calculate the HV sector corresponding to the cluster position + // Arguments: y + // Returns the HV sector in the single module Int_t hvsec = -1; Int_t pc, px, py; @@ -535,15 +539,15 @@ Int_t Param::inHVSector(float y) double Param::findTemp(double tLow, double tHigh, double y) { // Model for gradient in temperature - double yRad = hinRad(y); //height in a given radiator + double yRad = hinRad(y); // height in a given radiator if (tHigh < tLow) { - tHigh = tLow; //if Tout < Tin consider just Tin as reference... + tHigh = tLow; // if Tout < Tin consider just Tin as reference... } if (yRad < 0) { - yRad = 0; //protection against fake y values + yRad = 0; // protection against fake y values } if (yRad > sizePcY()) { - yRad = sizePcY(); //protection against fake y values + yRad = sizePcY(); // protection against fake y values } double gradT = (tHigh - tLow) / sizePcY(); // linear gradient @@ -552,9 +556,9 @@ double Param::findTemp(double tLow, double tHigh, double y) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::setChStatus(Int_t ch, bool status) { - //Set a chamber on or off depending on the status - //Arguments: ch=chamber,status=kTRUE = active, kFALSE=off - //Returns: none + // Set a chamber on or off depending on the status + // Arguments: ch=chamber,status=kTRUE = active, kFALSE=off + // Returns: none for (Int_t padx = 0; padx < kMaxPcx + 1; padx++) { for (Int_t pady = 0; pady < kMaxPcy + 1; pady++) { fgMapPad[padx][pady][ch] = status; @@ -564,10 +568,10 @@ void Param::setChStatus(Int_t ch, bool status) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::setSectStatus(Int_t ch, Int_t sect, bool status) { - //Set a given sector sect for a chamber ch on or off depending on the status - //Sector=0,5 (6 sectors) - //Arguments: ch=chamber,sect=sector,status: kTRUE = active, kFALSE=off - //Returns: none + // Set a given sector sect for a chamber ch on or off depending on the status + // Sector=0,5 (6 sectors) + // Arguments: ch=chamber,sect=sector,status: kTRUE = active, kFALSE=off + // Returns: none Int_t npadsect = (kMaxPcy + 1) / 6; Int_t padSectMin = npadsect * sect; @@ -582,9 +586,9 @@ void Param::setSectStatus(Int_t ch, Int_t sect, bool status) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::setPcStatus(Int_t ch, Int_t pc, bool status) { - //Set a given PC pc for a chamber ch on or off depending on the status - //Arguments: ch=chamber,pc=PC,status: kTRUE = active, kFALSE=off - //Returns: none + // Set a given PC pc for a chamber ch on or off depending on the status + // Arguments: ch=chamber,pc=PC,status: kTRUE = active, kFALSE=off + // Returns: none Int_t deltaX = pc % 2; Int_t deltaY = pc / 2; @@ -602,9 +606,9 @@ void Param::setPcStatus(Int_t ch, Int_t pc, bool status) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::printChStatus(Int_t ch) { - //Print the map status of a chamber on or off depending on the status - //Arguments: ch=chamber - //Returns: none + // Print the map status of a chamber on or off depending on the status + // Arguments: ch=chamber + // Returns: none Printf(" "); Printf(" --------- C H A M B E R %d ---------------", ch); for (Int_t pady = kMaxPcy; pady >= 0; pady--) { @@ -624,9 +628,9 @@ void Param::printChStatus(Int_t ch) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::setGeomAccept() { - //Set the real acceptance of the modules, due to ineficciency or hardware problems (up tp 1/6/2010) - //Arguments: none - //Returns: none + // Set the real acceptance of the modules, due to ineficciency or hardware problems (up tp 1/6/2010) + // Arguments: none + // Returns: none setSectStatus(0, 3, kFALSE); setSectStatus(4, 0, kFALSE); setSectStatus(5, 1, kFALSE); diff --git a/Detectors/HMPID/calibration/README.md b/Detectors/HMPID/calibration/README.md index 674ff2eca1265..a9a08309bb061 100644 --- a/Detectors/HMPID/calibration/README.md +++ b/Detectors/HMPID/calibration/README.md @@ -1,4 +1,8 @@ -#HMPID calibration + + +# HMPID calibration For a local test, use diff --git a/Detectors/HMPID/calibration/src/HMPIDDCSProcessor.cxx b/Detectors/HMPID/calibration/src/HMPIDDCSProcessor.cxx index 55bfe8fc101e7..02852a3213b30 100644 --- a/Detectors/HMPID/calibration/src/HMPIDDCSProcessor.cxx +++ b/Detectors/HMPID/calibration/src/HMPIDDCSProcessor.cxx @@ -395,7 +395,7 @@ double HMPIDDCSProcessor::calculatePhotonEnergy(int i) if (dp.id.get_type() == DeliveryType::DPVAL_DOUBLE) { lambda = o2::dcs::getValue(dp); } else { - LOGP(warn, "DP type is {}", dp.id.get_type()); + LOGP(warn, "DP type is {}", (int)dp.id.get_type()); LOGP(warn, "Not correct datatype for HMP_TRANPLANT_MEASURE_{}_WAVELENGTH --> Default wavelength used for iteration procTrans{}", i, i); lambda = arrWaveLenDefault[i]; } @@ -708,6 +708,7 @@ std::unique_ptr HMPIDDCSProcessor::finalizeHv(int iCh, int iSec) } else { (pHvTF).reset(new TF1(Form("HV%i%i", iCh, iSec), "[0]+x*[1]", hvFirstTime, hvLastTime)); + pGrHV->Fit(Form("HV%i%i", iCh, iSec), "Q"); // ef added } } else { LOGP(warn, "No entries in High Voltage for HV{}{}", iCh, iSec); diff --git a/Detectors/HMPID/reconstruction/CMakeLists.txt b/Detectors/HMPID/reconstruction/CMakeLists.txt index c56e087ba555d..9a4623fd347d2 100644 --- a/Detectors/HMPID/reconstruction/CMakeLists.txt +++ b/Detectors/HMPID/reconstruction/CMakeLists.txt @@ -10,16 +10,20 @@ # or submit itself to any jurisdiction. o2_add_library(HMPIDReconstruction - SOURCES src/Clusterer.cxx - src/HmpidDecoder2.cxx + TARGETVARNAME targetName + SOURCES src/Clusterer.cxx + src/HmpidDecoder2.cxx src/HmpidEquipment.cxx src/CTFHelper.cxx + src/Recon.cxx src/CTFCoder.cxx PUBLIC_LINK_LIBRARIES O2::HMPIDBase O2::DataFormatsHMP + ROOT::Physics O2::HMPIDSimulation) - + o2_target_root_dictionary(HMPIDReconstruction HEADERS include/HMPIDReconstruction/Clusterer.h include/HMPIDReconstruction/HmpidDecoder2.h + include/HMPIDReconstruction/Recon.h include/HMPIDReconstruction/HmpidEquipment.h) diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h index d1c825f603e83..894c11864f061 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h @@ -23,7 +23,6 @@ #include "DataFormatsHMP/CTF.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/CTFCoderBase.h" -#include "rANS/rans.h" #include "HMPIDReconstruction/CTFHelper.h" class TTree; @@ -33,10 +32,10 @@ namespace o2 namespace hmpid { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::HMP) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::HMP, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF @@ -85,14 +84,14 @@ o2::ctf::CTFIOSize CTFCoder::encode_impl(VEC& buff, const gsl::spansetHeader(helper.createHeader()); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODEHMP(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODEHMP(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODEHMP(helper.begin_bcIncTrig(), helper.end_bcIncTrig(), CTF::BLC_bcIncTrig, 0); iosize += ENCODEHMP(helper.begin_orbitIncTrig(), helper.end_orbitIncTrig(), CTF::BLC_orbitIncTrig, 0); @@ -136,12 +134,14 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VDIG& di auto header = ec.getHeader(); checkDictVersion(static_cast(header)); ec.print(getPrefix(), mVerbosity); - std::vector bcInc, q; - std::vector orbitInc, entriesDig; + std::vector bcInc; + std::vector orbitInc; + std::vector q; + std::vector entriesDig; std::vector chID, ph, x, y; o2::ctf::CTFIOSize iosize; -#define DECODEHMP(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODEHMP(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODEHMP(bcInc, CTF::BLC_bcIncTrig); iosize += DECODEHMP(orbitInc, CTF::BLC_orbitIncTrig); diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFHelper.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFHelper.h index 094aa7f367b3f..8e7c1823a0340 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFHelper.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFHelper.h @@ -60,7 +60,7 @@ class CTFHelper class _Iter { public: - using difference_type = int64_t; + using difference_type = std::ptrdiff_t; using value_type = T; using pointer = const T*; using reference = const T&; @@ -69,92 +69,86 @@ class CTFHelper _Iter(const gsl::span& data, bool end = false) : mData(data), mIndex(end ? M * data.size() : 0){}; _Iter() = default; - const I& operator++() + inline I& operator++() noexcept { ++mIndex; - return (I&)(*this); + return static_cast(*this); } - const I operator++(int) + inline I operator++(int) { - auto res = *this; + I res = *(static_cast(this)); ++mIndex; return res; } - const I& operator--() + inline I& operator--() noexcept { mIndex--; - return (I&)(*this); + return static_cast(*this); } - const I operator--(int) + inline I operator--(int) { - auto res = *this; + I res = *(static_cast(this)); --mIndex; return res; } - const I& operator+=(difference_type i) + I& operator+=(difference_type i) noexcept { mIndex += i; - return (I&)(*this); + return static_cast(*this); } - const I operator+=(difference_type i) const + I operator+(difference_type i) const { - auto tmp = *const_cast(this); - return tmp += i; + I res = *(const_cast(static_cast(this))); + return res += i; } - const I& operator-=(difference_type i) + I& operator-=(difference_type i) noexcept { mIndex -= i; - return (I&)(*this); + return static_cast(*this); } - const I operator-=(difference_type i) const + I operator-(difference_type i) const { - auto tmp = *const_cast(this); - return tmp -= i; + I res = *(const_cast(static_cast(this))); + return res -= i; } - difference_type operator-(const I& other) const { return mIndex - other.mIndex; } + difference_type operator-(const I& other) const noexcept { return mIndex - other.mIndex; } - difference_type operator-(size_t idx) const { return mIndex - idx; } + inline friend I operator+(difference_type i, const I& iter) { return iter + i; }; - const I& operator-(size_t idx) - { - mIndex -= idx; - return (I&)(*this); - } - - bool operator!=(const I& other) const { return mIndex != other.mIndex; } - bool operator==(const I& other) const { return mIndex == other.mIndex; } - bool operator>(const I& other) const { return mIndex > other.mIndex; } - bool operator<(const I& other) const { return mIndex < other.mIndex; } - bool operator>=(const I& other) const { return mIndex >= other.mIndex; } - bool operator<=(const I& other) const { return mIndex <= other.mIndex; } + bool operator!=(const I& other) const noexcept { return mIndex != other.mIndex; } + bool operator==(const I& other) const noexcept { return mIndex == other.mIndex; } + bool operator>(const I& other) const noexcept { return mIndex > other.mIndex; } + bool operator<(const I& other) const noexcept { return mIndex < other.mIndex; } + bool operator>=(const I& other) const noexcept { return mIndex >= other.mIndex; } + bool operator<=(const I& other) const noexcept { return mIndex <= other.mIndex; } protected: gsl::span mData{}; - size_t mIndex = 0; + difference_type mIndex = 0; }; //_______________________________________________ // BC difference wrt previous if in the same orbit, otherwise the abs.value. // For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_bcIncTrig : public _Iter + class Iter_bcIncTrig : public _Iter { public: - using _Iter::_Iter; + using _Iter::_Iter; value_type operator*() const { if (mIndex) { if (mData[mIndex].getOrbit() == mData[mIndex - 1].getOrbit()) { - return mData[mIndex].getBc() - mData[mIndex - 1].getBc(); + return value_type(mData[mIndex].getBc() - mData[mIndex - 1].getBc()); } else { - return mData[mIndex].getBc(); + return value_type(mData[mIndex].getBc()); } } return 0; @@ -164,9 +158,9 @@ class CTFHelper size_t id = mIndex + i; if (id) { if (mData[id].getOrbit() == mData[id - 1].getOrbit()) { - return mData[id].getBc() - mData[id - 1].getBc(); + return value_type(mData[id].getBc() - mData[id - 1].getBc()); } else { - return mData[id].getBc(); + return value_type(mData[id].getBc()); } } return 0; @@ -175,15 +169,15 @@ class CTFHelper //_______________________________________________ // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_orbitIncTrig : public _Iter + class Iter_orbitIncTrig : public _Iter { public: - using _Iter::_Iter; - value_type operator*() const { return mIndex ? mData[mIndex].getOrbit() - mData[mIndex - 1].getOrbit() : 0; } + using _Iter::_Iter; + value_type operator*() const { return value_type(mIndex ? mData[mIndex].getOrbit() - mData[mIndex - 1].getOrbit() : 0); } value_type operator[](difference_type i) const { size_t id = mIndex + i; - return id ? mData[id].getOrbit() - mData[id - 1].getOrbit() : 0; + return value_type(id ? mData[id].getOrbit() - mData[id - 1].getOrbit() : 0); } }; diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Clusterer.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Clusterer.h index 35ee3e311f363..fb2fabfd9ce8c 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Clusterer.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Clusterer.h @@ -22,6 +22,8 @@ #include "SimulationDataFormat/MCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" +#include "TMatrixF.h" // ef: added + namespace o2 { @@ -47,7 +49,7 @@ class Clusterer static void Dig2Clu(gsl::span digs, std::vector& clus, float* pUserCut, bool isUnfold = kTRUE); // digits->clusters static void FormClu(Cluster& pClu, int pDig, gsl::span digs, TMatrixF& pDigMap); // cluster formation recursive algorithm static int UseDig(int padX, int padY, TMatrixF& pDigMap); // use this pad's digit to form a cluster - inline bool IsDigSurvive(Digit* pDig) const; // check for sigma cut + inline bool IsDigSurvive(Digit* pDig) const; // check for sigma cut private: // void processChamber(std::vector& clusters, MCLabelContainer const* digitMCTruth); diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h deleted file mode 100644 index e92e8375ad0d0..0000000000000 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawFile.h -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data File stream -/// \version 1.0 -/// \date 24 set 2020 - -#ifndef COMMON_HMPIDDECODERAWFILE_H_ -#define COMMON_HMPIDDECODERAWFILE_H_ - -#include -#include -#include -#include -#include -#include - -#include "HMPIDReconstruction/HmpidDecoder.h" - -#define MAXFILENAMEBUFFER 512 -#define MAXRAWFILEBUFFER RAWBLOCKDIMENSION_W * 4 + 8 - -namespace o2 -{ -namespace hmpid -{ - -class HmpidDecodeRawFile : public HmpidDecoder -{ - public: - HmpidDecodeRawFile(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawFile(int numOfEquipments); - ~HmpidDecodeRawFile(); - - bool setUpStream(void* InpuFileName, long Size); - - private: - bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size); - bool getHeaderFromStream(uint32_t** streamPtr); - bool getWordFromStream(uint32_t* word); - int fileExists(char* filewithpath); - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge); - - private: - FILE* fh; - char mInputFile[MAXFILENAMEBUFFER]; - uint32_t mFileBuffer[MAXRAWFILEBUFFER]; -}; - -} // namespace hmpid -} // namespace o2 -#endif /* COMMON_HMPIDDECODERAWFILE_H_ */ diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h deleted file mode 100644 index d5d82d0f238e9..0000000000000 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawMem.h -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data Memory stream -/// \version 1.0 -/// \date 24 set 2020 - -#ifndef COMMON_HMPIDDECODERAWMEM_H_ -#define COMMON_HMPIDDECODERAWMEM_H_ - -#include -#include -#include -#include -#include -#include - -#include "DataFormatsHMP/Digit.h" -#include "HMPIDBase/Geo.h" -#include "HMPIDReconstruction/HmpidDecoder.h" - -using namespace o2; - -namespace o2 -{ -namespace hmpid -{ - -class HmpidDecodeRawMem : public HmpidDecoder -{ - public: - HmpidDecodeRawMem(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawMem(int numOfEquipments); - ~HmpidDecodeRawMem(); - - bool setUpStream(void* Buffer, long BufferLen) override; - - private: - bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size) override; - bool getHeaderFromStream(uint32_t** streamPtr) override; - bool getWordFromStream(uint32_t* word) override; - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) override; - - private: -}; - -class HmpidDecodeRawDigit : public HmpidDecodeRawMem -{ - public: - HmpidDecodeRawDigit(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawDigit(int numOfEquipments); - ~HmpidDecodeRawDigit(); - - std::vector mDigits; - - private: - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) override; -}; - -} // namespace hmpid -} // namespace o2 -#endif /* COMMON_HMPIDDECODERAWFILE_H_ */ diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecoder2.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecoder2.h index d8f59ee48ce1b..fb8409e685f83 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecoder2.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecoder2.h @@ -163,6 +163,7 @@ class HmpidDecoder2 public: bool decodeHmpidError(int ErrorField, char* outbuf); void dumpHmpidError(HmpidEquipment* eq, int ErrorField, int mHeBCDI, int mHeORBIT); + void dumpMemory(const void* MemoryStartPtr, std::size_t Dimension); bool isPadWord(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Channel, int* Charge); int decodeHeader(uint32_t* streamPtrAdr, int* EquipIndex); HmpidEquipment* evaluateHeaderContents(int EquipmentIndex); @@ -171,7 +172,6 @@ class HmpidDecoder2 protected: bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size); bool getHeaderFromStream(uint32_t** streamPtr); - bool getWordFromStream(uint32_t* word); uint32_t readWordFromStream(); uint32_t* getActualStreamPtr() { diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidEquipment.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidEquipment.h index 185dc653b22a2..1482e0c9a921a 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidEquipment.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidEquipment.h @@ -47,7 +47,7 @@ const int ERR_WRONGSIZESEGMENTMARK = 10; const int ERR_LOSTEOSMARK = 11; const int ERR_HMPID = 12; -// ---- HMPID error def ------- +// ---- HMPID TRY errors def ------- const int TH_FILENOTEXISTS = 9; const int TH_OPENFILE = 8; const int TH_CREATEFILE = 7; @@ -59,6 +59,7 @@ const int TH_WRONGFILELEN = 14; const int TH_NULLBUFFERPOINTER = 13; const int TH_BUFFEREMPTY = 12; const int TH_WRONGBUFFERDIM = 11; +const int TH_BUFFERPOINTERTOEND = 16; const uint64_t OUTRANGEEVENTNUMBER = 0x1FFFFFFFFFFF; diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Recon.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Recon.h new file mode 100644 index 0000000000000..cd976d3c14a7a --- /dev/null +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Recon.h @@ -0,0 +1,194 @@ +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_HMPID_RECON_H +#define ALICEO2_HMPID_RECON_H + +#include //base class + +#include +#include "Math/GenVector/RotationX.h" +#include "Math/GenVector/RotationY.h" +#include "Math/GenVector/RotationZ.h" + +#include +#include "Math/Vector3D.h" + +#include +#include + +#include + +#include "HMPIDBase/Param.h" +#include "DataFormatsHMP/Cluster.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/MatchInfoHMP.h" + +class Param; + +// using Polar3DVector = ROOT::Math::Polar3DVector; +using MatchInfo = o2::dataformats::MatchInfoHMP; +using TrackParCov = o2::track::TrackParCov; + +namespace o2 +{ +namespace hmpid +{ +class Recon : public TNamed +{ + public: + // ef : moved Recon(): ctor from .cxx file + // Recon() = default; + + Recon() : TNamed("RichRec", "RichPat"), // ef : moved from cxx + fPhotCnt(-1), + fPhotFlag(0x0), + fPhotClusIndex(0x0), + fPhotCkov(0x0), + fPhotPhi(0x0), + fPhotWei(0x0), + fCkovSigma2(0), + fIsWEIGHT(kFALSE), + fDTheta(0.001), + fWindowWidth(0.045), + fRingArea(0), + fRingAcc(0), + fTrkDir(0, 0, 1), // Just for test + fTrkPos(30, 40), // Just for test + fMipPos(0, 0), + fPc(0, 0), + fParam(o2::hmpid::Param::instance()) + // fParam(Param::instance()) + { + //.. + // init of data members + //.. + + fParam->setRefIdx(fParam->meanIdxRad()); // initialization of ref index to a default one // ef:ok + } + + //~Recon() = default; + virtual ~Recon() { ; } // dtor + + // ef : methods in these classeS? + void initVars(int n); // init space for variables + + // ef : commented out: no need to delete variables when smart-pointer + // void deleteVars() const; // delete variables + + // void CkovAngle (AliESDtrack *pTrk,TClonesArray *pCluLst,int index,double nmean,float xRa,float yRa ); + void ckovAngle(o2::dataformats::MatchInfoHMP* match, const std::vector clusters, int index, double nmean, float xRa, float yRa); // reconstructed Theta Cerenkov + + bool findPhotCkov(double cluX, double cluY, double& thetaCer, double& phiCer); // find ckov angle for single photon candidate + bool findPhotCkov2(double cluX, double cluY, double& thetaCer, double& phiCer); + double findRingCkov(int iNclus); // best ckov for ring formed by found photon candidates + void findRingGeom(double ckovAng, int level = 1); // estimated area of ring in cm^2 and portion accepted by geometry + + // template + const TVector2 intWithEdge(TVector2 p1, TVector2 p2); // find intercection between plane and lines of 2 thetaC + + int flagPhot(double ckov, const std::vector clusters, float* photChargeVec); // is photon ckov near most probable track ckov + // int flagPhot(double ckov, const std::vector clusters); // is photon ckov near most probable track ckov + + double houghResponse(); // most probable track ckov angle + // template + void propagate(const TVector3 dir, TVector3& pos, double z) const; // propagate photon alogn the line + // void refract(math_utils::Vector3D& dir, double n1, double n2) const; // refract photon on the boundary + + // template + void refract(TVector3& dir, double n1, double n2) const; // + + TVector2 tracePhot(double ckovTh, double ckovPh) const; // trace photon created by track to PC + + // ef : commented out addObjectToFriends + // void addObjectToFriends(TClonesArray *pCluLst, int photonIndex, AliESDtrack *pTrk ); // Add AliHMPIDCluster object to ESD friends + // template + TVector2 traceForward(TVector3 dirCkov) const; // tracing forward a photon from (x,y) to PC + void lors2Trs(TVector3 dirCkov, double& thetaCer, double& phiCer) const; // LORS to TRS + void trs2Lors(TVector3 dirCkov, double& thetaCer, double& phiCer) const; // TRS to LORS + + TVector2 getMip() const + { + return fMipPos; + } // mip coordinates + + double getRingArea() const + { + return fRingArea; + } // area of the current ring in cm^2 + + double getRingAcc() const + { + return fRingAcc; + } // portion of the ring ([0,1]) accepted by geometry.To scale n. of photons + double findRingExt(double ckov, int ch, double xPc, double yPc, double thRa, double phRa); // find ring acceptance by external parameters + + void setTrack(double xRad, double yRad, double theta, double phi) + { + fTrkDir.SetMagThetaPhi(1., theta, phi); + fTrkPos.Set(xRad, yRad); + } // set track parameter at RAD + + void setImpPC(double xPc, double yPc) + { + fPc.Set(xPc, yPc); + } // set track impact to PC + + void setMip(double xmip, double ymip) + { + fMipPos.Set(xmip, ymip); + } // set track impact to PC + + enum eTrackingFlags { kNotPerformed = -20, + kMipDistCut = -9, + kMipQdcCut = -5, + kNoPhotAccept = -11, + kNoRad = -22 }; + // + protected: + int fPhotCnt; // counter of photons candidate + + // ef : changed to smart-pointer arrays + std::unique_ptr fPhotFlag; // flags of photon candidates + std::unique_ptr fPhotClusIndex; // cluster index of photon candidates + std::unique_ptr fPhotCkov; // Ckov angles of photon candidates, [rad] + std::unique_ptr fPhotPhi; // phis of photons candidates, [rad] + std::unique_ptr fPhotWei; // weigths of photon candidates + + // int *fPhotClusIndex; // cluster index of photon candidates + + double fCkovSigma2; // sigma2 of the reconstructed ring + + bool fIsWEIGHT; // flag to consider weight procedure + float fDTheta; // Step for sliding window + float fWindowWidth; // Hough width of sliding window + + double fRingArea; // area of a given ring + double fRingAcc; // fraction of the ring accepted by geometry + + TVector3 fTrkDir; // track direction in LORS at RAD + + TVector2 fTrkPos; // track positon in LORS at RAD // XY mag + TVector2 fMipPos; // mip positon for a given trackf // XY + TVector2 fPc; // track position at PC // XY + + o2::hmpid::Param* fParam = o2::hmpid::Param::instance(); // Pointer to HMPIDParam + + private: + Recon(const Recon& r); // dummy copy constructor + Recon& operator=(const Recon& r); // dummy assignment operator + // + ClassDef(Recon, 3) +}; + +} // namespace hmpid +} // namespace o2 +#endif diff --git a/Detectors/HMPID/reconstruction/src/CTFCoder.cxx b/Detectors/HMPID/reconstruction/src/CTFCoder.cxx index 72fbfd66180ee..c09a4c924cfe9 100644 --- a/Detectors/HMPID/reconstruction/src/CTFCoder.cxx +++ b/Detectors/HMPID/reconstruction/src/CTFCoder.cxx @@ -41,11 +41,13 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); // just to get types - uint16_t bcInc, HCIDTrk, q; - uint32_t orbitInc, entriesDig; + int16_t bcInc; + int32_t orbitInc; + uint16_t HCIDTrk, q; + uint32_t entriesDig; uint8_t chID, ph, x, y; -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(bcInc, CTF::BLC_bcIncTrig); MAKECODER(orbitInc, CTF::BLC_orbitIncTrig); diff --git a/Detectors/HMPID/reconstruction/src/Clusterer.cxx b/Detectors/HMPID/reconstruction/src/Clusterer.cxx index 9ddacbb3de005..d2cbcf770f749 100644 --- a/Detectors/HMPID/reconstruction/src/Clusterer.cxx +++ b/Detectors/HMPID/reconstruction/src/Clusterer.cxx @@ -12,7 +12,7 @@ /// \file Clusterer.cxx /// \brief Implementation of the HMPID cluster finder #include -#include // for LOG +#include "FairLogger.h" // for LOG #include "Framework/Logger.h" #include "HMPIDBase/Param.h" #include "DataFormatsHMP/Cluster.h" @@ -39,6 +39,52 @@ void Clusterer::Dig2Clu(gsl::span digs, std::vector vPad; + std::vector digVec; + for (int iCh = Param::kMinCh; iCh <= Param::kMaxCh; iCh++) { // chambers loop + padMap = (Float_t)-1; // reset map to -1 (means no digit for this pad) + for (size_t iDig = 0; iDig < digs.size(); iDig++) { + o2::hmpid::Digit::pad2Absolute(digs[iDig].getPadID(), &module, &padChX, &padChY); + vPad.push_back({padChX, padChY, module}); + if (module == iCh) { + padMap(padChX, padChY) = iDig; // fill the map for the given chamber, (padx,pady) cell takes digit index + } + } // digits loop for current chamber + + for (size_t iDig = 0; iDig < digs.size(); iDig++) { // digits loop for current chamber + // o2::hmpid::Digit::pad2Absolute(digs[iDig].getPadID(), &module, &padChX, &padChY); + if (vPad.at(iDig).m != iCh || (pUsedDig = UseDig(vPad.at(iDig).x, vPad.at(iDig).y, padMap)) == -1) { // this digit is from other module or already taken in FormClu(), go after next digit + continue; + } + digVec.clear(); + Cluster clu; + clu.setDigits(&digVec); + clu.setCh(iCh); + FormClu(clu, pUsedDig, digs, padMap); // form cluster starting from this digit by recursion + clu.solve(&clus, pUserCut, isUnfold); // solve this cluster and add all unfolded clusters to provided list + } // digits loop for current chamber + vPad.clear(); + } // chambers loop + return; +} // Dig2Clu() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/*void Clusterer::Dig2Clu(gsl::span digs, std::vector& clus, float* pUserCut, bool isUnfold) +{ + // Finds all clusters for a given digits list provided not empty. Currently digits list is a list of all digits for a single chamber. + // Puts all found clusters in separate lists, one per clusters. + // Arguments: pDigAll - list of digits for all chambers + // pCluAll - list of clusters for all chambers + // isTryUnfold - flag to choose between CoG and Mathieson fitting + // Returns: none + TMatrixF padMap(Param::kMinPx, Param::kMaxPcx, Param::kMinPy, Param::kMaxPcy); // pads map for single chamber 0..159 x 0..143 int pUsedDig = -1; @@ -63,16 +109,17 @@ void Clusterer::Dig2Clu(gsl::span digs, std::vector digs, TMatrixF& pDigMap) { // Forms the initial cluster as a combination of all adjascent digits. Starts from the given digit then calls itself recursevly for all neighbours. // Arguments: pClu - pointer to cluster being formed // Returns: none + pClu.digAdd(&digs[pDig]); // take this digit in cluster int cnt = 0; int cx[4]; diff --git a/Detectors/HMPID/reconstruction/src/HMPIDReconstructionLinkDef.h b/Detectors/HMPID/reconstruction/src/HMPIDReconstructionLinkDef.h index 1fa77ff6676f2..af7466d30c2e5 100644 --- a/Detectors/HMPID/reconstruction/src/HMPIDReconstructionLinkDef.h +++ b/Detectors/HMPID/reconstruction/src/HMPIDReconstructionLinkDef.h @@ -18,6 +18,7 @@ //#pragma link C++ class o2::hmpid::DataReader + ; #pragma link C++ class o2::hmpid::Clusterer + ; //#pragma link C++ class o2::hmpid::HmpidDecodeRawMem + ; +#pragma link C++ class o2::hmpid::Recon + ; //#pragma link C++ class o2::hmpid::HmpidDecoder + ; #pragma link C++ class o2::hmpid::HmpidDecoder2 + ; #pragma link C++ class o2::hmpid::HmpidEquipment + ; diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx deleted file mode 100644 index df97a4d2101e0..0000000000000 --- a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawFile.cxx -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data File stream -/// \version 1.0 -/// \date 24 set 2020 - -/* ------ HISTORY --------- -*/ -#include // for LOG -#include "Framework/Logger.h" - -#include "HMPIDReconstruction/HmpidDecodeRawFile.h" - -using namespace o2::hmpid; - -/// Constructor with the default HMPID equipments map at P2 -/// @param[in] numOfEquipments : number of defined equipments [0..13] -HmpidDecodeRawFile::HmpidDecodeRawFile(int numOfEquipments) - : HmpidDecoder(numOfEquipments) -{ - fh = 0; -} - -/// Constructor with the HMPID address map -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawFile::HmpidDecodeRawFile(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecoder(EqIds, CruIds, LinkIds, numOfEquipments) -{ - fh = 0; -} - -/// Destructor -HmpidDecodeRawFile::~HmpidDecodeRawFile() -{ -} - -/// Setup the Input Stream with a File Handle -/// verify the existence and try to open it -/// @param[in] *FileName : the string that contains the File Name -/// @param[in] Size : not used -/// @returns True if the file is opened -/// @throws TH_FILENOTEXISTS Thrown if the file doesn't exists -/// @throws TH_OPENFILE Thrown if Fails to open the file -bool HmpidDecodeRawFile::setUpStream(void* FileName, long Size) -{ - strcpy(mInputFile, (const char*)FileName); - // files section ---- - if (!fileExists(mInputFile)) { - LOG(error) << "The input file " << mInputFile << " does not exist at this time."; - throw TH_FILENOTEXISTS; - } - // open the file - fh = fopen(mInputFile, "rb"); - if (fh == 0) { - LOG(error) << "ERROR to open Input file ! [" << mInputFile << "]"; - throw TH_OPENFILE; - } - - mActualStreamPtr = 0; // sets the pointer to the Buffer - mEndStreamPtr = 0; //sets the End of buffer - mStartStreamPtr = 0; - - return (true); -} - -/// Gets a sized chunk from the stream. Read from the file and update the pointers -/// ATTENTION : in order to optimize the disk accesses the block read pre-load a -/// complete Header+Payload block, the Size parameter is recalculated with the -/// dimension of the pack extract from the header field 'Offeset' -/// -/// verify the existence and try to open it -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @param[in] Size : not used -/// @returns True if the file is opened -/// @throws TH_WRONGFILELEN Thrown if the file doesn't contains enough words -bool HmpidDecodeRawFile::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) -{ - if (Size > MAXRAWFILEBUFFER) - return (false); - int nr = fread(mFileBuffer, sizeof(int32_t), HEADERDIMENSION_W, fh); - if (nr != HEADERDIMENSION_W) { - throw TH_WRONGFILELEN; - } - Size = ((mFileBuffer[2] & 0x0000FFFF) / sizeof(int32_t)) - HEADERDIMENSION_W; - nr = fread(mFileBuffer + HEADERDIMENSION_W, sizeof(int32_t), Size, fh); - LOG(debug) << " getBlockFromStream read " << nr << " of " << Size + HEADERDIMENSION_W << " words !"; - if (nr != Size) { - throw TH_WRONGFILELEN; - } - *streamPtr = mFileBuffer; - mStartStreamPtr = mFileBuffer; - mActualStreamPtr = mFileBuffer; - mEndStreamPtr = mFileBuffer + Size; - return (true); -} - -/// Reads the Header from the file -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @returns True if the header is read -bool HmpidDecodeRawFile::getHeaderFromStream(uint32_t** streamPtr) -{ - bool flag = getBlockFromStream(streamPtr, RAWBLOCKDIMENSION_W); // reads the 8k block - mActualStreamPtr += HEADERDIMENSION_W; // Move forward for the first word - return (flag); -} - -/// Read one word from the pre-load buffer -/// @param[in] *word : the buffer for the read word -/// @returns True every time -bool HmpidDecodeRawFile::getWordFromStream(uint32_t* word) -{ - *word = *mActualStreamPtr; - mActualStreamPtr++; - return (true); -} - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawFile::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - return; -} - -/// Checks if the file exists ! -/// @param[in] *filewithpath : the File Name to check -/// @returns True if the file exists -int HmpidDecodeRawFile::fileExists(char* filewithpath) -{ - if (access(filewithpath, F_OK) != -1) { - return (true); - } else { - return (false); - } -} -o2::hmpid::Digit diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx deleted file mode 100644 index 5a4f2acbfd97b..0000000000000 --- a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawMem.cxx -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data Memory stream -/// \version 1.0 -/// \date 24 set 2020 - -/* ------ HISTORY --------- -*/ -#include // for LOG -#include "Framework/Logger.h" - -#include "DataFormatsHMP/Digit.h" -#include "HMPIDBase/Geo.h" -#include "HMPIDReconstruction/HmpidDecodeRawMem.h" - -using namespace o2::hmpid; - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecodeRawMem::HmpidDecodeRawMem(int numOfEquipments) - : HmpidDecoder(numOfEquipments) -{ -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawMem::HmpidDecodeRawMem(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecoder(EqIds, CruIds, LinkIds, numOfEquipments) -{ -} - -/// Destructor -HmpidDecodeRawMem::~HmpidDecodeRawMem() = default; - -/// Setup the Input Stream with a Memory Pointer -/// the buffer length is in byte, some controls are done -/// -/// @param[in] *Buffer : the pointer to Memory buffer -/// @param[in] BufferLen : the length of the buffer (bytes) -/// @returns True if the stream is set -/// @throws TH_NULLBUFFERPOINTER Thrown if the pointer to the buffer is NULL -/// @throws TH_BUFFEREMPTY Thrown if the buffer is empty -/// @throws TH_WRONGBUFFERDIM Thrown if the buffer len is less then one header -bool HmpidDecodeRawMem::setUpStream(void* Buffer, long BufferLen) -{ - long wordsBufferLen = BufferLen / (sizeof(int32_t) / sizeof(char)); // Converts the len in words - if (Buffer == nullptr) { - LOG(error) << "Raw data buffer null Pointer ! "; - throw TH_NULLBUFFERPOINTER; - } - if (wordsBufferLen == 0) { - LOG(error) << "Raw data buffer Empty ! "; - throw TH_BUFFEREMPTY; - } - if (wordsBufferLen < 16) { - LOG(error) << "Raw data buffer less then the Header Dimension = " << wordsBufferLen; - throw TH_WRONGBUFFERDIM; - } - - mActualStreamPtr = (uint32_t*)Buffer; // sets the pointer to the Buffer - mEndStreamPtr = ((uint32_t*)Buffer) + wordsBufferLen; //sets the End of buffer - mStartStreamPtr = ((uint32_t*)Buffer); - // std::cout << " setUpStrem : StPtr=" << mStartStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << wordsBufferLen << std::endl; - return (true); -} - -/// Gets a sized chunk from the stream. The stream pointers members are updated -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @param[in] Size : the dimension of the chunk (words) -/// @returns True every time -/// @throw TH_WRONGBUFFERDIM Buffer length shorter then the requested -bool HmpidDecodeRawMem::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) -{ - *streamPtr = mActualStreamPtr; - mActualStreamPtr += Size; - if (mActualStreamPtr > mEndStreamPtr) { - // std::cout << " getBlockFromStream : StPtr=" << mActualStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << Size << std::endl; - // std::cout << "Beccato " << std::endl; - // throw TH_WRONGBUFFERDIM; - return (false); - } - return (true); -} - -/// Gets the Header Block from the stream. -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @returns True if the header is read -bool HmpidDecodeRawMem::getHeaderFromStream(uint32_t** streamPtr) -{ - return (getBlockFromStream(streamPtr, mRDHSize)); -} - -/// Gets a Word from the stream. -/// @param[in] *word : the buffer for the read word -/// @returns True if the operation end well -bool HmpidDecodeRawMem::getWordFromStream(uint32_t* word) -{ - uint32_t* appo; - *word = *mActualStreamPtr; - return (getBlockFromStream(&appo, 1)); -} - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawMem::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - return; -} - -// ======================================================================================== - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecodeRawDigit::HmpidDecodeRawDigit(int numOfEquipments) - : HmpidDecodeRawMem(numOfEquipments) -{ -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawDigit::HmpidDecodeRawDigit(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecodeRawMem(EqIds, CruIds, LinkIds, numOfEquipments) -{ -} - -/// Destructor -HmpidDecodeRawDigit::~HmpidDecodeRawDigit() = default; - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawDigit::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - mDigits.push_back(o2::hmpid::Digit(charge, eq->getEquipmentId(), col, dil, ch)); - //std::cout << "DI " << mDigits.back() << " "< // for LOG -#include "Framework/Logger.h" -#include "Headers/RAWDataHeader.h" -#include "HMPIDReconstruction/HmpidDecoder.h" -#include "DataFormatsHMP/Digit.h" - -using namespace o2::hmpid; - -// ============= HmpidDecoder Class implementation ======= - -/// Decoding Error Messages Definitions -char HmpidDecoder::sErrorDescription[MAXERRORS][MAXDESCRIPTIONLENGHT] = {"Word that I don't known !", - "Row Marker Word with 0 words", "Duplicated Pad Word !", "Row Marker Wrong/Lost -> to EoE", - "Row Marker Wrong/Lost -> to EoE", "Row Marker reports an ERROR !", "Lost EoE Marker !", "Double EoE marker", - "Wrong size definition in EoE Marker", "Double Mark Word", "Wrong Size in Segment Marker", "Lost EoS Marker !", - "HMPID Header Errors"}; - -/// HMPID Firmware Error Messages Definitions -char HmpidDecoder::sHmpidErrorDescription[MAXHMPIDERRORS][MAXDESCRIPTIONLENGHT] = { - "L0 Missing," - "L1 is received without L0", - "L1A signal arrived before the L1 Latency", "L1A signal arrived after the L1 Latency", - "L1A is missing or L1 timeout", "L1A Message is missing or L1 Message"}; - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecoder::HmpidDecoder(int numOfEquipments) -{ - // The standard definition of HMPID equipments at P2 - int EqIds[] = {0, 1, 2, 3, 4, 5, 8, 9, 6, 7, 10, 11, 12, 13}; - int CruIds[] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3}; - int LinkIds[] = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2}; - - mNumberOfEquipments = numOfEquipments; - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i] = new HmpidEquipment(EqIds[i], CruIds[i], LinkIds[i]); - } -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecoder::HmpidDecoder(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) -{ - mNumberOfEquipments = numOfEquipments; - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i] = new HmpidEquipment(EqIds[i], CruIds[i], LinkIds[i]); - } -} - -/// Destructor : remove the Equipments instances -HmpidDecoder::~HmpidDecoder() -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - delete mTheEquipments[i]; - } -} - -/// Init all the members variables. -void HmpidDecoder::init() -{ - mRDHSize = sizeof(o2::header::RAWDataHeader) / sizeof(uint32_t); - - mVerbose = 0; - mHeEvent = 0; - mHeBusy = 0; - mNumberWordToRead = 0; - mPayloadTail = 0; - - mHeFEEID = 0; - mHeSize = 0; - mHeVer = 0; - mHePrior = 0; - mHeStop = 0; - mHePages = 0; - mEquipment = 0; - - mHeOffsetNewPack = 0; - mHeMemorySize = 0; - - mHeDetectorID = 0; - mHeDW = 0; - mHeCruID = 0; - mHePackNum = 0; - mHePAR = 0; - mHePageNum = 0; - mHeLinkNum = 0; - mHeFirmwareVersion = 0; - mHeHmpidError = 0; - mHeBCDI = 0; - mHeORBIT = 0; - mHeTType = 0; - - mActualStreamPtr = nullptr; - mEndStreamPtr = nullptr; - mStartStreamPtr = nullptr; - - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - } -} - -/// Returns the Equipment Index (Pointer of the array) converting -/// the FLP hardware coords (CRU_Id and Link_Id) -/// @param[in] CruId : the CRU ID [0..3] -> FLP 160 = [0,1] FLP 161 = [2,3] -/// @param[in] LinkId : the Link ID [0..3] -/// @returns EquipmentIndex : the index in the Equipment array [0..13] (-1 := error) -int HmpidDecoder::getEquipmentIndex(int CruId, int LinkId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId(CruId, LinkId) != -1) { - return (i); - } - } - return (-1); -} - -/// Returns the Equipment Index (Pointer of the array) converting -/// the Equipment_ID (Firmaware defined Id AKA FFEID) -/// @param[in] EquipmentId : the Equipment ID [0..13] -/// @returns EquipmentIndex : the index in the Equipment array [0..13] (-1 := error) -int HmpidDecoder::getEquipmentIndex(int EquipmentId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId() == EquipmentId) { - return (i); - } - } - return (-1); -} - -/// Returns the Equipment_ID converting the FLP hardware coords -/// @param[in] CruId : the CRU ID [0..3] -> FLP 160 = [0,1] FLP 161 = [2,3] -/// @param[in] LinkId : the Link ID [0..3] -/// @returns EquipmentID : the ID of the Equipment [0..13] (-1 := error) -int HmpidDecoder::getEquipmentID(int CruId, int LinkId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId(CruId, LinkId) != -1) { - return (mTheEquipments[i]->getEquipmentId()); - } - } - return (-1); -} - -/// Scans the BitMap of Raw Data File word and detect the type -/// and the parameters -/// @param[in] wp : the word to analyze -/// @param[out] *p1 : first parameter extract (if it exists) -/// @param[out] *p2 : second parameter extract (if it exists) -/// @param[out] *p3 : third parameter extract (if it exists) -/// @param[out] *p4 : fourth parameter extract (if it exists) -/// @returns Type of Word : the type of word [0..4] (0 := undetect) -int HmpidDecoder::checkType(uint32_t wp, int* p1, int* p2, int* p3, int* p4) -{ - if ((wp & 0x0000ffff) == 0x000036A8 || (wp & 0x0000ffff) == 0x000032A8 || (wp & 0x0000ffff) == 0x000030A0 || (wp & 0x0800ffff) == 0x080010A0) { - *p2 = (wp & 0x03ff0000) >> 16; // Number of words of row - *p1 = wp & 0x0000ffff; - return (WTYPE_ROW); - } - if ((wp & 0xfff00000) >> 20 == 0xAB0) { - *p2 = (wp & 0x000fff00) >> 8; // Number of words of Segment - *p1 = (wp & 0xfff00000) >> 20; - *p3 = wp & 0x0000000F; - if (*p3 < 4 && *p3 > 0) { - return (WTYPE_EOS); - } - } - // #EX MASK Raul 0x3803FF80 # ex mask 0xF803FF80 - this is EoE marker 0586800B0 - if ((wp & 0x0803FF80) == 0x08000080) { - *p1 = (wp & 0x07c00000) >> 22; - *p2 = (wp & 0x003C0000) >> 18; - *p3 = (wp & 0x0000007F); - if (*p1 < 25 && *p2 < 11) { - return (WTYPE_EOE); - } - } - if ((wp & 0x08000000) == 0) { // # this is a pad - // PAD:0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value - *p1 = (wp & 0x07c00000) >> 22; - *p2 = (wp & 0x003C0000) >> 18; - *p3 = (wp & 0x0003F000) >> 12; - *p4 = (wp & 0x00000FFF); - if (*p1 > 0 && *p1 < 25 && *p2 > 0 && *p2 < 11 && *p3 < 48) { - return (WTYPE_PAD); - } - } else { - return (WTYPE_NONE); - } - return (WTYPE_NONE); -} - -/// Checks if is a Raw Marker and extract the Row Size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *rowSize : the number of words of the row -/// @param[out] *mark : the row marker -/// @returns True if Row Marker is detected -bool HmpidDecoder::isRowMarker(uint32_t wp, int* Err, int* rowSize, int* mark) -{ - if ((wp & 0x0000ffff) == 0x36A8 || (wp & 0x0000ffff) == 0x32A8 || (wp & 0x0000ffff) == 0x30A0 || (wp & 0x0800ffff) == 0x080010A0) { - *rowSize = (wp & 0x03ff0000) >> 16; // # Number of words of row - *mark = wp & 0x0000ffff; - *Err = false; - return (true); - } else { - *Err = true; - return (false); - } -} - -/// Checks if is a Segment Marker and extracts the Segment number and the size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *segSize : the number of words of the segment -/// @param[out] *Seg : the Segment number [1..3] -/// @param[out] *mark : the Segment Marker -/// @returns True if Segment Marker is detected -bool HmpidDecoder::isSegmentMarker(uint32_t wp, int* Err, int* segSize, int* Seg, int* mark) -{ - *Err = false; - if ((wp & 0xfff00000) >> 20 == 0xAB0) { - *segSize = (wp & 0x000fff00) >> 8; // # Number of words of Segment - *mark = (wp & 0xfff00000) >> 20; - *Seg = wp & 0x0000000F; - if (*Seg > 3 || *Seg < 1) { - LOG(info) << " Wrong segment Marker Word, bad Number of segment" << *Seg << "!"; - *Err = true; - } - return (true); - } else { - return (false); - } -} - -/// Checks if is a PAD Word and extracts all the parameters -/// PAD map : 0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *Col : the column number [1..24] -/// @param[out] *Dilogic : the dilogic number [1..10] -/// @param[out] *Channel : the channel number [0..47] -/// @param[out] *Charge : the value of Charge [0..4095] -/// @returns True if PAD Word is detected -bool HmpidDecoder::isPadWord(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Channel, int* Charge) -{ - *Err = false; - // if ((wp & 0x08000000) != 0) { - if ((wp & 0x08000000) != 0) { - return (false); - } - *Col = (wp & 0x07c00000) >> 22; - *Dilogic = (wp & 0x003C0000) >> 18; - *Channel = (wp & 0x0003F000) >> 12; - *Charge = (wp & 0x00000FFF); - - if ((wp & 0x0ffff) == 0x036A8 || (wp & 0x0ffff) == 0x032A8 || (wp & 0x0ffff) == 0x030A0 || (wp & 0x0ffff) == 0x010A0) { // # ! this is a pad - if (*Dilogic > 10 || *Channel > 47 || *Dilogic < 1 || *Col > 24 || *Col < 1) { - return (false); - } - } else { - if (*Dilogic > 10 || *Channel > 47 || *Dilogic < 1 || *Col > 24 || *Col < 1) { - // LOG(warning) << " Wrong Pad values Col=" << *Col << " Dilogic=" << *Dilogic << " Channel=" << *Channel << " Charge=" << *Charge << " wp:0x" << std::hex << wp << std::dec; - *Err = true; - return (false); - } - } - return (true); -} - -/// Checks if is a EoE Marker and extracts the Column, Dilogic and the size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *Col : the column number [1..24] -/// @param[out] *Dilogic : the dilogic number [1..10] -/// @param[out] *Eoesize : the number of words for dilogic -/// @returns True if EoE marker is detected -bool HmpidDecoder::isEoEmarker(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Eoesize) -{ - *Err = false; - // #EX MASK Raul 0x3803FF80 # ex mask 0xF803FF80 - this is EoE marker 0586800B0 - if ((wp & 0x0803FF80) == 0x08000080) { - *Col = (wp & 0x07c00000) >> 22; - *Dilogic = (wp & 0x003C0000) >> 18; - *Eoesize = (wp & 0x0000007F); - if (*Col > 24 || *Dilogic > 10) { - LOG(info) << " EoE size wrong definition. Col=" << *Col << " Dilogic=" << *Dilogic; - *Err = true; - } - return (true); - } else { - return (false); - } -} - -/// Decode the HMPID error BitMap field (5 bits) and returns true if there are -/// errors and in addition the concat string that contains the error messages -/// ATTENTION : the char * outbuf MUST point to a 250 bytes buffer -/// @param[in] ErrorField : the HMPID Error field -/// @param[out] *outbuf : the output buffer that contains the error description -/// @returns True if EoE marker is detected -bool HmpidDecoder::decodeHmpidError(int ErrorField, char* outbuf) -{ - int res = false; - outbuf[0] = '\0'; - for (int i = 0; i < MAXHMPIDERRORS; i++) { - if ((ErrorField & (0x01 << i)) != 0) { - res = true; - strcat(outbuf, sHmpidErrorDescription[i]); - } - } - return (res); -} - -/// This Decode the Raw Data Header, returns the EquipmentIndex -/// that is obtained with the FLP hardware coords -/// -/// ATTENTION : the 'EquipIndex' parameter and the mEquipment member -/// are different data: the first is the pointer in the Equipments instances -/// array, the second is the FEE_ID number -/// -/// The EVENT_NUMBER : actually is calculated from the ORBIT number -/// -/// @param[in] *streamPtrAdr : the pointer to the Header buffer -/// @param[out] *EquipIndex : the Index to the Equipment Object Array [0..13] -/// @returns True every time -/// @throws TH_WRONGEQUIPINDEX Thrown if the Equipment Index is out of boundary (Equipment not recognized) -int HmpidDecoder::decodeHeader(uint32_t* streamPtrAdr, int* EquipIndex) -{ - uint32_t* buffer = streamPtrAdr; // Sets the pointer to buffer - o2::header::RAWDataHeader* hpt = (o2::header::RAWDataHeader*)buffer; - - /* - mHeFEEID = (buffer[0] & 0x000f0000) >> 16; - mHeSize = (buffer[0] & 0x0000ff00) >> 8; - mHeVer = (buffer[0] & 0x000000ff); - mHePrior = (buffer[1] & 0x000000FF); - mHeDetectorID = (buffer[1] & 0x0000FF00) >> 8; - mHeOffsetNewPack = (buffer[2] & 0x0000FFFF); - mHeMemorySize = (buffer[2] & 0xffff0000) >> 16; - mHeDW = (buffer[3] & 0xF0000000) >> 24; - mHeCruID = (buffer[3] & 0x0FF0000) >> 16; - mHePackNum = (buffer[3] & 0x0000FF00) >> 8; - mHeLinkNum = (buffer[3] & 0x000000FF); - mHeBCDI = (buffer[4] & 0x00000FFF); - mHeORBIT = buffer[5]; - mHeTType = buffer[8]; - mHePageNum = (buffer[9] & 0x0000FFFF); - mHeStop = (buffer[9] & 0x00ff0000) >> 16; - mHeBusy = (buffer[12] & 0xfffffe00) >> 9; - mHeFirmwareVersion = buffer[12] & 0x0000000f; - mHeHmpidError = (buffer[12] & 0x000001F0) >> 4; - mHePAR = buffer[13] & 0x0000FFFF; - */ - mHeFEEID = hpt->feeId; - mHeSize = hpt->headerSize; - mHeVer = hpt->version; - mHePrior = hpt->priority; - mHeDetectorID = hpt->sourceID; - mHeOffsetNewPack = hpt->offsetToNext; - mHeMemorySize = hpt->memorySize; - mHeDW = hpt->endPointID; - mHeCruID = hpt->cruID; - mHePackNum = hpt->packetCounter; - mHeLinkNum = hpt->linkID; - mHeBCDI = hpt->bunchCrossing; - mHeORBIT = hpt->orbit; - mHeTType = hpt->triggerType; - mHePageNum = hpt->pageCnt; - mHeStop = hpt->stop; - mHeBusy = (hpt->detectorField & 0xfffffe00) >> 9; - mHeFirmwareVersion = hpt->detectorField & 0x0000000f; - mHeHmpidError = (hpt->detectorField & 0x000001F0) >> 4; - mHePAR = hpt->detectorPAR; - - *EquipIndex = getEquipmentIndex(mHeCruID, mHeLinkNum); - // mEquipment = (*EquipIndex != -1) ? mTheEquipments[*EquipIndex]->getEquipmentId() : -1; - mEquipment = mHeFEEID & 0x000F; - mNumberWordToRead = ((mHeMemorySize - mHeSize) / sizeof(uint32_t)); - mPayloadTail = ((mHeOffsetNewPack - mHeMemorySize) / sizeof(uint32_t)); - - // ---- Event ID : Actualy based on ORBIT NUMBER and BC - mHeEvent = (mHeORBIT << 12) | mHeBCDI; - - LOG(debug) << "FEE-ID=" << mHeFEEID << " HeSize=" << mHeSize << " HePrior=" << mHePrior << " Det.Id=" << mHeDetectorID << " HeMemorySize=" << mHeMemorySize << " HeOffsetNewPack=" << mHeOffsetNewPack; - LOG(debug) << " Equipment=" << mEquipment << " PakCounter=" << mHePackNum << " Link=" << mHeLinkNum << " CruID=" << mHeCruID << " DW=" << mHeDW << " BC=" << mHeBCDI << " ORBIT=" << mHeORBIT; - LOG(debug) << " TType=" << mHeTType << " HeStop=" << mHeStop << " PagesCounter=" << mHePageNum << " FirmVersion=" << mHeFirmwareVersion << " BusyTime=" << mHeBusy << " Error=" << mHeHmpidError << " PAR=" << mHePAR; - LOG(debug) << " EquIdx = " << *EquipIndex << " Event = " << mHeEvent << " Payload : Words to read=" << mNumberWordToRead << " PailoadTail=" << mPayloadTail; - - if (*EquipIndex == -1) { - LOG(error) << "ERROR ! Bad equipment Number: " << mEquipment; - throw TH_WRONGEQUIPINDEX; - } - // std::cout << "HMPID ! Exit decode header" << std::endl; - return (true); -} - -/// Updates some information related to the Event -/// this function is called at the end of the event -/// @param[in] *eq : the pointer to the Equipment Object -void HmpidDecoder::updateStatistics(HmpidEquipment* eq) -{ - eq->mPadsPerEventAverage = ((eq->mPadsPerEventAverage * (eq->mNumberOfEvents - 1)) + eq->mSampleNumber) / (eq->mNumberOfEvents); - eq->mEventSizeAverage = ((eq->mEventSizeAverage * (eq->mNumberOfEvents - 1)) + eq->mEventSize) / (eq->mNumberOfEvents); - eq->mBusyTimeAverage = ((eq->mBusyTimeAverage * eq->mBusyTimeSamples) + eq->mBusyTimeValue) / (++(eq->mBusyTimeSamples)); - if (eq->mSampleNumber == 0) { - eq->mNumberOfEmptyEvents += 1; - } - if (eq->mErrorsCounter > 0) { - eq->mNumberOfWrongEvents += 1; - } - eq->mTotalPads += eq->mSampleNumber; - eq->mTotalErrors += eq->mErrorsCounter; - - //std::cout << ">>>updateStatistics() >>> "<< eq->getEquipmentId() << "="<< eq->mNumberOfEvents<<" :" << eq->mEventSize <<","<< eq->mTotalPads << ", " << eq->mSampleNumber << std::endl; - - return; -} - -/// Evaluates the content of the header and detect the change of the event -/// with the relevant updates... -/// @param[in] EquipmentIndex : the pointer to the Array of Equipments Array -/// @returns the Pointer to the modified Equipment object -HmpidEquipment* HmpidDecoder::evaluateHeaderContents(int EquipmentIndex) -{ - //std::cout << "Enter evaluateHeaderContents.."; - HmpidEquipment* eq = mTheEquipments[EquipmentIndex]; - if (mHeEvent != eq->mEventNumber) { // Is a new event - if (eq->mEventNumber != OUTRANGEEVENTNUMBER) { // skip the first - updateStatistics(eq); // update previous statistics - } - eq->mNumberOfEvents++; - eq->mEventNumber = mHeEvent; - eq->mBusyTimeValue = mHeBusy * 0.00000005; - eq->mEventSize = 0; // reset the event - eq->mSampleNumber = 0; - eq->mErrorsCounter = 0; - mIntReco = {(uint16_t)mHeBCDI, (uint32_t)mHeORBIT}; - } - eq->mEventSize += mNumberWordToRead * sizeof(uint32_t); // Calculate the size in bytes - if (mHeHmpidError != 0) { - LOG(info) << "HMPID Header reports an error : " << mHeHmpidError; - dumpHmpidError(mHeHmpidError); - eq->setError(ERR_HMPID); - } - // std::cout << ".. end evaluateHeaderContents = " << eq->mEventNumber << std::endl; - return (eq); -} - -/// --------------- Decode One Page from Data Buffer --------------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -/// @param[in] streamBuf : the pointer to the Pointer of the Stream Buffer -void HmpidDecoder::decodePage(uint32_t** streamBuf) -{ - int equipmentIndex; - try { - getHeaderFromStream(streamBuf); - } catch (int e) { - // The stream end ! - LOG(debug) << "End main decoding loop !"; - throw TH_BUFFEREMPTY; - } - try { - decodeHeader(*streamBuf, &equipmentIndex); - } catch (int e) { - LOG(error) << "Failed to decode the Header !"; - throw TH_WRONGHEADER; - } - - HmpidEquipment* eq = evaluateHeaderContents(equipmentIndex); - - uint32_t wpprev = 0; - uint32_t wp = 0; - int newOne = true; - int p1, p2, p3, p4; - int error; - int type; - bool isIt; - - int payIndex = 0; - while (payIndex < mNumberWordToRead) { //start the payload loop word by word - if (newOne == true) { - wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } - type = checkType(wp, &p1, &p2, &p3, &p4); - if (type == WTYPE_NONE) { - if (eq->mWillBePad == true) { // try to recover the first pad ! - type = checkType((wp & 0xF7FFFFFF), &p1, &p2, &p3, &p4); - if (type == WTYPE_PAD && p3 == 0 && eq->mWordsPerDilogicCounter == 0) { - newOne = false; // # reprocess as pad - continue; - } - } - eq->setError(ERR_NOTKNOWN); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_NOTKNOWN] << " [" << wp << "]"; - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - payIndex++; - continue; - } - } - if (mEquipment == 8) { - LOG(info) << "Event" << eq->mEventNumber << " >" << std::hex << wp << std::dec << "<" << type; - } - if (eq->mWillBeRowMarker == true) { // #shoud be a Row Marker - if (type == WTYPE_ROW) { - eq->mColumnCounter++; - eq->mWordsPerSegCounter++; - eq->mRowSize = p2; - switch (p2) { - case 0: // Empty column - eq->setError(ERR_ROWMARKEMPTY); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKEMPTY] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - break; - case 0x3FF: // Error in column - eq->setError(ERR_ROWMARKERROR); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKERROR] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - break; - case 0x3FE: // Masked column - LOG(info) << "Equip=" << mEquipment << "The column=" << (eq->mSegment) * 8 + eq->mColumnCounter << " is Masked !"; - eq->mWillBeRowMarker = true; - break; - default: - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - break; - } - newOne = true; - } else { - if (wp == wpprev) { - eq->setError(ERR_DUPLICATEPAD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - newOne = true; - } else if (type == WTYPE_EOE) { // # Could be a EoE - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKWRONG); - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } else if (type == WTYPE_PAD) { //# Could be a PAD - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKLOST); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKLOST] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } else if (type == WTYPE_EOS) { // # Could be a EoS - eq->mWillBeRowMarker = false; - eq->mWillBeSegmentMarker = true; - newOne = false; - } else { - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKLOST); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKLOST] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } - } - } else if (eq->mWillBePad == true) { // # We expect a pad - //# PAD:0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value - // c = 1..24 d = 1..10 n = 0..47 - if (type == WTYPE_PAD) { - newOne = true; - if (wp == wpprev) { - eq->setError(ERR_DUPLICATEPAD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - } else if (p1 != (eq->mSegment * 8 + eq->mColumnCounter)) { // # Manage - // We try to recover the RowMarker misunderstanding - isIt = isRowMarker(wp, &error, &p2, &p1); - if (isIt == true && error == false) { - type = WTYPE_ROW; - newOne = false; - eq->mWillBeEoE = true; - eq->mWillBePad = false; - } else { - LOG(debug) << "Equip=" << mEquipment << " Mismatch in column" - << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mColumnCounter = p1 % 8; - } - } else { - setPad(eq, p1 - 1, p2 - 1, p3, p4); - if (mEquipment == 8) { - LOG(info) << "Event" << eq->mEventNumber << " >" << p1 - 1 << "," << p2 - 1 << "," << p3 << "," << p4; - } - eq->mWordsPerDilogicCounter++; - eq->mSampleNumber++; - if (p3 == 47) { - eq->mWillBeEoE = true; - eq->mWillBePad = false; - } - } - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - } else if (type == WTYPE_EOE) { //# the pads are end ok - eq->mWillBeEoE = true; - eq->mWillBePad = false; - newOne = false; - } else if (type == WTYPE_ROW) { // # We Lost the EoE ! - // We try to recover the PAD misunderstanding - isIt = isPadWord(wp, &error, &p1, &p2, &p3, &p4); - if (isIt == true && error == false) { - type = WTYPE_PAD; - newOne = false; // # reprocess as pad - } else { - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - eq->mWillBePad = false; - newOne = false; - } - } else if (type == WTYPE_EOS) { // # We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = true; - eq->mWillBePad = false; - newOne = false; - } - } else if (eq->mWillBeEoE == true) { // # We expect a EoE - if (type == WTYPE_EOE) { - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - if (wpprev == wp) { - eq->setError(ERR_DOUBLEEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DOUBLEEOEMARK] << " col=" << p1; - } else if (p3 != eq->mWordsPerDilogicCounter) { - eq->setError(ERR_WRONGSIZEINEOE); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_WRONGSIZEINEOE] << " col=" << p1; - } - eq->mWordsPerDilogicCounter = 0; - if (p2 == 10) { - if (p1 % 8 != 0) { // # we expect the Row Marker - eq->mWillBeRowMarker = true; - } else { - eq->mWillBeSegmentMarker = true; - } - } else { - eq->mWillBePad = true; - } - eq->mWillBeEoE = false; - newOne = true; - } else if (type == WTYPE_EOS) { // We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = true; - eq->mWillBeEoE = false; - newOne = false; - } else if (type == WTYPE_ROW) { //# We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - eq->mWillBeEoE = false; - newOne = false; - } else if (type == WTYPE_PAD) { // # We Lost the EoE ! - int typb, p1b, p2b, p3b, p4b; - typb = checkType((wp | 0x08000000), &p1b, &p2b, &p3b, &p4b); - if (typb == WTYPE_EOE && p3b == 48) { - type = typb; - p1 = p1b; - p2 = p2b; - p3 = p3b; - p4 = p4b; - newOne = false; // # reprocess as EoE - } else { - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBePad = true; - eq->mWillBeEoE = false; - newOne = false; - } - } - } else if (eq->mWillBeSegmentMarker == true) { // # We expect a EoSegment - if (wpprev == wp) { - eq->setError(ERR_DOUBLEMARKWORD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DOUBLEMARKWORD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - newOne = true; - } else if (type == 2) { - if (abs(eq->mWordsPerSegCounter - p2) > 5) { - eq->setError(ERR_WRONGSIZESEGMENTMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_WRONGSIZESEGMENTMARK] << " Seg=" << p2; - } - eq->mWordsPerSegCounter = 0; - eq->mWordsPerRowCounter = 0; - eq->mColumnCounter = 0; - eq->mSegment = p3 % 3; - eq->mWillBeRowMarker = true; - eq->mWillBeSegmentMarker = false; - newOne = true; - } else { - eq->setError(ERR_LOSTEOSMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOSMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = false; - eq->mWillBeRowMarker = true; - newOne = false; - } - } - if (newOne) { - payIndex += 1; - } - } - for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); - } -} - -/// --------------- Read Raw Data Buffer --------------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -bool HmpidDecoder::decodeBuffer() -{ - // ---------resets the PAdMap----------- - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - mTheEquipments[i]->resetPadMap(); - mTheEquipments[i]->resetErrors(); - } - - int type; - int equipmentIndex = -1; - int isIt; - HmpidEquipment* eq; - uint32_t* streamBuf; - LOG(debug) << "Enter decoding !"; - - // Input Stream Main Loop - while (true) { - try { - decodePage(&streamBuf); - } catch (int e) { - LOG(debug) << "End main buffer decoding loop !"; - break; - } - } // this is the end of stream - - // cycle in order to update info for the last event - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->mNumberOfEvents > 0) { - updateStatistics(mTheEquipments[i]); - } - } - return (true); -} - -/// --------- Decode One Page from Data Buffer with Fast Decoding -------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -/// @param[in] streamBuf : the pointer to the Pointer of the Stream Buffer -void HmpidDecoder::decodePageFast(uint32_t** streamBuf) -{ - int equipmentIndex; - try { - getHeaderFromStream(streamBuf); - } catch (int e) { - // The stream end ! - LOG(info) << "End Fast Page decoding loop !"; - throw TH_BUFFEREMPTY; - } - try { - decodeHeader(*streamBuf, &equipmentIndex); - } catch (int e) { - LOG(info) << "Failed to decode the Header !"; - throw TH_WRONGHEADER; - } - HmpidEquipment* eq = evaluateHeaderContents(equipmentIndex); - uint32_t wpprev = 0; - uint32_t wp = 0; - int newOne = true; - int Column, Dilogic, Channel, Charge; - int pwer; - int payIndex = 0; - while (payIndex < mNumberWordToRead) { //start the payload loop word by word - wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } - if (wp == wpprev) { - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << Column << "]"; - } else { - if (isPadWord(wp, &pwer, &Column, &Dilogic, &Channel, &Charge) == true) { - if (pwer != true) { - setPad(eq, Column - 1, Dilogic - 1, Channel, Charge); - eq->mSampleNumber++; - } - } - } - payIndex += 1; - } - for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); - } - return; -} -/// ---------- Read Raw Data Buffer with Fast Decoding ---------- -/// Read the stream, decode the contents and store resuls. -/// Fast alghoritm : no parsing of control words ! -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -bool HmpidDecoder::decodeBufferFast() -{ - // ---------resets the PAdMap----------- - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - mTheEquipments[i]->resetPadMap(); - } - - uint32_t* streamBuf; - LOG(info) << "Enter FAST decoding !"; - - // Input Stream Main Loop - while (true) { - try { - decodePageFast(&streamBuf); - } catch (int e) { - LOG(info) << " End Buffer Fast Decoding !"; - break; - } - } // this is the end of stream - - // cycle in order to update info for the last event - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->mNumberOfEvents > 0) { - updateStatistics(mTheEquipments[i]); - } - } - return (true); -} - -// ========================================================= - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Number of entries for specified pad -uint16_t HmpidDecoder::getPadSamples(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSamples[c][d][h]); -} - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Sum of Charges for specified pad -double HmpidDecoder::getPadSum(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSum[c][d][h]); -} - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Sum of Square Charges for specified pad -double HmpidDecoder::getPadSquares(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSquares[c][d][h]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Number of Entries for specified pad -uint16_t HmpidDecoder::getChannelSamples(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSamples[Column][Dilogic][Channel]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Sum of Charges for specified pad -double HmpidDecoder::getChannelSum(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSum[Column][Dilogic][Channel]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Sum of Square Charges for specified pad -double HmpidDecoder::getChannelSquare(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSquares[Column][Dilogic][Channel]); -} - -/// Gets the Average Event Size value -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @returns The Average Event Size value ( 0 for wrong Equipment Id) -float HmpidDecoder::getAverageEventSize(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0.0); - } - return (mTheEquipments[EqInd]->mEventSizeAverage); -} - -/// Gets the Average Busy Time value -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @returns The Average Busy Time value ( 0 for wrong Equipment Id) -float HmpidDecoder::getAverageBusyTime(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0.0); - } - return (mTheEquipments[EqInd]->mBusyTimeAverage); -} - -// =================================================== -// Methods to dump info - -/// Prints on the standard output the table of decoding -/// errors for one equipment -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -void HmpidDecoder::dumpErrors(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return; - } - std::cout << "Dump Errors for the Equipment = " << EquipmId << std::endl; - for (int i = 0; i < MAXERRORS; i++) { - std::cout << sErrorDescription[i] << " = " << mTheEquipments[EqInd]->mErrors[i] << std::endl; - } - std::cout << " -------- " << std::endl; - return; -} - -/// Prints on the standard output a Table of statistical -/// decoding information for one equipment -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @type[in] The type of info. 0 = Entries, 1 = Sum, 2 = Sum of squares -void HmpidDecoder::dumpPads(int EquipmId, int type) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return; - } - int Module = EquipmId / 2; - int StartRow = (EquipmId % 2 == 1) ? 80 : 0; - int EndRow = (EquipmId % 2 == 1) ? 160 : 80; - std::cout << "Dump Pads for the Equipment = " << EquipmId << std::endl; - for (int c = 0; c < 144; c++) { - for (int r = StartRow; r < EndRow; r++) { - switch (type) { - case 0: - std::cout << getPadSamples(Module, r, c) << ","; - break; - case 1: - std::cout << getPadSum(Module, r, c) << ","; - break; - case 2: - std::cout << getPadSquares(Module, r, c) << ","; - break; - } - } - std::cout << std::endl; - } - std::cout << " -------- " << std::endl; - return; -} - -/// Prints on the standard output the decoded HMPID error field -/// @param[in] ErrorField : the HMPID readout error field -void HmpidDecoder::dumpHmpidError(int ErrorField) -{ - char printbuf[MAXHMPIDERRORS * MAXDESCRIPTIONLENGHT]; - if (decodeHmpidError(ErrorField, printbuf) == true) { - LOG(error) << "HMPID Error field = " << ErrorField << " : " << printbuf; - } - return; -} - -/// Writes in a ASCCI File the complete report of the decoding -/// procedure -/// @param[in] *summaryFileName : the name of the output file -/// @throws TH_CREATEFILE Thrown if was not able to create the file -void HmpidDecoder::writeSummaryFile(char* summaryFileName) -{ - FILE* fs = fopen(summaryFileName, "w"); - if (fs == nullptr) { - printf("Error opening the file %s !\n", summaryFileName); - throw TH_CREATEFILE; - } - - fprintf(fs, "HMPID Readout Raw Data Decoding Summary File\n"); - fprintf(fs, "Equipment Id\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->getEquipmentId()); - } - fprintf(fs, "\n"); - - fprintf(fs, "Number of events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfEvents); - } - fprintf(fs, "\n"); - - fprintf(fs, "Average Event Size\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%f\t", mTheEquipments[i]->mEventSizeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Total pads\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mTotalPads); - } - fprintf(fs, "\n"); - - fprintf(fs, "Average pads per event\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%f\t", mTheEquipments[i]->mPadsPerEventAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Busy Time average\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%e\t", mTheEquipments[i]->mBusyTimeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Event rate\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%e\t", 1 / mTheEquipments[i]->mBusyTimeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Number of Empty Events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfEmptyEvents); - } - fprintf(fs, "\n"); - - fprintf(fs, "-------------Errors--------------------\n"); - fprintf(fs, "Wrong events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfWrongEvents); - } - fprintf(fs, "\n"); - - for (int j = 0; j < MAXERRORS; j++) { - fprintf(fs, "%s\t", sErrorDescription[j]); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mErrors[j]); - } - fprintf(fs, "\n"); - } - - fprintf(fs, "Total errors\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mTotalErrors); - } - fprintf(fs, "\n"); - - fclose(fs); - return; -} diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecoder2.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecoder2.cxx index d96d27e07a0d0..f88c3a6b65253 100644 --- a/Detectors/HMPID/reconstruction/src/HmpidDecoder2.cxx +++ b/Detectors/HMPID/reconstruction/src/HmpidDecoder2.cxx @@ -17,6 +17,7 @@ /// \date 17/11/2020 /* ------ HISTORY --------- +24/01/2024 - review of pointer management from the stream AF */ #include // for LOG @@ -133,6 +134,7 @@ void HmpidDecoder2::init() for (int i = 0; i < mNumberOfEquipments; i++) { mTheEquipments[i]->init(); mTheEquipments[i]->resetPadMap(); + mTheEquipments[i]->resetErrors(); } mDigits.clear(); @@ -531,9 +533,7 @@ void HmpidDecoder2::decodePage(uint32_t** streamBuf) while (payIndex < mNumberWordToRead) { // start the payload loop word by word if (newOne == true) { wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } + wp = readWordFromStream(); type = checkType(wp, &p1, &p2, &p3, &p4); if (type == WTYPE_NONE) { if (eq->mWillBePad == true) { // try to recover the first pad ! @@ -744,7 +744,7 @@ void HmpidDecoder2::decodePage(uint32_t** streamBuf) } } for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); + wp = readWordFromStream(); } *streamBuf = mActualStreamPtr; } @@ -822,7 +822,7 @@ void HmpidDecoder2::decodePageFast(uint32_t** streamBuf) // The stream end ! if (mVerbose > 6) { std::cout << "HMPID Decoder2 : [INFO] " - << "End Fast Page decoding loop !" << std::endl; + << "End Fast Page decoding loop ! (" << e << ")" << std::endl; } throw TH_BUFFEREMPTY; } @@ -831,15 +831,19 @@ void HmpidDecoder2::decodePageFast(uint32_t** streamBuf) } catch (int e) { if (mVerbose > 6) { std::cout << "HMPID Decoder2 : [INFO] " - << "Failed to decode the Header !" << std::endl; + << "Failed to decode the Header ! (" << e << ")" << std::endl; } - throw TH_WRONGHEADER; + throw e; } HmpidEquipment* eq; try { eq = evaluateHeaderContents(equipmentIndex); } catch (int e) { - throw TH_WRONGHEADER; + if (mVerbose > 6) { + std::cout << "HMPID Decoder2 : [INFO] " + << "Failed to evaluate the Header ! (" << e << ")" << std::endl; + } + throw e; } uint32_t wpprev = 0; uint32_t wp = 0; @@ -858,8 +862,10 @@ void HmpidDecoder2::decodePageFast(uint32_t** streamBuf) } else { if (isPadWord(wp, &pwer, &Column, &Dilogic, &Channel, &Charge) == true) { if (pwer != true) { - setPad(eq, Column - 1, Dilogic - 1, Channel, Charge); - eq->mSampleNumber++; + if (!((equipmentIndex == 6 && Column == 16 && Dilogic == 10) || (equipmentIndex == 13 && Column == 18 && Dilogic == 2))) { + setPad(eq, Column - 1, Dilogic - 1, Channel, Charge); + eq->mSampleNumber++; + } } } } @@ -879,7 +885,7 @@ void HmpidDecoder2::decodePageFast(uint32_t** streamBuf) /// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header bool HmpidDecoder2::decodeBufferFast() { - bool isNotEmpty = false; + bool isNotEmpty = true; // suppress the Error Decoding Superpage message in normal verbosity // ---------resets the PAdMap----------- for (int i = 0; i < mNumberOfEquipments; i++) { mTheEquipments[i]->init(); @@ -895,15 +901,14 @@ bool HmpidDecoder2::decodeBufferFast() try { decodePageFast(&streamBuf); } catch (int e) { - if (mVerbose > 6) { - std::cout << "HMPID Decoder2 : [INFO] " - << " End Buffer Fast Decoding !" << std::endl; + if (mVerbose > 6) { // print all the decode errors eccept the end buffer + std::cout << "HMPID Decoder2 : [ERROR] " + << "End Buffer Fast Decoding! Exit code error = " << e << std::endl; + isNotEmpty = false; } break; } - isNotEmpty = true; - } // this is the end of stream - + } // cycle in order to update info for the last event for (int i = 0; i < mNumberOfEquipments; i++) { if (mTheEquipments[i]->mNumberOfEvents > 0) { @@ -1103,6 +1108,33 @@ void HmpidDecoder2::dumpHmpidError(HmpidEquipment* eq, int ErrorField, int mHeBC return; } +/// Prints a block of memory +/// @param[in] MemoryStartPtr : the pointer to the begin +/// @param[in] Dimension : the block dimension in bytes +void HmpidDecoder2::dumpMemory(const void* MemoryStartPtr, std::size_t Dimension) +{ + const unsigned char* data = static_cast(MemoryStartPtr); + for (std::size_t i = 0; i < Dimension; i += 16) { + std::cout << std::hex << std::setw(4) << std::setfill('0') << i << " : "; + // Print hexadecimal values + for (std::size_t j = 0; j < 16; ++j) { + if (i + j < Dimension) { + std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast(data[i + j]) << " "; + } else { + std::cout << " "; + } + } + // Print ASCII values + std::cout << " - "; + for (std::size_t j = 0; j < 16 && i + j < Dimension; ++j) { + char ch = (data[i + j] >= 32 && data[i + j] <= 126) ? static_cast(data[i + j]) : '.'; + std::cout << ch; + } + std::cout << std::endl; + } + return; +} + /// Writes in a ASCCI File the complete report of the decoding /// procedure /// @param[in] *summaryFileName : the name of the output file @@ -1190,17 +1222,20 @@ void HmpidDecoder2::writeSummaryFile(char* summaryFileName) } /// Gets a sized chunk from the stream. The stream pointers members are updated -/// @param[in] **streamPtr : the pointer to the memory buffer +/// @param[out] **streamPtr : the pointer to the memory buffer /// @param[in] Size : the dimension of the chunk (words) /// @returns True every time -/// @throw TH_WRONGBUFFERDIM Buffer length shorter then the requested +/// @throws TH_BUFFERPOINTERTOEND Thrown if the pointer to the buffer is past end +/// @throws TH_WRONGBUFFERDIM Thrown if the buffer len is less then the size requested bool HmpidDecoder2::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) { + if (mActualStreamPtr > mEndStreamPtr) { + throw TH_BUFFERPOINTERTOEND; + return (false); + } *streamPtr = mActualStreamPtr; mActualStreamPtr += Size; if (mActualStreamPtr > mEndStreamPtr) { - // std::cout << " getBlockFromStream : StPtr=" << mActualStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << Size << std::endl; - // std::cout << "Beccato " << std::endl; throw TH_WRONGBUFFERDIM; return (false); } @@ -1208,35 +1243,26 @@ bool HmpidDecoder2::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) } /// Gets the Header Block from the stream. -/// @param[in] **streamPtr : the pointer to the memory buffer +/// @param[out] **streamPtr : the pointer to the memory buffer /// @returns True if the header is read bool HmpidDecoder2::getHeaderFromStream(uint32_t** streamPtr) { return (getBlockFromStream(streamPtr, mRDHSize)); } -/// Gets a Word from the stream. -/// @param[in] *word : the buffer for the read word -/// @returns True if the operation end well -bool HmpidDecoder2::getWordFromStream(uint32_t* word) -{ - uint32_t* appo; - if (getBlockFromStream(&appo, 1)) { - *word = *mActualStreamPtr; - return (true); - } - return (false); -} - /// Gets a Word from the stream. /// @returns The word read +/// @throws TH_BUFFERPOINTERTOEND Thrown if the pointer to the buffer is past end uint32_t HmpidDecoder2::readWordFromStream() { - mActualStreamPtr++; + uint32_t word = 0; if (mActualStreamPtr > mEndStreamPtr) { - throw TH_WRONGBUFFERDIM; + throw TH_BUFFERPOINTERTOEND; + return (word); } - return (*mActualStreamPtr); + word = *mActualStreamPtr; + mActualStreamPtr++; + return (word); } /// Setup the Input Stream with a Memory Pointer @@ -1274,7 +1300,16 @@ bool HmpidDecoder2::setUpStream(void* Buffer, long BufferLen) } mActualStreamPtr = (uint32_t*)Buffer; // sets the pointer to the Buffer - mEndStreamPtr = ((uint32_t*)Buffer) + wordsBufferLen; // sets the End of buffer + mEndStreamPtr = ((uint32_t*)Buffer) + wordsBufferLen - 1; // sets the End of buffer mStartStreamPtr = ((uint32_t*)Buffer); + + if (mVerbose > 6) { + std::cout << "HMPID Decoder2 : setUpStream() " + << "Buffer addr = " << std::hex << mActualStreamPtr << " End addr = " << mEndStreamPtr + << " BufferLen = " << std::dec << BufferLen << std::endl; + if (BufferLen < 256) { + dumpMemory((const void*)mActualStreamPtr, (std::size_t)BufferLen); + } + } return (true); } diff --git a/Detectors/HMPID/reconstruction/src/Recon.cxx b/Detectors/HMPID/reconstruction/src/Recon.cxx new file mode 100644 index 0000000000000..867e72cc319c8 --- /dev/null +++ b/Detectors/HMPID/reconstruction/src/Recon.cxx @@ -0,0 +1,628 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "HMPIDBase/Param.h" +#include "HMPIDReconstruction/Recon.h" //class header +// #include "ReconstructionDataFormats/MatchInfoHMP.h" + +#include //TracePhot() +#include //HoughResponse() +#include //HoughResponse() + +#include "ReconstructionDataFormats/MatchInfoHMP.h" +#include "ReconstructionDataFormats/Track.h" + +using MatchInfo = o2::dataformats::MatchInfoHMP; + +using namespace o2::hmpid; +// ClassImp(Recon); +ClassImp(o2::hmpid::Recon); +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::initVars(int n) +{ + //.. + // Init some variables + //.. + if (n <= 0) { + return; + } + + // ef : changed to smart-pointer Array + // fPhotFlag = new int[n]; + fPhotFlag = std::unique_ptr(new int[n]); + fPhotClusIndex = std::unique_ptr(new int[n]); + + fPhotCkov = std::unique_ptr(new double[n]); + fPhotPhi = std::unique_ptr(new double[n]); + fPhotWei = std::unique_ptr(new double[n]); + // +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::ckovAngle(o2::dataformats::MatchInfoHMP* match, const std::vector clusters, int index, double nmean, float xRa, float yRa) +{ + // Pattern recognition method based on Hough transform + // Arguments: pTrk - track for which Ckov angle is to be found + // pCluLst - list of clusters for this chamber + // Returns: - track ckov angle, [rad], + + const int nMinPhotAcc = 3; // Minimum number of photons required to perform the pattern recognition + + int nClusTot = clusters.size(); + + initVars(nClusTot); + + float xPc, yPc, th, ph; + + match->getHMPIDtrk(xPc, yPc, th, ph); // initialize this track: th and ph angles at middle of RAD + + setTrack(xRa, yRa, th, ph); + + fParam->setRefIdx(nmean); + + float mipQ = -1, mipX = -1, mipY = -1; + int chId = -1, sizeClu = -1; + + fPhotCnt = 0; + + int nPads = 0; + + for (int iClu = 0; iClu < clusters.size(); iClu++) { // clusters loop + + o2::hmpid::Cluster cluster = clusters.at(iClu); + nPads += cluster.size(); + if (iClu == index) { // this is the MIP! not a photon candidate: just store mip info + mipX = cluster.x(); + mipY = cluster.y(); + mipQ = cluster.q(); + sizeClu = cluster.size(); + continue; + } + + chId = cluster.ch(); + if (cluster.q() > 2 * fParam->qCut() || cluster.size() > 4) { + continue; + } + double thetaCer, phiCer; + if (findPhotCkov(cluster.x(), cluster.y(), thetaCer, phiCer)) { // find ckov angle for this photon candidate + fPhotCkov[fPhotCnt] = thetaCer; // actual theta Cerenkov (in TRS) + fPhotPhi[fPhotCnt] = phiCer; + fPhotClusIndex[fPhotCnt] = iClu; // actual phi Cerenkov (in TRS): -pi to come back to "unusual" ref system (X,Y,-Z) + fPhotCnt++; // increment counter of photon candidates + } + } // clusters loop + + match->setHMPIDmip(mipX, mipY, mipQ, fPhotCnt); // store mip info in any case + match->setIdxHMPClus(chId, index + 1000 * sizeClu); // set index of cluster + match->setMipClusSize(sizeClu); + + if (fPhotCnt < nMinPhotAcc) { // no reconstruction with <=3 photon candidates + match->setHMPsignal(kNoPhotAccept); // set the appropriate flag + return; + } + + fMipPos.Set(mipX, mipY); + + // PATTERN RECOGNITION STARTED: + if (fPhotCnt > fParam->multCut()) { + fIsWEIGHT = kTRUE; + } // offset to take into account bkg in reconstruction + else { + fIsWEIGHT = kFALSE; + } + + float photCharge[10] = {0x0}; + + int iNrec = flagPhot(houghResponse(), clusters, photCharge); // flag photons according to individual theta ckov with respect to most probable + // int iNrec = flagPhot(houghResponse(), clusters); // flag photons according to individual theta ckov with respect to most probable + + match->setPhotCharge(photCharge); + match->setHMPIDmip(mipX, mipY, mipQ, iNrec); // store mip info + + if (iNrec < nMinPhotAcc) { + match->setHMPsignal(kNoPhotAccept); // no photon candidates are accepted + return; + } + + int occupancy = (int)(1000 * (nPads / (6. * 80. * 48.))); + + double thetaC = findRingCkov(clusters.size()); // find the best reconstructed theta Cherenkov + findRingGeom(thetaC, 2); + + match->setHMPsignal(thetaC + occupancy); // store theta Cherenkov and chmaber occupancy + // match->SetHMPIDchi2(fCkovSigma2); //store experimental ring angular resolution squared + + // deleteVars(); ef : in case of smart-pointers, should not be necessary? +} // CkovAngle() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +bool Recon::findPhotCkov(double cluX, double cluY, double& thetaCer, double& phiCer) +{ + // Finds Cerenkov angle for this photon candidate + // Arguments: cluX,cluY - position of cadidate's cluster + // Returns: Cerenkov angle + + TVector3 dirCkov; + + double zRad = -0.5 * fParam->radThick() - 0.5 * fParam->winThick(); // z position of middle of RAD + TVector3 rad(fTrkPos.X(), fTrkPos.Y(), zRad); // impact point at middle of RAD + TVector3 pc(cluX, cluY, 0.5 * fParam->winThick() + fParam->gapThick()); // mip at PC + double cluR = TMath::Sqrt((cluX - fPc.X()) * (cluX - fPc.X()) + + (cluY - fPc.Y()) * (cluY - fPc.Y())); // ref. distance impact RAD-CLUSTER + double phi = (pc - rad).Phi(); // phi of photon + + double ckov1 = 0; + double ckov2 = 0.75 + fTrkDir.Theta(); // start to find theta cerenkov in DRS + const double kTol = 0.01; + Int_t iIterCnt = 0; + while (1) { + if (iIterCnt >= 50) { + return kFALSE; + } + double ckov = 0.5 * (ckov1 + ckov2); + dirCkov.SetMagThetaPhi(1, ckov, phi); + TVector2 posC = traceForward(dirCkov); // trace photon with actual angles + double dist = cluR - (posC - fPc).Mod(); // get distance between trial point and cluster position + if (posC.X() == -999) { + dist = -999; + } // total reflection problem + iIterCnt++; // counter step + if (dist > kTol) { + ckov1 = ckov; + } // cluster @ larger ckov + else if (dist < -kTol) { + ckov2 = ckov; + } // cluster @ smaller ckov + else { // precision achived: ckov in DRS found + dirCkov.SetMagThetaPhi(1, ckov, phi); // + lors2Trs(dirCkov, thetaCer, phiCer); // find ckov (in TRS:the effective Cherenkov angle!) + return kTRUE; + } + } +} // FindPhotTheta() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +bool Recon::findPhotCkov2(double cluX, double cluY, double& thetaCer, double& phiCer) +{ + + // TVector3 emissionV; + /** set emission point **/ + // emissionV.SetXYZ(xEm, yEm, zEm); + + // TVector3 directionV; + /** set track direction vector **/ + // directionV.SetXYZ(trkPx, trkPy, trkPz); + + double zRad = -0.5 * fParam->radThick() - 0.5 * fParam->winThick(); // z position of middle of RAD + TVector3 emissionV(fTrkPos.X(), fTrkPos.Y(), zRad); // impact point at middle of RAD + + TVector3 photonHitV, apparentV, surfaceV; + photonHitV.SetXYZ(cluX, cluY, 0.5 * fParam->winThick() + fParam->gapThick()); + apparentV = photonHitV - emissionV; + surfaceV = emissionV; + // surfaceV.SetZ(0); + + Double_t n1 = fParam->getRefIdx(); + Double_t n2 = 1.; + Double_t apparentTheta = apparentV.Theta(); + Double_t correctedTheta = asin(n2 / n1 * sin(apparentTheta)); + Double_t deltaTheta = apparentTheta - correctedTheta; + + TVector3 perpV = apparentV.Cross(surfaceV); + TVector3 cherenkovV = apparentV; + // cherenkovV.Rotate(deltaTheta, perpV); + + lors2Trs(cherenkovV, thetaCer, phiCer); + + // thetaCer = cherenkovV.Angle(fTrkDir); + // phiCer = cherenkovV.Phi(); + + return kTRUE; +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +TVector2 Recon::traceForward(TVector3 dirCkov) const +{ + // Trace forward a photon from (x,y) up to PC + // Arguments: dirCkov photon vector in LORS + // Returns: pos of traced photon at PC + + TVector2 pos(-999, -999); + double thetaCer = dirCkov.Theta(); + if (thetaCer > TMath::ASin(1. / fParam->getRefIdx())) { + return pos; + } // total refraction on WIN-GAP boundary + double zRad = -0.5 * fParam->radThick() - 0.5 * fParam->winThick(); // z position of middle of RAD + TVector3 posCkov(fTrkPos.X(), fTrkPos.Y(), zRad); // RAD: photon position is track position @ middle of RAD + propagate(dirCkov, posCkov, -0.5 * fParam->winThick()); // go to RAD-WIN boundary + refract(dirCkov, fParam->getRefIdx(), fParam->winIdx()); // RAD-WIN refraction + propagate(dirCkov, posCkov, 0.5 * fParam->winThick()); // go to WIN-GAP boundary + refract(dirCkov, fParam->winIdx(), fParam->gapIdx()); // WIN-GAP refraction + propagate(dirCkov, posCkov, 0.5 * fParam->winThick() + fParam->gapThick()); // go to PC + pos.Set(posCkov.X(), posCkov.Y()); + return pos; + +} // TraceForward() + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::lors2Trs(TVector3 dirCkov, double& thetaCer, double& phiCer) const +{ + // Theta Cerenkov reconstruction + // Arguments: dirCkov photon vector in LORS + // Returns: thetaCer of photon in TRS + // phiCer of photon in TRS + // TVector3 dirTrk; + // dirTrk.SetMagThetaPhi(1,fTrkDir.Theta(),fTrkDir.Phi()); -> dirTrk.SetCoordinates(1,fTrkDir.Theta(),fTrkDir.Phi()) + // double thetaCer = TMath::ACos(dirCkov*dirTrk); + + TRotation mtheta; + mtheta.RotateY(-fTrkDir.Theta()); + + TRotation mphi; + mphi.RotateZ(-fTrkDir.Phi()); + + TRotation mrot = mtheta * mphi; + + TVector3 dirCkovTRS; + dirCkovTRS = mrot * dirCkov; + phiCer = dirCkovTRS.Phi(); // actual value of the phi of the photon + thetaCer = dirCkovTRS.Theta(); // actual value of thetaCerenkov of the photon +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::trs2Lors(TVector3 dirCkov, double& thetaCer, double& phiCer) const +{ + // Theta Cerenkov reconstruction + // Arguments: dirCkov photon vector in TRS + // Returns: thetaCer of photon in LORS + // phiCer of photon in LORS + + // TRotation mtheta; + // mtheta.RotateY(fTrkDir.Theta()); ef : changed to : + + TRotation mtheta; + mtheta.RotateY(fTrkDir.Theta()); + + TRotation mphi; + mphi.RotateZ(fTrkDir.Phi()); + + TRotation mrot = mphi * mtheta; + + TVector3 dirCkovLORS; + dirCkovLORS = mrot * dirCkov; + + phiCer = dirCkovLORS.Phi(); // actual value of the phi of the photon + thetaCer = dirCkovLORS.Theta(); // actual value of thetaCerenkov of the photon +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::findRingGeom(double ckovAng, int level) +{ + // Find area covered in the PC acceptance + // Arguments: ckovAng - cerenkov angle + // level - precision in finding area and portion of ring accepted (multiple of 50) + // Returns: area of the ring in cm^2 for given theta ckov + + Int_t kN = 50 * level; + Int_t nPoints = 0; + Double_t area = 0; + + Bool_t first = kFALSE; + TVector2 pos1; + + for (Int_t i = 0; i < kN; i++) { + if (!first) { + pos1 = tracePhot(ckovAng, Double_t(TMath::TwoPi() * (i + 1) / kN)); // find a good trace for the first photon + if (pos1.X() == -999) { + continue; + } // no area: open ring + if (!fParam->isInside(pos1.X(), pos1.Y(), 0)) { + pos1 = intWithEdge(fMipPos, pos1); // find the very first intersection... + } else { + if (!fParam->isInDead(pos1.X(), pos1.Y())) { + nPoints++; + } // photon is accepted if not in dead zone + } + first = kTRUE; + continue; + } + TVector2 pos2 = tracePhot(ckovAng, Double_t(TMath::TwoPi() * (i + 1) / kN)); // trace the next photon + if (pos2.X() == -999) { + { + continue; + } + } // no area: open ring + if (!fParam->isInside(pos2.X(), pos2.Y(), 0)) { + pos2 = intWithEdge(fMipPos, pos2); + } else { + if (!fParam->isInDead(pos2.X(), pos2.Y())) { + nPoints++; + } // photon is accepted if not in dead zone + } + area += TMath::Abs((pos1 - fMipPos).X() * (pos2 - fMipPos).Y() - (pos1 - fMipPos).Y() * (pos2 - fMipPos).X()); // add area of the triangle... + pos1 = pos2; + } + //--- find area and length of the ring; + fRingAcc = (Double_t)nPoints / (Double_t)kN; + area *= 0.5; + fRingArea = area; + +} // FindRingGeom() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +const TVector2 Recon::intWithEdge(TVector2 p1, TVector2 p2) +{ + // It finds the intersection of the line for 2 points traced as photons + // and the edge of a given PC + // Arguments: 2 points obtained tracing the photons + // Returns: intersection point with detector (PC) edges + + double xmin = (p1.X() < p2.X()) ? p1.X() : p2.X(); + double xmax = (p1.X() < p2.X()) ? p2.X() : p1.X(); + double ymin = (p1.Y() < p2.Y()) ? p1.Y() : p2.Y(); + double ymax = (p1.Y() < p2.Y()) ? p2.Y() : p1.Y(); + + double m = TMath::Tan((p2 - p1).Phi()); + TVector2 pint; + // intersection with low X + pint.Set((double)(p1.X() + (0 - p1.Y()) / m), 0.); + if (pint.X() >= 0 && pint.X() <= fParam->sizeAllX() && + pint.X() >= xmin && pint.X() <= xmax && + pint.Y() >= ymin && pint.Y() <= ymax) { + return pint; + } + // intersection with high X + pint.Set((double)(p1.X() + (fParam->sizeAllY() - p1.Y()) / m), (double)(fParam->sizeAllY())); + if (pint.X() >= 0 && pint.X() <= fParam->sizeAllX() && + pint.X() >= xmin && pint.X() <= xmax && + pint.Y() >= ymin && pint.Y() <= ymax) { + return pint; + } + // intersection with left Y + pint.Set(0., (double)(p1.Y() + m * (0 - p1.X()))); + if (pint.Y() >= 0 && pint.Y() <= fParam->sizeAllY() && + pint.Y() >= ymin && pint.Y() <= ymax && + pint.X() >= xmin && pint.X() <= xmax) { + return pint; + } + // intersection with righ Y + pint.Set((double)(fParam->sizeAllX()), (double)(p1.Y() + m * (fParam->sizeAllX() - p1.X()))); // ef: Set->SetCoordinates + if (pint.Y() >= 0 && pint.Y() <= fParam->sizeAllY() && + pint.Y() >= ymin && pint.Y() <= ymax && + pint.X() >= xmin && pint.X() <= xmax) { + return pint; + } + return p1; +} // IntWithEdge() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +double Recon::findRingCkov(int) +{ + // Loops on all Ckov candidates and estimates the best Theta Ckov for a ring formed by those candidates. Also estimates an error for that Theat Ckov + // collecting errors for all single Ckov candidates thetas. (Assuming they are independent) + // Arguments: iNclus- total number of clusters in chamber for background estimation + // Return: best estimation of track Theta ckov + + Double_t wei = 0.; + Double_t weightThetaCerenkov = 0.; + + Double_t ckovMin = 9999., ckovMax = 0.; + Double_t sigma2 = 0; // to collect error squared for this ring + + for (Int_t i = 0; i < fPhotCnt; i++) { // candidates loop + if (fPhotFlag[i] == 2) { + if (fPhotCkov[i] < ckovMin) { + ckovMin = fPhotCkov[i]; + } // find max and min Theta ckov from all candidates within probable window + if (fPhotCkov[i] > ckovMax) { + ckovMax = fPhotCkov[i]; + } + weightThetaCerenkov += fPhotCkov[i] * fPhotWei[i]; + wei += fPhotWei[i]; // collect weight as sum of all candidate weghts + + sigma2 += 1. / fParam->sigma2(fTrkDir.Theta(), fTrkDir.Phi(), fPhotCkov[i], fPhotPhi[i]); + } + } // candidates loop + + if (sigma2 > 0) { + fCkovSigma2 = 1. / sigma2; + } else { + fCkovSigma2 = 1e10; + } + + if (wei != 0.) { + weightThetaCerenkov /= wei; + } else { + weightThetaCerenkov = 0.; + } + return weightThetaCerenkov; + +} // FindCkovRing() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +int Recon::flagPhot(double ckov, const std::vector clusters, float* photChargeVec) +// int Recon::flagPhot(double ckov, const std::vector clusters) +{ + // Flag photon candidates if their individual ckov angle is inside the window around ckov angle returned by HoughResponse() + // Arguments: ckov- value of most probable ckov angle for track as returned by HoughResponse() + // Returns: number of photon candidates happened to be inside the window + + // Photon Flag: Flag = 0 initial set; + // Flag = 1 good candidate (charge compatible with photon); + // Flag = 2 photon used for the ring; + + Int_t steps = (Int_t)((ckov) / fDTheta); // how many times we need to have fDTheta to fill the distance between 0 and thetaCkovHough + + Double_t tmin = (Double_t)(steps - 1) * fDTheta; + Double_t tmax = (Double_t)(steps)*fDTheta; + Double_t tavg = 0.5 * (tmin + tmax); + + tmin = tavg - 0.5 * fWindowWidth; + tmax = tavg + 0.5 * fWindowWidth; + + Int_t iInsideCnt = 0; // count photons which Theta ckov inside the window + for (Int_t i = 0; i < fPhotCnt; i++) { // photon candidates loop + fPhotFlag[i] = 0; + if (fPhotCkov[i] >= tmin && fPhotCkov[i] <= tmax) { + fPhotFlag[i] = 2; + o2::hmpid::Cluster cluster = clusters.at(fPhotClusIndex[i]); + float charge = cluster.q(); + if (iInsideCnt < 10) { + photChargeVec[iInsideCnt] = charge; + } // AddObjectToFriends(pCluLst,i,pTrk); + iInsideCnt++; + } + } + + return iInsideCnt; + +} // FlagPhot() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +TVector2 Recon::tracePhot(double ckovThe, double ckovPhi) const +{ + // Trace a single Ckov photon from emission point somewhere in radiator up to photocathode taking into account ref indexes of materials it travereses + // Arguments: ckovThe,ckovPhi- photon ckov angles in TRS, [rad] + // Returns: distance between photon point on PC and track projection + + double theta, phi; + TVector3 dirTRS, dirLORS; + dirTRS.SetMagThetaPhi(1, ckovThe, ckovPhi); // photon in TRS + trs2Lors(dirTRS, theta, phi); + dirLORS.SetMagThetaPhi(1, theta, phi); // photon in LORS + return traceForward(dirLORS); // now foward tracing + +} // tracePhot() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::propagate(const TVector3 dir, TVector3& pos, double z) const +{ + // Finds an intersection point between a line and XY plane shifted along Z. + // Arguments: dir,pos - vector along the line and any point of the line + // z - z coordinate of plain + // Returns: none + // On exit: pos is the position if this intesection if any + static TVector3 nrm(0, 0, 1); + TVector3 pnt(0, 0, z); + + TVector3 diff = pnt - pos; + double sint = (nrm * diff) / (nrm * dir); + pos += sint * dir; +} // Propagate() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::refract(TVector3& dir, double n1, double n2) const +{ + // Refract direction vector according to Snell law + // Arguments: + // n1 - ref idx of first substance + // n2 - ref idx of second substance + // Returns: none + // On exit: dir is new direction + + double sinref = (n1 / n2) * TMath::Sin(dir.Theta()); + if (TMath::Abs(sinref) > 1.) { + dir.SetXYZ(-999, -999, -999); + } else { + dir.SetTheta(TMath::ASin(sinref)); + } +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +double Recon::houghResponse() +{ + // fIdxMip = mipId; + + Double_t kThetaMax = 0.75; + Int_t nChannels = (Int_t)(kThetaMax / fDTheta + 0.5); + TH1D* phots = new TH1D("Rphot", "phots", nChannels, 0, kThetaMax); + TH1D* photsw = new TH1D("RphotWeighted", "photsw", nChannels, 0, kThetaMax); + TH1D* resultw = new TH1D("resultw", "resultw", nChannels, 0, kThetaMax); + Int_t nBin = (Int_t)(kThetaMax / fDTheta); + Int_t nCorrBand = (Int_t)(fWindowWidth / (2 * fDTheta)); + + for (Int_t i = 0; i < fPhotCnt; i++) { // photon cadidates loop + Double_t angle = fPhotCkov[i]; + if (angle < 0 || angle > kThetaMax) { + continue; + } + phots->Fill(angle); + Int_t bin = (Int_t)(0.5 + angle / (fDTheta)); + Double_t weight = 1.; + if (fIsWEIGHT) { + Double_t lowerlimit = ((Double_t)bin) * fDTheta - 0.5 * fDTheta; + Double_t upperlimit = ((Double_t)bin) * fDTheta + 0.5 * fDTheta; + findRingGeom(lowerlimit); + Double_t areaLow = getRingArea(); + findRingGeom(upperlimit); + Double_t areaHigh = getRingArea(); + Double_t diffArea = areaHigh - areaLow; + if (diffArea > 0) { + weight = 1. / diffArea; + } + } + photsw->Fill(angle, weight); + fPhotWei[i] = weight; + } // photon candidates loop + + for (Int_t i = 1; i <= nBin; i++) { + Int_t bin1 = i - nCorrBand; + Int_t bin2 = i + nCorrBand; + if (bin1 < 1) { + bin1 = 1; + } + if (bin2 > nBin) { + bin2 = nBin; + } + Double_t sumPhots = phots->Integral(bin1, bin2); + if (sumPhots < 3) { + continue; + } // if less then 3 photons don't trust to this ring + Double_t sumPhotsw = photsw->Integral(bin1, bin2); + if ((Double_t)((i + 0.5) * fDTheta) > 0.7) { + continue; + } + resultw->Fill((Double_t)((i + 0.5) * fDTheta), sumPhotsw); + } + // evaluate the "BEST" theta ckov as the maximum value of histogramm + Double_t* pVec = resultw->GetArray(); + Int_t locMax = TMath::LocMax(nBin, pVec); + delete phots; + delete photsw; + delete resultw; // Reset and delete objects + + return (Double_t)(locMax * fDTheta + 0.5 * fDTheta); // final most probable track theta ckov + +} // HoughResponse() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +double Recon::findRingExt(double ckov, Int_t ch, double xPc, double yPc, double thRa, double phRa) +{ + // To find the acceptance of the ring even from external inputs. + // + // + double xRa = xPc - (fParam->radThick() + fParam->winThick() + fParam->gapThick()) * TMath::Cos(phRa) * TMath::Tan(thRa); // just linear extrapolation back to RAD + double yRa = yPc - (fParam->radThick() + fParam->winThick() + fParam->gapThick()) * TMath::Sin(phRa) * TMath::Tan(thRa); + + int nStep = 500; + int nPhi = 0; + + Int_t ipc, ipadx, ipady; + + if (ckov > 0) { + setTrack(xRa, yRa, thRa, phRa); + for (int j = 0; j < nStep; j++) { + TVector2 pos; + pos = tracePhot(ckov, j * TMath::TwoPi() / (double)(nStep - 1)); + if (Param::isInDead(pos.X(), pos.Y())) { + continue; + } + fParam->lors2Pad(pos.X(), pos.Y(), ipc, ipadx, ipady); + ipadx += (ipc % 2) * fParam->kPadPcX; + ipady += (ipc / 2) * fParam->kPadPcY; + if (ipadx < 0 || ipady > 160 || ipady < 0 || ipady > 144 || ch < 0 || ch > 6) { + continue; + } + if (Param::isDeadPad(ipadx, ipady, ch)) { + continue; + } + nPhi++; + } // point loop + return ((double)nPhi / (double)nStep); + } // if + return -1; +} diff --git a/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h b/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h index b55ba3b6c7353..9e9a78914049e 100644 --- a/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h +++ b/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h @@ -55,6 +55,7 @@ class Detector : public o2::base::DetImpl void EndOfEvent() override { Reset(); } // for the geometry sub-parts + TGeoVolume* createAbsorber(float tickness); TGeoVolume* createChamber(int number); TGeoVolume* CreateCradle(); TGeoVolume* CradleBaseVolume(TGeoMedium* med, double l[7], const char* name); diff --git a/Detectors/HMPID/simulation/include/HMPIDSimulation/HMPIDDigitizer.h b/Detectors/HMPID/simulation/include/HMPIDSimulation/HMPIDDigitizer.h index 9d843feda64bc..b01935f4cac5d 100644 --- a/Detectors/HMPID/simulation/include/HMPIDSimulation/HMPIDDigitizer.h +++ b/Detectors/HMPID/simulation/include/HMPIDSimulation/HMPIDDigitizer.h @@ -59,7 +59,7 @@ class HMPIDDigitizer } } } - + // void setOrbit(double timeNS) {mOrbit = } uint32_t getOrbit() { return mOrbit; }; uint16_t getBc() { return mBc; }; @@ -99,7 +99,7 @@ class HMPIDDigitizer std::vector mDigits; // internal store for digits constexpr static double TRACKHOLDTIME = 1200; // defines the window for pile-up after a trigger received in nanoseconds - constexpr static double BUSYTIME = 22000; // the time for which no new trigger can be received in nanoseconds + constexpr static double BUSYTIME = 40000; // the time for which no new trigger can be received in nanoseconds std::map mIndexForPad; //! logarithmic mapping of pad to digit index diff --git a/Detectors/HMPID/simulation/src/Detector.cxx b/Detectors/HMPID/simulation/src/Detector.cxx index 674bf7e85441d..83bab71e7177d 100644 --- a/Detectors/HMPID/simulation/src/Detector.cxx +++ b/Detectors/HMPID/simulation/src/Detector.cxx @@ -73,20 +73,20 @@ bool Detector::ProcessHits(FairVolume* v) Int_t volID = fMC->CurrentVolID(copy); auto stack = (o2::data::Stack*)fMC->GetStack(); - //Treat photons - //photon (Ckov or feedback) hits on module PC (Hpad) + // Treat photons + // photon (Ckov or feedback) hits on module PC (Hpad) if ((fMC->TrackPid() == 50000050 || fMC->TrackPid() == 50000051) && (volID == mHpad0VolID || volID == mHpad1VolID || volID == mHpad2VolID || volID == mHpad3VolID || volID == mHpad4VolID || volID == mHpad5VolID || volID == mHpad6VolID)) { - if (fMC->Edep() > 0) { //photon survided QE test i.e. produces electron + if (fMC->Edep() > 0) { // photon survided QE test i.e. produces electron if (IsLostByFresnel()) { fMC->StopTrack(); return false; - } //photon lost due to fersnel reflection on PC - Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); //take TID - Int_t pid = fMC->TrackPid(); //take PID - Float_t etot = fMC->Etot(); //total hpoton energy, [GeV] + } // photon lost due to fersnel reflection on PC + Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); // take TID + Int_t pid = fMC->TrackPid(); // take PID + Float_t etot = fMC->Etot(); // total hpoton energy, [GeV] Double_t x[3]; - fMC->TrackPosition(x[0], x[1], x[2]); //take MARS position at entrance to PC - Float_t hitTime = (Float_t)fMC->TrackTime(); //hit formation time + fMC->TrackPosition(x[0], x[1], x[2]); // take MARS position at entrance to PC + Float_t hitTime = (Float_t)fMC->TrackTime(); // hit formation time Int_t idch; // chamber number if (volID == mHpad0VolID) { idch = 0; @@ -104,20 +104,20 @@ bool Detector::ProcessHits(FairVolume* v) idch = 6; } Double_t xl, yl; - o2::hmpid::Param::instance()->mars2Lors(idch, x, xl, yl); //take LORS position - AddHit(x[0], x[1], x[2], hitTime, etot, tid, idch); //HIT for photon, position at P, etot will be set to Q - GenFee(etot); //generate feedback photons etot is modified in hit ctor to Q of hit + o2::hmpid::Param::instance()->mars2Lors(idch, x, xl, yl); // take LORS position + AddHit(x[0], x[1], x[2], hitTime, etot, tid, idch); // HIT for photon, position at P, etot will be set to Q + GenFee(etot); // generate feedback photons etot is modified in hit ctor to Q of hit stack->addHit(GetDetId()); - } //photon hit PC and DE >0 + } // photon hit PC and DE >0 return kTRUE; - } //photon hit PC + } // photon hit PC - //Treat charged particles - static Float_t eloss; //need to store mip parameters between different steps + // Treat charged particles + static Float_t eloss; // need to store mip parameters between different steps static Double_t in[3]; if (fMC->IsTrackEntering() && fMC->TrackCharge() && (volID == mHpad0VolID || volID == mHpad1VolID || volID == mHpad2VolID || volID == mHpad3VolID || volID == mHpad4VolID || volID == mHpad5VolID || volID == mHpad6VolID)) { - //Trackref stored when entering in the pad volume + // Trackref stored when entering in the pad volume o2::TrackReference tr(*fMC, GetDetId()); tr.setTrackID(stack->GetCurrentTrackNumber()); // tr.setUserId(lay); @@ -126,18 +126,18 @@ bool Detector::ProcessHits(FairVolume* v) if (fMC->TrackCharge() && (volID == mHcel0VolID || volID == mHcel1VolID || volID == mHcel2VolID || volID == mHcel3VolID || volID == mHcel4VolID || volID == mHcel5VolID || volID == mHcel6VolID)) { // charged particle in amplification gap (Hcel) - if (fMC->IsTrackEntering() || fMC->IsNewTrack()) { //entering or newly created - eloss = 0; //reset Eloss collector - fMC->TrackPosition(in[0], in[1], in[2]); //take position at the entrance - } else if (fMC->IsTrackExiting() || fMC->IsTrackStop() || fMC->IsTrackDisappeared()) { //exiting or disappeared - eloss += fMC->Edep(); //take into account last step Eloss - Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); //take TID - Int_t pid = fMC->TrackPid(); //take PID + if (fMC->IsTrackEntering() || fMC->IsNewTrack()) { // entering or newly created + eloss = 0; // reset Eloss collector + fMC->TrackPosition(in[0], in[1], in[2]); // take position at the entrance + } else if (fMC->IsTrackExiting() || fMC->IsTrackStop() || fMC->IsTrackDisappeared()) { // exiting or disappeared + eloss += fMC->Edep(); // take into account last step Eloss + Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); // take TID + Int_t pid = fMC->TrackPid(); // take PID Double_t out[3]; - fMC->TrackPosition(out[0], out[1], out[2]); //take MARS position at exit - Float_t hitTime = (Float_t)fMC->TrackTime(); //hit formation time + fMC->TrackPosition(out[0], out[1], out[2]); // take MARS position at exit + Float_t hitTime = (Float_t)fMC->TrackTime(); // hit formation time out[0] = 0.5 * (out[0] + in[0]); // - out[1] = 0.5 * (out[1] + in[1]); //take hit position at the anod plane + out[1] = 0.5 * (out[1] + in[1]); // take hit position at the anod plane out[2] = 0.5 * (out[2] + in[2]); Int_t idch; // chamber number if (volID == mHcel0VolID) { @@ -156,20 +156,20 @@ bool Detector::ProcessHits(FairVolume* v) idch = 6; } Double_t xl, yl; - o2::hmpid::Param::instance()->mars2Lors(idch, out, xl, yl); //take LORS position + o2::hmpid::Param::instance()->mars2Lors(idch, out, xl, yl); // take LORS position if (eloss > 0) { // HIT for MIP, position near anod plane, eloss will be set to Q AddHit(out[0], out[1], out[2], hitTime, eloss, tid, idch); - GenFee(eloss); //generate feedback photons + GenFee(eloss); // generate feedback photons stack->addHit(GetDetId()); eloss = 0; } } else { - //just going inside - eloss += fMC->Edep(); //collect this step eloss + // just going inside + eloss += fMC->Edep(); // collect this step eloss } return kTRUE; - } //MIP in GAP + } // MIP in GAP // later on return true if there was a hit! return false; @@ -187,14 +187,14 @@ void Detector::GenFee(Float_t qtot) // eloss=0 means photon so only pulse height distribution is to be analysed. TLorentzVector x4; fMC->TrackPosition(x4); - Int_t iNphotons = fMC->GetRandom()->Poisson(0.02 * qtot); //# of feedback photons is proportional to the charge of hit - //AliDebug(1,Form("N photons=%i",iNphotons)); + Int_t iNphotons = fMC->GetRandom()->Poisson(0.02 * qtot); // # of feedback photons is proportional to the charge of hit + // AliDebug(1,Form("N photons=%i",iNphotons)); Int_t j; Float_t cthf, phif, enfp = 0, sthf, e1[3], e2[3], e3[3], vmod, uswop, dir[3], phi, pol[3], mom[4]; - //Generate photons - for (Int_t i = 0; i < iNphotons; i++) { //feedbacks loop + // Generate photons + for (Int_t i = 0; i < iNphotons; i++) { // feedbacks loop Double_t ranf[2]; - fMC->GetRandom()->RndmArray(2, ranf); //Sample direction + fMC->GetRandom()->RndmArray(2, ranf); // Sample direction cthf = ranf[0] * 2 - 1.0; if (cthf < 0) { continue; @@ -276,8 +276,8 @@ void Detector::GenFee(Float_t qtot) } fMC->Gdtom(pol, pol, 2); Int_t outputNtracksStored; - } //feedbacks loop -} //GenerateFeedbacks() + } // feedbacks loop +} // GenerateFeedbacks() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Bool_t Detector::IsLostByFresnel() { @@ -301,7 +301,7 @@ Bool_t Detector::IsLostByFresnel() } else { return kFALSE; } -} //IsLostByFresnel() +} // IsLostByFresnel() //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) { @@ -325,8 +325,8 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) Float_t cn = csin[j] + ((csin[j + 1] - csin[j]) / 0.1) * (xe - en[j]); Float_t ck = csik[j] + ((csik[j + 1] - csik[j]) / 0.1) * (xe - en[j]); - //FORMULAE FROM HANDBOOK OF OPTICS, 33.23 OR - //W.R. HUNTER, J.O.S.A. 54 (1964),15 , J.O.S.A. 55(1965),1197 + // FORMULAE FROM HANDBOOK OF OPTICS, 33.23 OR + // W.R. HUNTER, J.O.S.A. 54 (1964),15 , J.O.S.A. 55(1965),1197 Float_t sinin = TMath::Sqrt((1. - pdoti) * (1. + pdoti)); Float_t tanin = sinin / pdoti; @@ -340,8 +340,8 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) Float_t rp = rs * ((aO - sinin * tanin) * (aO - sinin * tanin) + b2) / ((aO + sinin * tanin) * (aO + sinin * tanin) + b2); - //CORRECTION FACTOR FOR SURFACE ROUGHNESS - //B.J. STAGG APPLIED OPTICS, 30(1991),4113 + // CORRECTION FACTOR FOR SURFACE ROUGHNESS + // B.J. STAGG APPLIED OPTICS, 30(1991),4113 Float_t sigraf = 18.; Float_t lamb = 1240 / ene; @@ -351,7 +351,7 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) (4 * TMath::Pi() * pdoti * sigraf / lamb)); if (pola) { - Float_t pdotr = 0.8; //DEGREE OF POLARIZATION : 1->P , -1->S + Float_t pdotr = 0.8; // DEGREE OF POLARIZATION : 1->P , -1->S fresn = 0.5 * (rp * (1 + pdotr) + rs * (1 - pdotr)); } else { fresn = 0.5 * (rp + rs); @@ -359,7 +359,7 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) fresn = fresn * rO; return fresn; -} //Fresnel() +} // Fresnel() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Detector::Register() { FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, true); } @@ -537,7 +537,15 @@ void Detector::createMaterials() Material(++matId, "Ar", aAr, zAr, dAr, radAr, absAr); Medium(kAr, "Ar", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); } - +//************************************************************************************************** +TGeoVolume* Detector::createAbsorber(float tickness) +{ + double cm = 1, mm = 0.1 * cm, um = 0.001 * mm; // default is cm + auto& matmgr = o2::base::MaterialManager::Instance(); + TGeoMedium* al = matmgr.getTGeoMedium("HMP_Al"); + TGeoVolume* abs = gGeoManager->MakeBox("Habs", al, tickness * mm / 2, 1300.00 * mm / 2, 1300 * mm / 2); + return abs; +} //************************************************************************************************** TGeoVolume* Detector::createChamber(int number) @@ -1252,6 +1260,25 @@ void Detector::ConstructGeometry() TGeoVolume* hmpcradle = CreateCradle(); + TGeoVolume* hmpidabs_cham2 = createAbsorber(40.0); + TGeoVolume* hmpidabs_cham4 = createAbsorber(80.0); + + double theta = 33.5; + + TGeoHMatrix* pMatrixAbs2 = new TGeoHMatrix; + const double trans2[] = {435.5, 0., -155.}; + pMatrixAbs2->SetTranslation(trans2); + pMatrixAbs2->RotateZ(theta); + + gGeoManager->GetVolume("barrel")->AddNode(hmpidabs_cham2, 0, pMatrixAbs2); + + TGeoHMatrix* pMatrixAbs4 = new TGeoHMatrix; + const double trans4[] = {435., 0., 155.}; + pMatrixAbs4->SetTranslation(trans4); + pMatrixAbs4->RotateZ(theta); + + gGeoManager->GetVolume("barrel")->AddNode(hmpidabs_cham4, 0, pMatrixAbs4); + for (Int_t iCh = 0; iCh <= 6; iCh++) { // place 7 chambers TGeoVolume* hmpid = createChamber(iCh); TGeoHMatrix* pMatrix = new TGeoHMatrix; diff --git a/Detectors/HMPID/workflow/CMakeLists.txt b/Detectors/HMPID/workflow/CMakeLists.txt index 886e54dda8391..b532e6304fe96 100644 --- a/Detectors/HMPID/workflow/CMakeLists.txt +++ b/Detectors/HMPID/workflow/CMakeLists.txt @@ -18,6 +18,7 @@ o2_add_library(HMPIDWorkflow src/DigitsToClustersSpec.cxx src/DigitsWriterSpec.cxx src/ClustersWriterSpec.cxx + src/HMPMatchedWriterSpec.cxx src/DumpDigitsSpec.cxx src/PedestalsCalculationSpec.cxx src/RawToDigitsSpec.cxx @@ -25,16 +26,19 @@ o2_add_library(HMPIDWorkflow src/WriteRawFileSpec.cxx src/EntropyEncoderSpec.cxx src/EntropyDecoderSpec.cxx - - PUBLIC_LINK_LIBRARIES O2::Framework - O2::CCDB - O2::DPLUtils - O2::DetectorsRaw - O2::HMPIDBase - O2::DataFormatsHMP - O2::DataFormatsDCS - O2::HMPIDSimulation - O2::HMPIDReconstruction) + src/HMPMatchedReaderSpec.cxx + src/HMPMatchedWriterSpec.cxx + + PUBLIC_LINK_LIBRARIES + O2::Framework + O2::CCDB + O2::DPLUtils + O2::DetectorsRaw + O2::HMPIDBase + O2::DataFormatsHMP + O2::DataFormatsDCS + O2::HMPIDSimulation + O2::HMPIDReconstruction) o2_add_executable(digits-reader-workflow COMPONENT_NAME hmpid diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h deleted file mode 100644 index 6102ec481c97c..0000000000000 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZER_H_ -#define STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZER_H_ - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace hmpid -{ - -o2::framework::DataProcessorSpec getHMPIDClusterizerSpec(bool useMC); - -} // end namespace hmpid -} // end namespace o2 - -#endif /* STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZERSPEC_H_ */ diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h deleted file mode 100644 index eea9b134bd911..0000000000000 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file DigitReader.h - -#ifndef O2_HMPID_DIGITREADER -#define O2_HMPID_DIGITREADER - -#include "TFile.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "DataFormatsHMP/Digit.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -namespace o2 -{ -namespace hmpid -{ - -class DigitReader : public o2::framework::Task -{ - public: - DigitReader(bool useMC) : mUseMC(useMC) {} - ~DigitReader() override = default; - void init(o2::framework::InitContext& ic) final; - void run(o2::framework::ProcessingContext& pc) final; - - private: - int mState = 0; - bool mUseMC = true; - std::unique_ptr mFile = nullptr; - - std::vector mDigits, *mPdigits = &mDigits; - - o2::dataformats::MCTruthContainer mLabels, *mPlabels = &mLabels; -}; - -/// read simulated HMPID digits from a root file -framework::DataProcessorSpec getDigitReaderSpec(bool useMC); - -} // namespace hmpid -} // namespace o2 - -#endif /* O2_HMPID_DIGITREADER */ diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h index 8c64f326a6878..d03a30ab905e5 100644 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h +++ b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h @@ -24,7 +24,7 @@ namespace hmpid { /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt = "none"); } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h index 2fb9fd301f13b..9c2c4eb5b4fb0 100644 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h +++ b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h @@ -24,7 +24,7 @@ namespace hmpid { /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedReaderSpec.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedReaderSpec.h new file mode 100644 index 0000000000000..ec459aa94ab3e --- /dev/null +++ b/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedReaderSpec.h @@ -0,0 +1,61 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file HMPMatchedReaderSpec.h + +#ifndef O2_HMP_MATCHINFOREADER +#define O2_HMP_MATCHINFOREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "ReconstructionDataFormats/MatchInfoHMP.h" +#include "ReconstructionDataFormats/TrackTPCTOF.h" +#include "SimulationDataFormat/MCCompLabel.h" + +namespace o2 +{ +namespace hmpid +{ + +class HMPMatchedReader : public o2::framework::Task +{ + public: + HMPMatchedReader(bool useMC) : mUseMC(useMC) {} + ~HMPMatchedReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + private: + void connectTree(const std::string& filename); + + bool mUseMC = false; + // int mMode = 1; + + std::string mInFileName{"o2match_hmp.root"}; + std::string mInTreeName{"matchHMP"}; + std::unique_ptr mFile = nullptr; + std::unique_ptr mTree = nullptr; + std::vector mMatches, *mMatchesPtr = &mMatches; + // std::vector *mMatchesPtr = nullptr; + std::vector mLabelHMP, *mLabelHMPPtr = &mLabelHMP; +}; + +/// create a processor spec +/// read matched HMP clusters from a ROOT file +framework::DataProcessorSpec getHMPMatchedReaderSpec(bool useMC); + +} // namespace hmpid +} // namespace o2 + +#endif /* O2_HMP_MATCHINFOREADER */ diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedWriterSpec.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedWriterSpec.h new file mode 100644 index 0000000000000..f951a0fb05f0b --- /dev/null +++ b/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedWriterSpec.h @@ -0,0 +1,33 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file HMPMatchedWriterSpec.h + +#ifndef HMPWORKFLOW_HMPMATCHEDWRITER_H_ +#define HMPWORKFLOW_HMPMATCHEDWRITER_H_ + +#include "Framework/DataProcessorSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace hmpid +{ + +/// create a processor spec +/// write HMP matching info in a root file +o2::framework::DataProcessorSpec getHMPMatchedWriterSpec(bool useMC, const char* outdef = "o2match_hmpid.root"); // int mode = 0, bool strict = false); + +} // namespace hmpid +} // namespace o2 + +#endif /* HMPWORKFLOW_HMPMATCHEDWRITER_H_ */ diff --git a/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx b/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx index e71cd42845dd4..9ac5074acb505 100644 --- a/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx +++ b/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx @@ -71,8 +71,8 @@ void ClusterReaderTask::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - pc.outputs().snapshot(Output{"HMP", "CLUSTERS", 0, Lifetime::Timeframe}, mClustersFromFile); - pc.outputs().snapshot(Output{"HMP", "INTRECORDS1", 0, Lifetime::Timeframe}, mClusterTriggersFromFile); + pc.outputs().snapshot(Output{"HMP", "CLUSTERS", 0}, mClustersFromFile); + pc.outputs().snapshot(Output{"HMP", "INTRECORDS1", 0}, mClusterTriggersFromFile); mClustersReceived += mClustersFromFile.size(); LOG(info) << "[HMPID ClusterReader - run() ] clusters = " << mClustersFromFile.size(); diff --git a/Detectors/HMPID/workflow/src/ClustersWriterSpec.cxx b/Detectors/HMPID/workflow/src/ClustersWriterSpec.cxx index fae72edfa275a..3d5cdb19be338 100644 --- a/Detectors/HMPID/workflow/src/ClustersWriterSpec.cxx +++ b/Detectors/HMPID/workflow/src/ClustersWriterSpec.cxx @@ -33,8 +33,7 @@ o2::framework::DataProcessorSpec getClusterWriterSpec() return MakeRootTreeWriterSpec("HMPClustersWriter", "hmpidclusters.root", - "o2hmp", - 1, + MakeRootTreeWriterSpec::TreeAttributes{"o2hmp", "Tree with HMPID clusters"}, BranchDefinition>{InputSpec{"hmpclusterinput", "HMP", "CLUSTERS"}, "HMPIDclusters"}, BranchDefinition>{InputSpec{"hmpinteractionrecords", "HMP", "INTRECORDS1"}, "InteractionRecords"})(); } diff --git a/Detectors/HMPID/workflow/src/DataDecoderSpec.cxx b/Detectors/HMPID/workflow/src/DataDecoderSpec.cxx index e1e7e58d381f8..63f6552e70242 100644 --- a/Detectors/HMPID/workflow/src/DataDecoderSpec.cxx +++ b/Detectors/HMPID/workflow/src/DataDecoderSpec.cxx @@ -87,8 +87,8 @@ void DataDecoderTask::run(framework::ProcessingContext& pc) // decodeReadout(pc); // decodeRawFile(pc); - pc.outputs().snapshot(o2::framework::Output{"HMP", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mDeco->mDigits); - pc.outputs().snapshot(o2::framework::Output{"HMP", "INTRECORDS", 0, o2::framework::Lifetime::Timeframe}, mDeco->mIntReco); + pc.outputs().snapshot(o2::framework::Output{"HMP", "DIGITS", 0}, mDeco->mDigits); + pc.outputs().snapshot(o2::framework::Output{"HMP", "INTRECORDS", 0}, mDeco->mIntReco); LOG(debug) << "Writing Digitis=" << mDeco->mDigits.size() << "/" << mTotalDigits << " Frame=" << mTotalFrames << " IntRec " << mDeco->mIntReco; mExTimer.elapseMes("Decoding... Digits decoded = " + std::to_string(mTotalDigits) + " Frames received = " + std::to_string(mTotalFrames)); @@ -131,7 +131,7 @@ void DataDecoderTask::endOfStream(framework::EndOfStreamContext& ec) } } char summaryFileName[254]; - sprintf(summaryFileName, "%s_stat.txt", mRootStatFile.c_str()); + snprintf(summaryFileName, 254, "%s_stat.txt", mRootStatFile.c_str()); mDeco->writeSummaryFile(summaryFileName); for (int e = 0; e < numEqui; e++) { avgEventSize = mDeco->getAverageEventSize(e); @@ -178,7 +178,7 @@ void DataDecoderTask::decodeTF(framework::ProcessingContext& pc) if (payloadSize == 0) { auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", + LOGP(warning, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); } @@ -280,7 +280,7 @@ void DataDecoderTask::decodeRawFile(framework::ProcessingContext& pc) o2::framework::DataProcessorSpec getDecodingSpec(bool askDISTSTF) { std::vector inputs; - inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"HMP", "RAWDATA"}, o2::framework::Lifetime::Optional); + inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"HMP", "RAWDATA"}, o2::framework::Lifetime::Timeframe); if (askDISTSTF) { inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); } diff --git a/Detectors/HMPID/workflow/src/DataDecoderSpec2.cxx b/Detectors/HMPID/workflow/src/DataDecoderSpec2.cxx index 0721682ccb7a3..62a46ca5f30d8 100644 --- a/Detectors/HMPID/workflow/src/DataDecoderSpec2.cxx +++ b/Detectors/HMPID/workflow/src/DataDecoderSpec2.cxx @@ -93,8 +93,8 @@ void DataDecoderTask2::run(framework::ProcessingContext& pc) // Output the Digits/Triggers vector orderTriggers(); - pc.outputs().snapshot(o2::framework::Output{"HMP", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mDeco->mDigits); - pc.outputs().snapshot(o2::framework::Output{"HMP", "INTRECORDS", 0, o2::framework::Lifetime::Timeframe}, mTriggers); + pc.outputs().snapshot(o2::framework::Output{"HMP", "DIGITS", 0}, mDeco->mDigits); + pc.outputs().snapshot(o2::framework::Output{"HMP", "INTRECORDS", 0}, mTriggers); mExTimer.elapseMes("Decoding... Digits decoded = " + std::to_string(mTotalDigits) + " Frames received = " + std::to_string(mTotalFrames)); return; @@ -140,7 +140,7 @@ void DataDecoderTask2::endOfStream(framework::EndOfStreamContext& ec) } } char summaryFileName[254]; - sprintf(summaryFileName, "%s_stat.txt", mRootStatFile.c_str()); + snprintf(summaryFileName, 254, "%s_stat.txt", mRootStatFile.c_str()); mDeco->writeSummaryFile(summaryFileName); for (int e = 0; e < numEqui; e++) { avgEventSize = mDeco->getAverageEventSize(e); @@ -187,7 +187,7 @@ void DataDecoderTask2::decodeTF(framework::ProcessingContext& pc) if (payloadSize == 0) { auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", + LOGP(warning, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); } @@ -213,7 +213,7 @@ void DataDecoderTask2::decodeTF(framework::ProcessingContext& pc) // The stream end ! LOG(debug) << "End Page decoding !"; } - // std::cout << ">>>>" << pointerToTheFirst << "," << mDeco->mDigits.size() << std::endl; + // std::cout << " fDigit=" << pointerToTheFirst << " lDigit=," << mDeco->mDigits.size() << " nDigit=" << mDeco->mDigits.size()-pointerToTheFirst << std::endl; mTriggers.push_back(o2::hmpid::Trigger(mDeco->mIntReco, pointerToTheFirst, mDeco->mDigits.size() - pointerToTheFirst)); mTotalFrames++; } @@ -336,7 +336,7 @@ void DataDecoderTask2::orderTriggers() o2::framework::DataProcessorSpec getDecodingSpec2(bool askDISTSTF) { std::vector inputs; - inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"HMP", "RAWDATA"}, o2::framework::Lifetime::Optional); + inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"HMP", "RAWDATA"}, o2::framework::Lifetime::Timeframe); if (askDISTSTF) { inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); } diff --git a/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx b/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx index ed83db476b01a..88f6df2bce2e7 100644 --- a/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx +++ b/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx @@ -115,8 +115,8 @@ void DigitReader::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - pc.outputs().snapshot(Output{"HMP", "DIGITS", 0, Lifetime::Timeframe}, mDigitsFromFile); - pc.outputs().snapshot(Output{"HMP", "INTRECORDS", 0, Lifetime::Timeframe}, mTriggersFromFile); + pc.outputs().snapshot(Output{"HMP", "DIGITS", 0}, mDigitsFromFile); + pc.outputs().snapshot(Output{"HMP", "INTRECORDS", 0}, mTriggersFromFile); mDigitsReceived += mDigitsFromFile.size(); LOG(info) << "[HMPID DigitsReader - run() ] digits = " << mDigitsFromFile.size(); diff --git a/Detectors/HMPID/workflow/src/DigitsToClustersSpec.cxx b/Detectors/HMPID/workflow/src/DigitsToClustersSpec.cxx index 0f6d7d0cd5a6c..81c04a9875c51 100644 --- a/Detectors/HMPID/workflow/src/DigitsToClustersSpec.cxx +++ b/Detectors/HMPID/workflow/src/DigitsToClustersSpec.cxx @@ -108,30 +108,19 @@ void DigitsToClustersTask::run(framework::ProcessingContext& pc) size_t(trig.getNumberOfObjects())}; size_t clStart = clusters.size(); mRec->Dig2Clu(trigDigits, clusters, mSigmaCut, true); - clusterTriggers.emplace_back(trig.getIr(), clStart, - clusters.size() - clStart); + clusterTriggers.emplace_back(trig.getIr(), clStart, clusters.size() - clStart); } } LOGP(info, "Received {} triggers with {} digits -> {} triggers with {} clusters", - triggers.size(), digits.size(), clusterTriggers.size(), - clusters.size()); - + triggers.size(), digits.size(), clusterTriggers.size(), clusters.size()); mDigitsReceived += digits.size(); mClustersReceived += clusters.size(); - pc.outputs().snapshot( - o2::framework::Output{"HMP", "CLUSTERS", 0, - o2::framework::Lifetime::Timeframe}, - clusters); - pc.outputs().snapshot( - o2::framework::Output{"HMP", "INTRECORDS1", 0, - o2::framework::Lifetime::Timeframe}, - clusterTriggers); - - mExTimer.elapseMes("Clusterization of Digits received = " + - std::to_string(mDigitsReceived)); - mExTimer.elapseMes("Clusterization of Clusters received = " + - std::to_string(mClustersReceived)); + pc.outputs().snapshot(o2::framework::Output{"HMP", "CLUSTERS", 0}, clusters); + pc.outputs().snapshot(o2::framework::Output{"HMP", "INTRECORDS1", 0}, clusterTriggers); + + mExTimer.elapseMes("Clusterization of Digits received = " + std::to_string(mDigitsReceived)); + mExTimer.elapseMes("Clusterization of Clusters received = " + std::to_string(mClustersReceived)); } void DigitsToClustersTask::endOfStream(framework::EndOfStreamContext& ec) diff --git a/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx b/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx index 7c22c22c4927d..9ec05efc846fb 100644 --- a/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx @@ -26,11 +26,10 @@ namespace o2 { namespace hmpid { - class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,12 +41,13 @@ class EntropyDecoderSpec : public o2::framework::Task TStopwatch mTimer; }; -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); mCTFCoder.setSupportBCShifts(true); + mCTFCoder.setDictBinding("ctfdict_HMP"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -68,8 +68,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc); - auto buff = pc.inputs().get>("ctf"); + mCTFCoder.updateTimeDependentParams(pc, true); + auto buff = pc.inputs().get>("ctf_HMP"); auto& triggers = pc.outputs().make>(OutputRef{"triggers"}); auto& digits = pc.outputs().make>(OutputRef{"digits"}); @@ -90,7 +90,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "HMP", "INTRECORDS", 0, Lifetime::Timeframe}, @@ -98,17 +98,19 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) OutputSpec{{"ctfrep"}, "HMP", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "HMP", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionary")); + inputs.emplace_back("ctf_HMP", "HMP", "CTFDATA", sspec, Lifetime::Timeframe); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_HMP", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "hmpid-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx b/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx index d9f032ec5ef7f..c29c1cee459bc 100644 --- a/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace hmpid { - class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR = false); + EntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -44,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task TStopwatch mTimer; }; -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -66,13 +65,14 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - mCTFCoder.updateTimeDependentParams(pc); + mCTFCoder.updateTimeDependentParams(pc, true); auto triggers = pc.inputs().get>("triggers"); auto digits = pc.inputs().get>("digits"); if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{"HMP", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"HMP", "CTFDATA", 0}); + auto iosize = mCTFCoder.encode(buffer, triggers, digits); pc.outputs().snapshot({"ctfrep", 0}, iosize); if (mSelIR) { @@ -88,12 +88,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "HMP", "INTRECORDS", 0, Lifetime::Timeframe); inputs.emplace_back("digits", "HMP", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionary")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -102,12 +105,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"HMP", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "HMP", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/src/HMPMatchedReaderSpec.cxx b/Detectors/HMPID/workflow/src/HMPMatchedReaderSpec.cxx new file mode 100644 index 0000000000000..3ddb71e282a96 --- /dev/null +++ b/Detectors/HMPID/workflow/src/HMPMatchedReaderSpec.cxx @@ -0,0 +1,97 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file HMPMatchedReaderSpec.cxx + +#include + +#include "TTree.h" +#include "TFile.h" + +#include "HMPIDWorkflow/HMPMatchedReaderSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Headers/DataHeader.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "ReconstructionDataFormats/MatchInfoHMP.h" +#include "ReconstructionDataFormats/MatchingType.h" +#include "CommonUtils/StringUtils.h" +#include "CommonUtils/NameConf.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace hmpid +{ +void HMPMatchedReader::init(InitContext& ic) +{ + // get the option from the init context + LOG(debug) << "Init HMPID matching info reader!"; + connectTree(mInFileName); +} + +void HMPMatchedReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mInTreeName.c_str())); + assert(mTree); + mTree->SetBranchAddress("HMPMatchInfo", &mMatchesPtr); + if (mUseMC) { + mTree->SetBranchAddress("MatchHMPMCTruth", &mLabelHMPPtr); + } + LOG(debug) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +void HMPMatchedReader::run(ProcessingContext& pc) +{ + auto currEntry = mTree->GetReadEntry() + 1; + assert(currEntry < mTree->GetEntries()); // this should not happen + mTree->GetEntry(currEntry); + LOG(debug) << "Pushing " << mMatches.size() << " HMP matchings at entry " << currEntry; + + pc.outputs().snapshot(Output{o2::header::gDataOriginHMP, "MATCHES", 0}, mMatches); + if (mUseMC) { + pc.outputs().snapshot(Output{o2::header::gDataOriginHMP, "MCLABELS", 0}, mLabelHMP); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +DataProcessorSpec getHMPMatchedReaderSpec(bool useMC) +{ + std::vector outputs; + outputs.emplace_back(o2::header::gDataOriginHMP, "MATCHES", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back(o2::header::gDataOriginHMP, "MCLABELS", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "HMPMatchedReader", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask(useMC)}, + /*Options{ + {"hmp-matched-infile", VariantType::String, "o2match_hmp.root", {"Name of the input file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}, + {"treename", VariantType::String, "matchHMP", {"Name of top-level TTree"}}, + }*/ + }; +} +} // namespace hmpid +} // namespace o2 diff --git a/Detectors/HMPID/workflow/src/HMPMatchedWriterSpec.cxx b/Detectors/HMPID/workflow/src/HMPMatchedWriterSpec.cxx new file mode 100644 index 0000000000000..4c07ec2f5fc08 --- /dev/null +++ b/Detectors/HMPID/workflow/src/HMPMatchedWriterSpec.cxx @@ -0,0 +1,59 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file TOFMatchedWriterSpec.cxx + +#include "HMPIDWorkflow/HMPMatchedWriterSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "Headers/DataHeader.h" +#include +#include +#include "ReconstructionDataFormats/MatchInfoHMP.h" +#include "ReconstructionDataFormats/MatchingType.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +#include "DataFormatsHMP/Cluster.h" +#include "CommonUtils/StringUtils.h" +#include + +using namespace o2::framework; + +namespace o2 +{ +namespace hmpid +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using MatchInfo = std::vector; +using LabelsType = std::vector; +using namespace o2::header; + +DataProcessorSpec getHMPMatchedWriterSpec(bool useMC, const char* outdef) //, bool writeTracks, int mode, bool strict) +{ + + const char* taskName = "HMPMatchedWriter"; + + return MakeRootTreeWriterSpec(taskName, + outdef, + "matchHMP", + BranchDefinition{InputSpec{"hmpmatching", gDataOriginHMP, "MATCHES", 0}, + "HMPMatchInfo", + "HMPMatchInfo-branch-name"}, + BranchDefinition{InputSpec{"matchhmplabels", gDataOriginHMP, "MCLABELS", 0}, + "MatchHMPMCTruth", + "MatchHMPMCTruth-branch-name", + (useMC ? 1 : 0)})(); +} +} // namespace hmpid +} // namespace o2 diff --git a/Detectors/HMPID/workflow/src/RawToDigitsSpec.cxx b/Detectors/HMPID/workflow/src/RawToDigitsSpec.cxx index d94085917db73..bcdf722005c80 100644 --- a/Detectors/HMPID/workflow/src/RawToDigitsSpec.cxx +++ b/Detectors/HMPID/workflow/src/RawToDigitsSpec.cxx @@ -302,7 +302,7 @@ void RawToDigitsTask::writeResults() theObj[Geo::N_MODULES]->Branch("Average_Busy_Time", &avgBusyTime, "F"); char summaryFileName[254]; - sprintf(summaryFileName, "%s_stat.txt", mBaseFileName.c_str()); + snprintf(summaryFileName, 254, "%s_stat.txt", mBaseFileName.c_str()); mDecod->writeSummaryFile(summaryFileName); for (int e = 0; e < numEqui; e++) { avgEventSize = mDecod->getAverageEventSize(e); diff --git a/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx b/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx index fde5e0183abd6..4a1883233b605 100644 --- a/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::hmpid::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::hmpid::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/ITSMFT/ITS/CMakeLists.txt b/Detectors/ITSMFT/ITS/CMakeLists.txt index 760c4a2775d6c..708556ec8b7ec 100644 --- a/Detectors/ITSMFT/ITS/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/CMakeLists.txt @@ -15,5 +15,6 @@ add_subdirectory(simulation) add_subdirectory(reconstruction) add_subdirectory(tracking) add_subdirectory(workflow) +add_subdirectory(postprocessing) add_subdirectory(macros) add_subdirectory(QC) diff --git a/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx b/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx index d6ef8c947ed49..90ed033ed67da 100644 --- a/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx +++ b/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx @@ -139,14 +139,14 @@ void TestDataReader::run(ProcessingContext& pc) if (mDiffFolderName.size() == 0) { cout << "No New Run -- No Need to Reset" << endl; mResetCommand = 0; - pc.outputs().snapshot(Output{"ITS", "TEST", 0, Lifetime::Timeframe}, mResetCommand); + pc.outputs().snapshot(Output{"ITS", "TEST", 0}, mResetCommand); } // New folders found, send the reset signal and reload configuration if (mDiffFolderName.size() > 0) { cout << "New Run Started -- Reset All Histograms" << endl; mResetCommand = 1; - pc.outputs().snapshot(Output{"ITS", "TEST", 0, Lifetime::Timeframe}, mResetCommand); + pc.outputs().snapshot(Output{"ITS", "TEST", 0}, mResetCommand); for (int i = 0; i < sNError; i++) { mErrors[i] = 0; } @@ -210,7 +210,7 @@ void TestDataReader::run(ProcessingContext& pc) size_t pos = mNowFolderNames[i].find_last_of("/"); - if (pos != string::npos) { + if (pos != std::string::npos) { mRunID = mNowFolderNames[i].substr(pos + 1); } @@ -232,7 +232,7 @@ void TestDataReader::run(ProcessingContext& pc) // Getting the FileID string FileIDS; pos = mDiffFileNames[i][0].find_last_of("/"); - if (pos != string::npos) { + if (pos != std::string::npos) { FileIDS = mDiffFileNames[i][0].substr(pos + 1); } @@ -264,11 +264,11 @@ void TestDataReader::run(ProcessingContext& pc) mErrorsVecTest.push_back(mErrors); mFileDone = 1; mFileInfo = mFileDone + mFileRemain * 10; - pc.outputs().snapshot(Output{"ITS", "Run", 0, Lifetime::Timeframe}, mRunNumber); - pc.outputs().snapshot(Output{"ITS", "File", 0, Lifetime::Timeframe}, mFileID); - pc.outputs().snapshot(Output{"ITS", "Error", 0, Lifetime::Timeframe}, mErrorsVecTest[0]); - pc.outputs().snapshot(Output{"ITS", "Finish", 0, Lifetime::Timeframe}, mFileInfo); - pc.outputs().snapshot(Output{"ITS", "DIGITS", 0, Lifetime::Timeframe}, mMultiDigitsTest); + pc.outputs().snapshot(Output{"ITS", "Run", 0}, mRunNumber); + pc.outputs().snapshot(Output{"ITS", "File", 0}, mFileID); + pc.outputs().snapshot(Output{"ITS", "Error", 0}, mErrorsVecTest[0]); + pc.outputs().snapshot(Output{"ITS", "Finish", 0}, mFileInfo); + pc.outputs().snapshot(Output{"ITS", "DIGITS", 0}, mMultiDigitsTest); mNewFileInj = 0; mErrorsVecTest.clear(); mDigitsTest.clear(); @@ -422,10 +422,10 @@ void TestDataReader::run(ProcessingContext& pc) cout << "RunIDS = " << mRunNumber << " FileIDS = " << mFileID << endl; - pc.outputs().snapshot(Output{"ITS", "Run", 0, Lifetime::Timeframe}, mRunNumber); - pc.outputs().snapshot(Output{"ITS", "File", 0, Lifetime::Timeframe}, mFileID); + pc.outputs().snapshot(Output{"ITS", "Run", 0}, mRunNumber); + pc.outputs().snapshot(Output{"ITS", "File", 0}, mFileID); - pc.outputs().snapshot(Output{"ITS", "Error", 0, Lifetime::Timeframe}, mErrorsVec[j]); + pc.outputs().snapshot(Output{"ITS", "Error", 0}, mErrorsVec[j]); mIndexPushEx = mIndexPush + mNDigits[j]; LOG(debug) << "IndexPushEx = " << mIndexPushEx << " mDigits.size() " << mDigits.size(); if (mIndexPushEx > mDigits.size() - 5) { @@ -436,11 +436,11 @@ void TestDataReader::run(ProcessingContext& pc) mFileInfo = mFileDone + mFileRemain * 10; - pc.outputs().snapshot(Output{"ITS", "Finish", 0, Lifetime::Timeframe}, mFileInfo); + pc.outputs().snapshot(Output{"ITS", "Finish", 0}, mFileInfo); LOG(debug) << "mIndexPush = " << mIndexPush << " Chip ID Pushing " << mDigits[mIndexPush].getChipIndex(); - pc.outputs().snapshot(Output{"ITS", "DIGITS", 0, Lifetime::Timeframe}, mMultiDigits); + pc.outputs().snapshot(Output{"ITS", "DIGITS", 0}, mMultiDigits); mMultiDigits.clear(); mIndexPush = mIndexPush + mNDigits[j]; @@ -453,7 +453,7 @@ void TestDataReader::run(ProcessingContext& pc) // << "mIndexPush = " << mIndexPush << " mDigits.size() = " << mDigits.size(); // while (mIndexPush < mDigits.size()) { // LOG(debug) << "mDigits.size() = " << mDigits.size(); - // pc.outputs().snapshot(Output{"ITS", "DIGITS", 0, Lifetime::Timeframe}, mDigits[mIndexPush++]); + // pc.outputs().snapshot(Output{"ITS", "DIGITS", 0}, mDigits[mIndexPush++]); // if (mIndexPush % 100000 == 0) // LOG(debug) << "mIndexPush = " << mIndexPush << " Chip ID Pushing " << mDigits[mIndexPush].getChipIndex(); // } @@ -483,7 +483,7 @@ void TestDataReader::run(ProcessingContext& pc) j = 0; mNDigits.clear(); mFileDone = 1; - pc.outputs().snapshot(Output{"TST", "Finish", 0, Lifetime::Timeframe}, mFileDone); + pc.outputs().snapshot(Output{"TST", "Finish", 0}, mFileDone); PercentDone = 0; mErrorsVec.clear(); } @@ -509,7 +509,6 @@ void TestDataReader::run(ProcessingContext& pc) std::vector TestDataReader::GetFName(std::string folder) { - DIR* dirp; char cstr[folder.size() + 1]; diff --git a/Detectors/ITSMFT/ITS/base/include/ITSBase/DescriptorInnerBarrel.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/DescriptorInnerBarrel.h index 7e3032695ea00..5c7f7c665b5ba 100644 --- a/Detectors/ITSMFT/ITS/base/include/ITSBase/DescriptorInnerBarrel.h +++ b/Detectors/ITSMFT/ITS/base/include/ITSBase/DescriptorInnerBarrel.h @@ -36,7 +36,7 @@ class DescriptorInnerBarrel : public TObject DescriptorInnerBarrel(const DescriptorInnerBarrel& src) = delete; DescriptorInnerBarrel& operator=(const DescriptorInnerBarrel& geom) = delete; - double radii2Turbo(double rMin, double rMid, double rMax, double sensW) + double radii2Turbo(double rMin, double rMid, double rMax, double sensW) const { // compute turbo angle from radii and sensor width return TMath::ASin((rMax * rMax - rMin * rMin) / (2 * rMid * sensW)) * TMath::RadToDeg(); @@ -44,8 +44,9 @@ class DescriptorInnerBarrel : public TObject int getNumberOfLayers() const { return mNumLayers; } double getSensorThickness() const { return mSensorLayerThickness; } - void getConfigurationWrapperVolume(double& minradius, double& maxradius, double& zspan); - TGeoTube* defineWrapperVolume(); + void getConfigurationWrapperVolume(double& minradius, double& maxradius, double& zspan) const; + void setConfigurationWrapperVolume(double minradius, double maxradius, double zspan); + TGeoTube* defineWrapperVolume() const; protected: int mNumLayers{3}; @@ -70,4 +71,4 @@ class DescriptorInnerBarrel : public TObject } // namespace its } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h index 3e4dde5ee1a56..c8ef445e273d3 100644 --- a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h @@ -25,14 +25,15 @@ #include "DetectorsBase/GeometryManager.h" #include "DetectorsCommonDataFormats/DetID.h" #include "ITSMFTBase/GeometryTGeo.h" -#include "MathUtils/Utils.h" #include "Rtypes.h" // for Int_t, Double_t, Bool_t, UInt_t, etc +#ifdef ENABLE_UPGRADES +#include "ITS3Base/SpecsV2.h" +#endif + class TGeoPNEntry; -namespace o2 -{ -namespace its +namespace o2::its { /// GeometryTGeo is a simple interface class to TGeoManager. It is used in the simulation /// and reconstruction in order to query the TGeo ITS geometry. @@ -43,7 +44,7 @@ namespace its class GeometryTGeo : public o2::itsmft::GeometryTGeo { public: - typedef o2::math_utils::Transform3D Mat3D; + using Mat3D = o2::math_utils::Transform3D; using DetMatrixCache::getMatrixL2G; using DetMatrixCache::getMatrixT2GRot; using DetMatrixCache::getMatrixT2L; @@ -58,28 +59,39 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo return nullptr; // TODO: DR: Obviously wrong, but to make it compile for now #else if (!sInstance) { - sInstance = std::unique_ptr(new GeometryTGeo(true, 0)); + sInstance = std::make_unique(true, 0); } return sInstance.get(); #endif } - // adopt the unique instance from external raw pointer (to be used only to read saved instance from file) - static void adopt(GeometryTGeo* raw); + static bool instanceExist() + { +#ifdef GPUCA_STANDALONE + return false; +#else + return sInstance.get() != nullptr; +#endif + } + // Adopt the unique instance from external raw pointer (to be used only to read saved instance from file) + // When adopting an object owned by the CCDB cache we should not delete it + static void adopt(GeometryTGeo* raw, bool canDelete = false); // constructor // ATTENTION: this class is supposed to behave as a singleton, but to make it root-persistent // we must define public default constructor. // NEVER use it, it will throw exception if the class instance was already created // Use GeometryTGeo::Instance() instead - GeometryTGeo(bool build = kFALSE, int loadTrans = 0 - /*o2::base::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, // default transformations to load - o2::math_utils::TransformType::T2G, - o2::math_utils::TransformType::L2G)*/ - ); + GeometryTGeo(bool build = kFALSE, int loadTrans = 0); - /// Default destructor + /// Default destructor, don't use ~GeometryTGeo() override; + void destroy() + { +#ifndef GPUCA_STANDALONE + sInstance.reset(); // TODO: DR: See above +#endif + } GeometryTGeo(const GeometryTGeo& src) = delete; GeometryTGeo& operator=(const GeometryTGeo& geom) = delete; @@ -97,7 +109,12 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo int getNumberOfChipRowsPerModule(int lay) const { return mNumberOfChipRowsPerModule[lay]; } int getNumberOfChipColsPerModule(int lay) const { - return mNumberOfChipRowsPerModule[lay] ? mNumberOfChipsPerModule[lay] / mNumberOfChipRowsPerModule[lay] : -1; +#ifdef ENABLE_UPGRADES + if (mIsLayerITS3[lay]) { + return o2::its3::constants::pixelarray::nCols; + } +#endif + return mNumberOfChipRowsPerModule[lay] != 0 ? mNumberOfChipsPerModule[lay] / mNumberOfChipRowsPerModule[lay] : -1; } int getNumberOfChipsPerModule(int lay) const { return mNumberOfChipsPerModule[lay]; } @@ -159,7 +176,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo bool getChipId(int index, int& lay, int& hba, int& sta, int& ssta, int& mod, int& chip) const; /// Get chip layer, from 0 - int getLayer(int index) const; + int getLayer(int index) const final; /// Get chip half barrel, from 0 int getHalfBarrel(int index) const; @@ -199,7 +216,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo return getSymbolicName(getChipIndex(lay, hba, sta, det)); } - /// Get the transformation matrix for a given chip (NOT A SENSOR!!!) 'index' by quering the TGeoManager + /// Get the transformation matrix for a given chip (NOT A SENSOR!!!) 'index' by querying the TGeoManager TGeoHMatrix* getMatrix(int index) const { return o2::base::GeometryManager::getMatrix(getDetID(), index); } TGeoHMatrix* getMatrix(int lay, int hba, int sta, int sens) const { return getMatrix(getChipIndex(lay, hba, sta, sens)); } bool getOriginalMatrix(int index, TGeoHMatrix& m) const @@ -218,7 +235,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo const Mat3D& getMatrixT2L(int lay, int hba, int sta, int det) const { return getMatrixT2L(getChipIndex(lay, hba, sta, det)); } const Mat3D& getMatrixSensor(int index) const { return getMatrixL2G(index); } - const Mat3D& getMatrixSensor(int lay, int hba, int sta, int det) + const Mat3D& getMatrixSensor(int lay, int hba, int sta, int det) const { // get positioning matrix of the sensor, alias to getMatrixL2G return getMatrixSensor(getChipIndex(lay, hba, sta, det)); @@ -280,8 +297,24 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static const char* getITS3ModulePattern() { return sModuleNameITS3.c_str(); } static const char* getITS3ChipPattern() { return sChipNameITS3.c_str(); } static const char* getITS3SensorPattern() { return sSensorNameITS3.c_str(); } + + static const char* getITS3LayerPatternRaw() { return sLayerNameITS3.c_str(); }; + static const char* getITS3LayerPattern(int layer) { return Form("%s%d", getITS3LayerPatternRaw(), layer); }; + static const char* getITS3CarbonFormPatternRaw() { return sHalfBarrelNameITS3.c_str(); }; + static const char* getITS3CarbonFormPattern(int layer) { return Form("%s%d", getITS3CarbonFormPatternRaw(), layer); }; + static const char* getITS3ChipPatternRaw() { return sStaveNameITS3.c_str(); }; + static const char* getITS3ChipPattern(int layer) { return Form("%s%d", getITS3ChipPatternRaw(), layer); }; + static const char* getITS3SegmentPatternRaw() { return sHalfStaveNameITS3.c_str(); }; + static const char* getITS3SegmentPattern(int layer) { return Form("%s%d", getITS3SegmentPatternRaw(), layer); }; + static const char* getITS3RSUPatternRaw() { return sModuleNameITS3.c_str(); }; + static const char* getITS3RSUPattern(int layer) { return Form("%s%d", getITS3RSUPatternRaw(), layer); }; + static const char* getITS3TilePatternRaw() { return sChipNameITS3.c_str(); }; + static const char* getITS3TilePattern(int layer) { return Form("%s%d", getITS3TilePatternRaw(), layer); }; + static const char* getITS3PixelArrayPatternRaw() { return sSensorNameITS3.c_str(); }; + static const char* getITS3PixelArrayPattern(int layer) { return Form("%s%d", getITS3PixelArrayPatternRaw(), layer); }; + /// sym name of the layer - static const char* composeSymNameITS(bool isITS3 = false); + static const char* composeSymNameITS(); /// sym name of the layer static const char* composeSymNameLayer(int lr, bool isITS3 = false); @@ -300,15 +333,10 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo /// Sym name of the chip in the given layer/halfbarrel/stave/substave/module static const char* composeSymNameChip(int lr, int hba, int sta, int ssta, int mod, int chip, bool isITS3 = false); - // get tracking frame alpha for ITS3 clusters in global coordinates - float getAlphaFromGlobalITS3(int isn, o2::math_utils::Point3D gloXYZ); - - // create matrix for transformation from tracking frame to local one for ITS3 - const Mat3D getT2LMatrixITS3(int isn, float alpha); + TString getMatrixPath(int index) const; - protected: /// Get the transformation matrix of the SENSOR (not necessary the same as the chip) - /// for a given chip 'index' by quering the TGeoManager + /// for a given chip 'index' by querying the TGeoManager TGeoHMatrix* extractMatrixSensor(int index) const; // create matrix for transformation from sensor local frame to global one @@ -317,6 +345,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo // get sensor tracking frame alpha and x void extractSensorXAlpha(int isn, float& x, float& alp); + protected: /// This routine computes the layer number a given the chip index /// \param int index The chip index number, starting from zero. /// \param int indexInLr The chip index inside a layer, starting from zero. @@ -378,7 +407,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo std::vector mNumberOfChipsPerStave; ///< number of chips per stave std::vector mNumberOfChipsPerHalfBarrel; ///< number of chips per halfbarrel std::vector mNumberOfChipsPerLayer; ///< number of chips per stave - std::vector mLastChipIndex; ///< max ID of the detctor in the layer + std::vector mLastChipIndex; ///< max ID of the detector in the layer std::array mIsLayerITS3; ///< flag with the information of the ITS version (ITS2 or ITS3) std::array mLayerToWrapper; ///< Layer to wrapper correspondence @@ -395,22 +424,21 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static std::string sSensorName; ///< Sensor name static std::string sWrapperVolumeName; ///< Wrapper volume name - static std::string sLayerNameITS3; ///< Layer name for ITS3 - static std::string sHalfBarrelNameITS3; ///< HalfBarrel name for ITS3 - static std::string sStaveNameITS3; ///< Stave name for ITS3 - static std::string sHalfStaveNameITS3; ///< HalfStave name for ITS3 - static std::string sModuleNameITS3; ///< Module name for ITS3 - static std::string sChipNameITS3; ///< Chip name for ITS3 - static std::string sSensorNameITS3; ///< Sensor name for ITS3 + static const std::string sLayerNameITS3; ///< Layer name for ITS3 + static const std::string sHalfBarrelNameITS3; ///< HalfBarrel name for ITS3 + static const std::string sStaveNameITS3; ///< Stave name for ITS3 + static const std::string sHalfStaveNameITS3; ///< HalfStave name for ITS3 + static const std::string sModuleNameITS3; ///< Module name for ITS3 + static const std::string sChipNameITS3; ///< Chip name for ITS3 + static const std::string sSensorNameITS3; ///< Sensor name for ITS3 private: #ifndef GPUCA_STANDALONE static std::unique_ptr sInstance; ///< singletone instance #endif - ClassDefOverride(GeometryTGeo, 1); // ITS geometry based on TGeo + ClassDefOverride(GeometryTGeo, 2); // ITS geometry based on TGeo }; -} // namespace its -} // namespace o2 +} // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/base/src/DescriptorInnerBarrel.cxx b/Detectors/ITSMFT/ITS/base/src/DescriptorInnerBarrel.cxx index 3c9f93ec155df..aa7d1abd54e5e 100644 --- a/Detectors/ITSMFT/ITS/base/src/DescriptorInnerBarrel.cxx +++ b/Detectors/ITSMFT/ITS/base/src/DescriptorInnerBarrel.cxx @@ -52,7 +52,7 @@ DescriptorInnerBarrel::DescriptorInnerBarrel(int nlayers) : TObject(), mNumLayer } //________________________________________________________________ -void DescriptorInnerBarrel::getConfigurationWrapperVolume(double& minradius, double& maxradius, double& zspan) +void DescriptorInnerBarrel::getConfigurationWrapperVolume(double& minradius, double& maxradius, double& zspan) const { minradius = mWrapperMinRadius; maxradius = mWrapperMaxRadius; @@ -60,8 +60,17 @@ void DescriptorInnerBarrel::getConfigurationWrapperVolume(double& minradius, dou } //________________________________________________________________ -TGeoTube* DescriptorInnerBarrel::defineWrapperVolume() +void DescriptorInnerBarrel::setConfigurationWrapperVolume(double minradius, double maxradius, double zspan) +{ + mWrapperMinRadius = minradius; + mWrapperMaxRadius = maxradius; + mWrapperZSpan = zspan; +} + +//________________________________________________________________ +TGeoTube* DescriptorInnerBarrel::defineWrapperVolume() const { TGeoTube* wrap = new TGeoTube(mWrapperMinRadius, mWrapperMaxRadius, mWrapperZSpan / 2.); + LOGP(debug, "Creating IB Wrappervolume with Rmin={}, Rmax={}, ZSpan={}", mWrapperMinRadius, mWrapperMaxRadius, mWrapperZSpan); return wrap; -} \ No newline at end of file +} diff --git a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx index 65819b0f28cbf..5dc499d05037e 100644 --- a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx +++ b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx @@ -23,7 +23,7 @@ #include "MathUtils/Cartesian.h" #ifdef ENABLE_UPGRADES -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SpecsV2.h" #endif #include // for TGeoBBox @@ -43,6 +43,7 @@ #include // for isdigit #include // for snprintf, NULL, printf #include // for strstr, strlen +#include using namespace TMath; using namespace o2::its; @@ -50,14 +51,16 @@ using namespace o2::detectors; using Segmentation = o2::itsmft::SegmentationAlpide; -#ifdef ENABLE_UPGRADES -using SegmentationITS3 = o2::its3::SegmentationSuperAlpide; -#endif - ClassImp(o2::its::GeometryTGeo); std::unique_ptr GeometryTGeo::sInstance; -o2::its::GeometryTGeo::~GeometryTGeo() = default; +o2::its::GeometryTGeo::~GeometryTGeo() +{ + if (!mOwner) { + mOwner = true; + sInstance.release(); + } +} std::string GeometryTGeo::sVolumeName = "ITSV"; ///< Mother volume name std::string GeometryTGeo::sLayerName = "ITSULayer"; ///< Layer name @@ -69,13 +72,13 @@ std::string GeometryTGeo::sChipName = "ITSUChip"; ///< Chip name std::string GeometryTGeo::sSensorName = "ITSUSensor"; ///< Sensor name std::string GeometryTGeo::sWrapperVolumeName = "ITSUWrapVol"; ///< Wrapper volume name -std::string GeometryTGeo::sLayerNameITS3 = "ITS3Layer"; ///< Layer name for ITS3 -std::string GeometryTGeo::sHalfBarrelNameITS3 = "ITS3HalfBarrel"; ///< HalfBarrel name for ITS3 -std::string GeometryTGeo::sStaveNameITS3 = "ITS3Stave"; ///< Stave name for ITS3 -std::string GeometryTGeo::sHalfStaveNameITS3 = "ITS3HalfStave"; ///< HalfStave name for ITS3 -std::string GeometryTGeo::sModuleNameITS3 = "ITS3Module"; ///< Module name for ITS3 -std::string GeometryTGeo::sChipNameITS3 = "ITS3Chip"; ///< Chip name for ITS3 -std::string GeometryTGeo::sSensorNameITS3 = "ITS3Sensor"; ///< Sensor name for ITS3 +const std::string GeometryTGeo::sLayerNameITS3 = "ITS3Layer"; ///< Layer name for ITS3 +const std::string GeometryTGeo::sHalfBarrelNameITS3 = "ITS3CarbonForm"; ///< HalfBarrel name for ITS3 +const std::string GeometryTGeo::sStaveNameITS3 = "ITS3Chip"; ///< Stave name for ITS3 +const std::string GeometryTGeo::sHalfStaveNameITS3 = "ITS3Segment"; ///< HalfStave name for ITS3 +const std::string GeometryTGeo::sModuleNameITS3 = "ITS3RSU"; ///< Module name for ITS3 +const std::string GeometryTGeo::sChipNameITS3 = "ITS3Tile"; ///< Chip name for ITS3 +const std::string GeometryTGeo::sSensorNameITS3 = "ITS3PixelArray"; ///< Sensor name for ITS3 //__________________________________________________________________________ GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : o2::itsmft::GeometryTGeo(DetID::ITS) @@ -87,22 +90,21 @@ GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : o2::itsmft::GeometryTGeo // throw std::runtime_error("Invalid use of public constructor: o2::its::GeometryTGeo instance exists"); } - for (int i = MAXLAYERS; i--;) { - mLayerToWrapper[i] = -1; - } + mLayerToWrapper.fill(-1); if (build) { Build(loadTrans); } } //__________________________________________________________________________ -void GeometryTGeo::adopt(GeometryTGeo* raw) +void GeometryTGeo::adopt(GeometryTGeo* raw, bool canDelete) { // adopt the unique instance from external raw pointer (to be used only to read saved instance from file) if (sInstance) { LOG(fatal) << "No adoption: o2::its::GeometryTGeo instance exists"; } sInstance = std::unique_ptr(raw); + sInstance->mOwner = canDelete; } //__________________________________________________________________________ @@ -205,7 +207,7 @@ int GeometryTGeo::getModule(int index) const } index -= getFirstChipIndex(lay); index %= mNumberOfChipsPerStave[lay]; - if (mNumberOfHalfStaves[lay]) { + if (mNumberOfHalfStaves[lay] != 0) { index %= mNumberOfChipsPerHalfStave[lay]; } return index / mNumberOfChipsPerModule[lay]; @@ -288,14 +290,8 @@ bool GeometryTGeo::getChipId(int index, int& lay, int& hba, int& sta, int& hsta, } //__________________________________________________________________________ -const char* GeometryTGeo::composeSymNameITS(bool isITS3) +const char* GeometryTGeo::composeSymNameITS() { - if (isITS3) { -#ifdef ENABLE_UPGRADES - return o2::detectors::DetID(o2::detectors::DetID::IT3).getName(); -#endif - } - return o2::detectors::DetID(o2::detectors::DetID::ITS).getName(); } @@ -338,16 +334,8 @@ const char* GeometryTGeo::composeSymNameChip(int lr, int hba, int sta, int subst return Form("%s/%s%d", composeSymNameModule(lr, hba, sta, substave, mod, isITS3), isITS3 ? getITS3ChipPattern() : getITSChipPattern(), chip); } -//__________________________________________________________________________ -TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const +TString GeometryTGeo::getMatrixPath(int index) const { - // extract matrix transforming from the PHYSICAL sensor frame to global one - // Note, the if the effective sensitive layer thickness is smaller than the - // total physical sensor tickness, this matrix is biased and connot be used - // directly for transformation from sensor frame to global one. - // - // Therefore we need to add a shift - int lay, hba, stav, sstav, mod, chipInMod; getChipId(index, lay, hba, stav, sstav, mod, chipInMod); @@ -359,26 +347,50 @@ TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const path += Form("%s%d_1/", getITSWrapVolPattern(), wrID); } - path += - Form("%s%d_1/", mIsLayerITS3[lay] ? getITS3LayerPattern() : getITSLayerPattern(), lay); - - if (mNumberOfHalfBarrels > 0) { - path += Form("%s%d_%d/", mIsLayerITS3[lay] ? getITS3HalfBarrelPattern() : getITSHalfBarrelPattern(), lay, hba); - } - path += - Form("%s%d_%d/", mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern(), lay, stav); - - if (mNumberOfHalfStaves[lay] > 0) { - path += Form("%s%d_%d/", mIsLayerITS3[lay] ? getITS3HalfStavePattern() : getITSHalfStavePattern(), lay, sstav); - } - if (mNumberOfModules[lay] > 0) { - path += Form("%s%d_%d/", mIsLayerITS3[lay] ? getITS3ModulePattern() : getITSModulePattern(), lay, mod); - } if (!mIsLayerITS3[lay]) { + path += + Form("%s%d_1/", getITSLayerPattern(), lay); + if (mNumberOfHalfBarrels > 0) { + path += Form("%s%d_%d/", getITSHalfBarrelPattern(), lay, hba); + } + path += + Form("%s%d_%d/", getITSStavePattern(), lay, stav); + + if (mNumberOfHalfStaves[lay] > 0) { + path += Form("%s%d_%d/", getITSHalfStavePattern(), lay, sstav); + } + if (mNumberOfModules[lay] > 0) { + path += Form("%s%d_%d/", getITSModulePattern(), lay, mod); + } path += Form("%s%d_%d/%s%d_1", getITSChipPattern(), lay, chipInMod, getITSSensorPattern(), lay); } else { - path += Form("%s%d_%d", getITS3ChipPattern(), lay, chipInMod); // for ITS3 currently we might have more sensors than chips, so we have to take the chip to avoid mismatches with chipId + // hba = carbonform + // stav = 0 + // sstav = segment + // mod = rsu + // chipInMod = tile + // sensor = pixelarray + path += Form("%s_0/", getITS3LayerPattern(lay)); + path += Form("%s_%d/", getITS3CarbonFormPattern(lay), hba); + path += Form("%s_0/", getITS3ChipPattern(lay)); + path += Form("%s_%d/", getITS3SegmentPattern(lay), sstav); + path += Form("%s_%d/", getITS3RSUPattern(lay), mod); + path += Form("%s_%d/", getITS3TilePattern(lay), chipInMod); + path += Form("%s_0", getITS3PixelArrayPattern(lay)); } + return path; +} + +//__________________________________________________________________________ +TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const +{ + // extract matrix transforming from the PHYSICAL sensor frame to global one + // Note, the if the effective sensitive layer thickness is smaller than the + // total physical sensor tickness, this matrix is biased and connot be used + // directly for transformation from sensor frame to global one. + // + // Therefore we need to add a shift + auto path = getMatrixPath(index); static TGeoHMatrix matTmp; gGeoManager->PushPath(); @@ -390,59 +402,30 @@ TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const } // end if !gGeoManager matTmp = *gGeoManager->GetCurrentMatrix(); // matrix may change after cd + // RSS // printf("%d/%d/%d %s\n", lay, stav, detInSta, path.Data()); // matTmp.Print(); // Restore the modeler state. gGeoManager->PopPath(); - // account for the difference between physical sensitive layer (where charge collection is simulated) and effective sensor ticknesses - double delta = 0.; - if (mIsLayerITS3[lay]) { -#ifdef ENABLE_UPGRADES - delta = SegmentationITS3::mDetectorLayerThickness - SegmentationITS3::mSensorLayerThicknessEff; - TGeoTranslation tra(0., 0., 0.); - matTmp *= tra; - return &matTmp; -#endif - } + static int chipInGlo{0}; - delta = Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff; + // account for the difference between physical sensitive layer (where charge collection is simulated) and effective sensor thicknesses + // in the ITS3 case this accounted by specialized functions + double delta = Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff; static TGeoTranslation tra(0., 0.5 * delta, 0.); - +#ifdef ENABLE_UPGRADES // only apply for non ITS3 OB layers + if (!mIsLayerITS3[getLayer(index)]) { + matTmp *= tra; + } +#else matTmp *= tra; +#endif return &matTmp; } -//__________________________________________________________________________ -float GeometryTGeo::getAlphaFromGlobalITS3(int isn, o2::math_utils::Point3D gloXYZ) -{ - // calculate the tracking alpha of the ITS3 cluster in global coordinates - const TGeoHMatrix* matL2G = extractMatrixSensor(isn); - auto matG2L = matL2G->Inverse(); - auto translation = matG2L.GetTranslation(); // we only need the translation - o2::math_utils::Point3D gloXYZtra{gloXYZ.x() + (float)translation[0], gloXYZ.y() + (float)translation[1], gloXYZ.z() + (float)translation[2]}; - - float alp = ATan2(gloXYZtra.y(), gloXYZtra.x()); - o2::math_utils::bringTo02Pi(alp); - - return alp; -} - -//__________________________________________________________________________ -const o2::math_utils::Transform3D GeometryTGeo::getT2LMatrixITS3(int isn, float alpha) -{ - // create for sensor isn the TGeo matrix for Tracking to Local frame transformations - static TGeoHMatrix t2l; - t2l.Clear(); - t2l.RotateZ(alpha * RadToDeg()); // rotate in direction of normal to the tangent to the cylinder - const TGeoHMatrix* matL2G = extractMatrixSensor(isn); - const TGeoHMatrix& matL2Gi = matL2G->Inverse(); - t2l.MultiplyLeft(&matL2Gi); - return Mat3D(t2l); -} - //__________________________________________________________________________ void GeometryTGeo::Build(int loadTrans) { @@ -451,16 +434,14 @@ void GeometryTGeo::Build(int loadTrans) return; // already initialized } - if (!gGeoManager) { + if (gGeoManager == nullptr) { // RSTODO: in future there will be a method to load matrices from the CDB LOG(fatal) << "Geometry is not loaded"; } - for (int i = 0; i < MAXLAYERS; ++i) { - mIsLayerITS3[i] = false; - } + mIsLayerITS3.fill(false); mNumberOfLayers = extractNumberOfLayers(); - if (!mNumberOfLayers) { + if (mNumberOfLayers == 0) { return; } @@ -489,9 +470,16 @@ void GeometryTGeo::Build(int loadTrans) numberOfChips += mNumberOfChipsPerLayer[i]; mLastChipIndex[i] = numberOfChips - 1; } + +#ifdef ENABLE_UPGRADES + if (std::any_of(mIsLayerITS3.cbegin(), mIsLayerITS3.cend(), [](auto b) { return b; })) { + LOGP(info, "Found active IT3 layers -> Renaming Detector ITS to IT3"); + mDetID = DetID::IT3; + } +#endif + setSize(numberOfChips); fillTrackingFramesCache(); - // fillMatrixCache(loadTrans); } @@ -509,7 +497,7 @@ void GeometryTGeo::fillMatrixCache(int mask) // build matrices if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)) && !getCacheL2G().isFilled()) { // Matrices for Local (Sensor!!! rather than the full chip) to Global frame transformation - LOG(info) << "Loading ITS L2G matrices from TGeo"; + LOGP(info, "Loading {} L2G matrices from TGeo; there are {} matrices", getName(), mSize); auto& cacheL2G = getCacheL2G(); cacheL2G.setSize(mSize); @@ -521,7 +509,7 @@ void GeometryTGeo::fillMatrixCache(int mask) if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)) && !getCacheT2L().isFilled()) { // matrices for Tracking to Local (Sensor!!! rather than the full chip) frame transformation - LOG(info) << "Loading ITS T2L matrices from TGeo"; + LOGP(info, "Loading {} T2L matrices from TGeo", getName()); auto& cacheT2L = getCacheT2L(); cacheT2L.setSize(mSize); for (int i = 0; i < mSize; i++) { @@ -530,29 +518,34 @@ void GeometryTGeo::fillMatrixCache(int mask) } } - if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2G)) && !getCacheT2G().isFilled()) { - LOG(warning) << "It is faster to use 2D rotation for T2G instead of full Transform3D matrices"; - // matrices for Tracking to Global frame transformation - LOG(info) << "Loading ITS T2G matrices from TGeo"; - auto& cacheT2G = getCacheT2G(); - cacheT2G.setSize(mSize); - - for (int i = 0; i < mSize; i++) { - TGeoHMatrix& mat = createT2LMatrix(i); - mat.MultiplyLeft(extractMatrixSensor(i)); - cacheT2G.setMatrix(Mat3D(mat), i); - } - } - if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot)) && !getCacheT2GRot().isFilled()) { // 2D rotation matrices for Tracking frame to Global rotations - LOG(info) << "Loading ITS T2G rotation 2D matrices"; + LOGP(info, "Loading {} T2G rotation 2D matrices", getName()); auto& cacheT2Gr = getCacheT2GRot(); cacheT2Gr.setSize(mSize); for (int i = 0; i < mSize; i++) { cacheT2Gr.setMatrix(Rot2D(getSensorRefAlpha(i)), i); } } + + if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2G)) && !getCacheT2G().isFilled()) { + LOG(debug) << "It is faster to use 2D rotation for T2G instead of full Transform3D matrices"; + // matrices for Tracking to Global frame transformation + LOGP(info, "Creating {} T2G matrices from TGeo", getName()); + auto& cacheT2G = getCacheT2G(); + cacheT2G.setSize(mSize); + + for (int i = 0; i < mSize; i++) { + /* + TGeoHMatrix& mat = createT2LMatrix(i); + mat.MultiplyLeft(extractMatrixSensor(i)); + */ + Rot2D r(getSensorRefAlpha(i)); + Mat3D mat{}; + mat.SetComponents(r.getCos(), -r.getSin(), 0., 0., r.getSin(), r.getCos(), 0., 0., 0., 0., 1., 0.); + cacheT2G.setMatrix(mat, i); + } + } } //__________________________________________________________________________ @@ -575,47 +568,42 @@ int GeometryTGeo::extractNumberOfLayers() int numberOfLayers = 0; TGeoVolume* itsV = gGeoManager->GetVolume(getITSVolPattern()); - if (!itsV) { - LOG(fatal) << "ITS volume " << getITSVolPattern() << " is not in the geometry"; + if (itsV == nullptr) { + LOG(fatal) << getName() << " volume " << getITSVolPattern() << " is not in the geometry"; } // Loop on all ITSV nodes, count Layer volumes by checking names // Build on the fly layer - wrapper correspondence TObjArray* nodes = itsV->GetNodes(); int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { int lrID = -1; - TGeoNode* nd = (TGeoNode*)nodes->At(j); + auto nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); - if (strstr(name, getITSLayerPattern()) || strstr(name, getITS3LayerPattern())) { + if ((strstr(name, getITSLayerPattern()) != nullptr) || (strstr(name, getITS3LayerPattern()) != nullptr)) { numberOfLayers++; if ((lrID = extractVolumeCopy(name, GeometryTGeo::getITSLayerPattern())) < 0) { if ((lrID = extractVolumeCopy(name, GeometryTGeo::getITS3LayerPattern())) < 0) { LOG(fatal) << "Failed to extract layer ID from the " << name; - exit(1); } mIsLayerITS3[lrID] = true; } - mLayerToWrapper[lrID] = -1; // not wrapped - } else if (strstr(name, getITSWrapVolPattern())) { // this is a wrapper volume, may cointain layers + mLayerToWrapper[lrID] = -1; // not wrapped + } else if (strstr(name, getITSWrapVolPattern()) != nullptr) { // this is a wrapper volume, may cointain layers int wrID = -1; if ((wrID = extractVolumeCopy(name, GeometryTGeo::getITSWrapVolPattern())) < 0) { LOG(fatal) << "Failed to extract wrapper ID from the " << name; - exit(1); } - TObjArray* nodesW = nd->GetNodes(); int nNodesW = nodesW->GetEntriesFast(); for (int jw = 0; jw < nNodesW; jw++) { - TGeoNode* ndW = (TGeoNode*)nodesW->At(jw); - if (strstr(ndW->GetName(), getITSLayerPattern()) || strstr(ndW->GetName(), getITS3LayerPattern())) { - if ((lrID = extractVolumeCopy(ndW->GetName(), GeometryTGeo::getITSLayerPattern())) < 0) { - if ((lrID = extractVolumeCopy(ndW->GetName(), GeometryTGeo::getITS3LayerPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; - exit(1); + auto ndW = dynamic_cast(nodesW->At(jw))->GetName(); + if ((strstr(ndW, getITSLayerPattern()) != nullptr) || (strstr(ndW, getITS3LayerPattern()) != nullptr)) { + if ((lrID = extractVolumeCopy(ndW, GeometryTGeo::getITSLayerPattern())) < 0) { + if ((lrID = extractVolumeCopy(ndW, GeometryTGeo::getITS3LayerPattern())) < 0) { + LOGP(fatal, "Failed to extract layer ID from wrapper volume '{}' from one of its nodes '{}'", name, ndW); } mIsLayerITS3[lrID] = true; } @@ -648,8 +636,8 @@ int GeometryTGeo::extractNumberOfStaves(int lay) const snprintf(hbarnam, 30, "%s%d", mIsLayerITS3[lay] ? getITS3HalfBarrelPattern() : getITSHalfBarrelPattern(), lay); } TGeoVolume* volHb = gGeoManager->GetVolume(hbarnam); - if (!volHb) { - LOG(fatal) << "can't find " << hbarnam << " volume"; + if (volHb == nullptr) { + LOGP(fatal, "Can't find '{}' volume (ITS3={})", hbarnam, mIsLayerITS3[lay]); return -1; } @@ -659,7 +647,7 @@ int GeometryTGeo::extractNumberOfStaves(int lay) const // LOG(info) << "L" << lay << " " << j << " of " << nNodes << " " // << volHb->GetNodes()->At(j)->GetName() << " " // << mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern() << " -> " << numberOfStaves; - if (strstr(volHb->GetNodes()->At(j)->GetName(), mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern())) { + if (strstr(volHb->GetNodes()->At(j)->GetName(), mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern()) != nullptr) { numberOfStaves++; } } @@ -676,13 +664,13 @@ int GeometryTGeo::extractNumberOfHalfStaves(int lay) const char stavnam[30]; snprintf(stavnam, 30, "%s%d", mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern(), lay); TGeoVolume* volLd = gGeoManager->GetVolume(stavnam); - if (!volLd) { + if (volLd == nullptr) { LOG(fatal) << "can't find volume " << stavnam; } // Loop on all stave nodes, count Chip volumes by checking names int nNodes = volLd->GetNodes()->GetEntries(); for (int j = 0; j < nNodes; j++) { - if (strstr(volLd->GetNodes()->At(j)->GetName(), mIsLayerITS3[lay] ? getITS3HalfStavePattern() : getITSHalfStavePattern())) { + if (strstr(volLd->GetNodes()->At(j)->GetName(), mIsLayerITS3[lay] ? getITS3HalfStavePattern() : getITSHalfStavePattern()) != nullptr) { nSS++; } } @@ -727,22 +715,31 @@ int GeometryTGeo::extractNumberOfModules(int lay) const //__________________________________________________________________________ int GeometryTGeo::extractNumberOfChipsPerModule(int lay, int& nrow) const { +#ifdef ENABLE_UPGRADES + // FS: TODO + // For now we hardcode ITS3 number of chips is eq. to the number of tiles per RSU + // The test in the end does not work for ITS3. + if (mIsLayerITS3[lay]) { + nrow = o2::its3::constants::pixelarray::nRows; + return o2::its3::constants::rsu::nTiles; + } +#endif int numberOfChips = 0; char stavnam[30]; TGeoVolume* volLd = nullptr; if (!sModuleName.empty()) { - snprintf(stavnam, 30, "%s%d", mIsLayerITS3[lay] ? getITS3ModulePattern() : getITSModulePattern(), lay); + snprintf(stavnam, 30, "%s%d", getITSModulePattern(), lay); volLd = gGeoManager->GetVolume(stavnam); } if (!volLd) { // no modules on this layer, check substaves if (!sHalfStaveName.empty()) { - snprintf(stavnam, 30, "%s%d", mIsLayerITS3[lay] ? getITS3HalfStavePattern() : getITSHalfStavePattern(), lay); + snprintf(stavnam, 30, "%s%d", getITSHalfStavePattern(), lay); volLd = gGeoManager->GetVolume(stavnam); } } if (!volLd) { // no substaves on this layer, check staves - snprintf(stavnam, 30, "%s%d", mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern(), lay); + snprintf(stavnam, 30, "%s%d", getITSStavePattern(), lay); volLd = gGeoManager->GetVolume(stavnam); } if (!volLd) { @@ -757,9 +754,8 @@ int GeometryTGeo::extractNumberOfChipsPerModule(int lay, int& nrow) const double dx = -1, dz = -1; for (int j = 0; j < nNodes; j++) { - LOGP(debug, "layer={}, node/nodes={}/{}, node name={}, pattern={}, number of chips={}, is ITS3 layer={}", lay, j, nNodes, volLd->GetNodes()->At(j)->GetName(), mIsLayerITS3[lay] ? getITS3ChipPattern() : getITSChipPattern(), numberOfChips, mIsLayerITS3[lay]); TGeoNodeMatrix* node = (TGeoNodeMatrix*)volLd->GetNodes()->At(j); - if (!strstr(node->GetName(), mIsLayerITS3[lay] ? getITS3ChipPattern() : getITSChipPattern())) { + if (strstr(node->GetName(), getITSChipPattern()) == nullptr) { continue; } node->LocalToMaster(loc, lab); @@ -797,7 +793,9 @@ int GeometryTGeo::extractNumberOfChipsPerModule(int lay, int& nrow) const if (nrow * ncol != numberOfChips) { LOG(error) << "Inconsistency between Nchips=" << numberOfChips << " and Nrow*Ncol=" << nrow << "*" << ncol << "->" << nrow * ncol << "\n" - << "Extracted chip dimensions (x,z): " << dx << " " << dz << " Module Span: " << spanX << " " << spanZ; + << "Extracted chip dimensions (x,z): " << dx << " " << dz << " Module Span: " << spanX << " " << spanZ << "\n" + << "xmin=" << xmin << " xmax=" << xmax + << " zmin=" << zmin << " zmax=" << zmax; } return numberOfChips; } @@ -818,19 +816,21 @@ int GeometryTGeo::extractLayerChipType(int lay) const //__________________________________________________________________________ void GeometryTGeo::Print(Option_t*) const { - printf("NLayers:%d NChips:%d\n", mNumberOfLayers, getNumberOfChips()); if (!isBuilt()) { + LOGF(info, "Geometry not built yet!"); return; } + LOGF(info, "Summary of GeometryTGeo: %s", getName()); + LOGF(info, "NLayers:%d NChips:%d\n", mNumberOfLayers, getNumberOfChips()); for (int i = 0; i < mNumberOfLayers; i++) { - printf( - "Lr%2d\tNStav:%2d\tNChips:%2d " - "(%dx%-2d)\tNMod:%d\tNSubSt:%d\tNSt:%3d\tChip#:%5d:%-5d\tWrapVol:%d\n", - i, mNumberOfStaves[i], mNumberOfChipsPerModule[i], mNumberOfChipRowsPerModule[i], - mNumberOfChipRowsPerModule[i] ? mNumberOfChipsPerModule[i] / mNumberOfChipRowsPerModule[i] : 0, - mNumberOfModules[i], mNumberOfHalfStaves[i], mNumberOfStaves[i], getFirstChipIndex(i), getLastChipIndex(i), - mLayerToWrapper[i]); + LOGF(info, + "Lr%2d\tNStav:%2d\tNChips:%2d " + "(%dx%-2d)\tNMod:%d\tNSubSt:%d\tNSt:%3d\tChip#:%5d:%-5d\tWrapVol:%d", + i, mNumberOfStaves[i], mNumberOfChipsPerModule[i], mNumberOfChipRowsPerModule[i], + mNumberOfChipRowsPerModule[i] ? mNumberOfChipsPerModule[i] / mNumberOfChipRowsPerModule[i] : 0, + mNumberOfModules[i], mNumberOfHalfStaves[i], mNumberOfStaves[i], getFirstChipIndex(i), getLastChipIndex(i), + mLayerToWrapper[i]); } } @@ -842,25 +842,39 @@ void GeometryTGeo::extractSensorXAlpha(int isn, float& x, float& alp) const TGeoHMatrix* matL2G = extractMatrixSensor(isn); double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; - int iLayer = getLayer(isn); + double xp{0}, yp{0}; - if (mIsLayerITS3[iLayer]) { - // in this case we need the line tangent to the circumference - double radius = 0.; #ifdef ENABLE_UPGRADES - radius = SegmentationITS3::mRadii[iLayer]; -#endif - locA[1] = radius; - locB[1] = radius; - } - + if (int iLayer = getLayer(isn); mIsLayerITS3[iLayer]) { + // For a TGeoTubeSeg the local coordinate system is defined at the origin + // of the circle of the side, since in our implementation we rotated the geometry a bit + const auto radius = o2::its3::constants::radii[iLayer]; + const auto phi1 = o2::its3::constants::tile::width / radius; + const auto phi2 = o2::its3::constants::pixelarray::width / radius + phi1; + const auto phi3 = (phi2 - phi1) / 2.; // mid-point in phi + locA[0] = radius * std::cos(phi3); + locA[1] = radius * std::sin(phi3); + matL2G->LocalToMaster(locA, gloA); + xp = gloA[0]; + yp = gloA[1]; + } else { + matL2G->LocalToMaster(locA, gloA); + matL2G->LocalToMaster(locB, gloB); + double dx = gloB[0] - gloA[0], dy = gloB[1] - gloA[1]; + double t = (gloB[0] * dx + gloB[1] * dy) / (dx * dx + dy * dy); + xp = gloB[0] - dx * t; + yp = gloB[1] - dy * t; + } +#else // just ITS2 part matL2G->LocalToMaster(locA, gloA); matL2G->LocalToMaster(locB, gloB); double dx = gloB[0] - gloA[0], dy = gloB[1] - gloA[1]; double t = (gloB[0] * dx + gloB[1] * dy) / (dx * dx + dy * dy); - double xp = gloB[0] - dx * t, yp = gloB[1] - dy * t; - x = Sqrt(xp * xp + yp * yp); - alp = ATan2(yp, xp); + xp = gloB[0] - dx * t; + yp = gloB[1] - dy * t; +#endif + x = std::hypot(xp, yp); + alp = std::atan2(yp, xp); o2::math_utils::bringTo02Pi(alp); } diff --git a/Detectors/ITSMFT/ITS/calibration/include/ITSCalibration/NoiseSlotCalibrator.h b/Detectors/ITSMFT/ITS/calibration/include/ITSCalibration/NoiseSlotCalibrator.h index 9373482fa1c3b..a467cbd30c2b8 100644 --- a/Detectors/ITSMFT/ITS/calibration/include/ITSCalibration/NoiseSlotCalibrator.h +++ b/Detectors/ITSMFT/ITS/calibration/include/ITSCalibration/NoiseSlotCalibrator.h @@ -35,7 +35,7 @@ class ROFRecord; namespace its { -class NoiseSlotCalibrator : public o2::calibration::TimeSlotCalibration +class NoiseSlotCalibrator final : public o2::calibration::TimeSlotCalibration { using Slot = calibration::TimeSlot; diff --git a/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibrator.cxx b/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibrator.cxx index 04a87f06480f3..3ffa500b8516e 100644 --- a/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibrator.cxx +++ b/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibrator.cxx @@ -32,7 +32,7 @@ bool NoiseCalibrator::processTimeFrameClusters(gsl::span const& rofs) { static int nTF = 0; - LOG(info) << "Processing TF# " << nTF++ << " of " << clusters.size() << " clusters in" << rofs.size() << " ROFs"; + LOG(detail) << "Processing TF# " << nTF++ << " of " << clusters.size() << " clusters in" << rofs.size() << " ROFs"; // extract hits auto pattIt = patterns.begin(); mChipIDs.clear(); @@ -104,7 +104,7 @@ bool NoiseCalibrator::processTimeFrameDigits(gsl::span gsl::span const& rofs) { static int nTF = 0; - LOG(info) << "Processing TF# " << nTF++ << " of " << digits.size() << " digits in " << rofs.size() << " ROFs"; + LOG(detail) << "Processing TF# " << nTF++ << " of " << digits.size() << " digits in " << rofs.size() << " ROFs"; mChipIDs.clear(); for (const auto& rof : rofs) { int chipID = -1; diff --git a/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibratorSpec.cxx b/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibratorSpec.cxx index 479dcad6e4b28..e50f5be3edf5e 100644 --- a/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibratorSpec.cxx +++ b/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibratorSpec.cxx @@ -105,13 +105,13 @@ void NoiseCalibratorSpec::run(ProcessingContext& pc) done = (++mNPartsDone == partInfo[1]); mStrobeCounter += partInfo[2]; mCalibrator->setNStrobes(mStrobeCounter); - LOGP(info, "Received accumulated map {} of {} with {} ROFs, total number of maps = {} and strobes = {}", partInfo[0] + 1, partInfo[1], partInfo[2], mNPartsDone, mCalibrator->getNStrobes()); + LOGP(important, "Received accumulated map {} of {} with {} ROFs, total number of maps = {} and strobes = {}", partInfo[0] + 1, partInfo[1], partInfo[2], mNPartsDone, mCalibrator->getNStrobes()); } if (done || pc.transitionState() == TransitionHandlingState::Requested) { if (done) { - LOG(info) << "Minimum number of noise counts has been reached !"; + LOG(important) << "Minimum number of noise counts has been reached !"; } else { - LOG(info) << "Run stop is requested, sending output"; + LOG(important) << "Run stop is requested, sending output"; } if (mMode == ProcessingMode::Full || mMode == ProcessingMode::Normalize) { sendOutput(pc.outputs()); @@ -134,7 +134,7 @@ void NoiseCalibratorSpec::sendAccumulatedMap(DataAllocator& output) outInf.push_back(mCalibrator->getNInstances()); outInf.push_back(mCalibrator->getNStrobes()); output.snapshot(Output{"ITS", "NOISEMAPPARTINF", (unsigned int)mCalibrator->getInstanceID()}, outInf); - LOGP(info, "Sending accumulated map with {} ROFs processed", mCalibrator->getNStrobes()); + LOGP(important, "Sending accumulated map with {} ROFs processed", mCalibrator->getNStrobes()); } void NoiseCalibratorSpec::sendOutput(DataAllocator& output) @@ -271,9 +271,9 @@ DataProcessorSpec getNoiseCalibratorSpec(bool useClusters, int pmode) inputs.emplace_back("ROframes", "ITS", "DIGITSROF", 0, Lifetime::Timeframe); } } else { - useClusters = false; // not needed for normalization - inputs.emplace_back("mapspart", ConcreteDataTypeMatcher{"ITS", "NOISEMAPPART"}, Lifetime::Timeframe); // for normalization of multiple inputs only - inputs.emplace_back("mapspartInfo", ConcreteDataTypeMatcher{"ITS", "NOISEMAPPARTINF"}, Lifetime::Timeframe); // for normalization of multiple inputs only + useClusters = false; // not needed for normalization + inputs.emplace_back("mapspart", ConcreteDataTypeMatcher{"ITS", "NOISEMAPPART"}, Lifetime::Sporadic); // for normalization of multiple inputs only + inputs.emplace_back("mapspartInfo", ConcreteDataTypeMatcher{"ITS", "NOISEMAPPARTINF"}, Lifetime::Sporadic); // for normalization of multiple inputs only } if (md == NoiseCalibratorSpec::ProcessingMode::Full || md == NoiseCalibratorSpec::ProcessingMode::Normalize) { inputs.emplace_back("confdbmap", "ITS", "CONFDBMAP", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/Confdbmap")); diff --git a/Detectors/ITSMFT/ITS/calibration/src/NoiseSlotCalibrator.cxx b/Detectors/ITSMFT/ITS/calibration/src/NoiseSlotCalibrator.cxx index a687da5eda53e..c4faf788f6afe 100644 --- a/Detectors/ITSMFT/ITS/calibration/src/NoiseSlotCalibrator.cxx +++ b/Detectors/ITSMFT/ITS/calibration/src/NoiseSlotCalibrator.cxx @@ -29,7 +29,7 @@ bool NoiseSlotCalibrator::processTimeFrame(gsl::span const& rofs) { calibration::TFType nTF = rofs[0].getBCData().orbit / 256; - LOG(info) << "Processing TF# " << nTF; + LOG(detail) << "Processing TF# " << nTF; auto& slotTF = getSlotForTF(nTF); auto& noiseMap = *(slotTF.getContainer()); diff --git a/Detectors/ITSMFT/ITS/macros/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/CMakeLists.txt index 8c6767cfdad89..d651d94fbc07a 100644 --- a/Detectors/ITSMFT/ITS/macros/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(EVE) add_subdirectory(test) +add_subdirectory(DCS) diff --git a/Detectors/ITSMFT/ITS/macros/DCS/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/DCS/CMakeLists.txt new file mode 100644 index 0000000000000..5850bebbeae05 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/DCS/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_test_root_macro( + makeITSCCDBEntryForDCS.C + PUBLIC_LINK_LIBRARIES O2::DetectorsDCS O2::CCDB) + +install( + FILES makeITSCCDBEntryForDCS.C + DESTINATION share/macro/) diff --git a/Detectors/ITSMFT/ITS/macros/DCS/makeITSCCDBEntryForDCS.C b/Detectors/ITSMFT/ITS/macros/DCS/makeITSCCDBEntryForDCS.C new file mode 100644 index 0000000000000..482f48132f80b --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/DCS/makeITSCCDBEntryForDCS.C @@ -0,0 +1,58 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "CommonUtils/NameConf.h" +#endif +#include +#include +#include "TFile.h" +#include "DetectorsDCS/AliasExpander.h" +#include "DetectorsDCS/DeliveryType.h" +#include "DetectorsDCS/DataPointIdentifier.h" +#include +#include +#include "CCDB/CcdbApi.h" + +using DPID = o2::dcs::DataPointIdentifier; + +int makeITSCCDBEntryForDCS(std::string ccdb_path = o2::base::NameConf::getCCDBServer()) +{ + + std::unordered_map dpid2DataDesc; + std::vector aliases; + + // fill aliases + int nStaves[] = {12, 16, 20, 24, 30, 42, 48}; + for (int iL = 0; iL < 7; iL++) { + for (int iS = 0; iS < nStaves[iL]; iS++) { + std::string stv = iS > 9 ? std::to_string(iS) : std::string(1, '0').append(std::to_string(iS)); + aliases.push_back("ITS_L" + std::to_string(iL) + "_" + stv + "_STROBE"); + } + } + + std::vector expaliases = o2::dcs::expandAliases(aliases); + + DPID dpidtmp; + for (size_t i = 0; i < expaliases.size(); ++i) { + DPID::FILL(dpidtmp, expaliases[i], o2::dcs::DeliveryType::DPVAL_INT); + dpid2DataDesc[dpidtmp] = "ITSDATAPOINTS"; + } + + o2::ccdb::CcdbApi api; + api.init(ccdb_path); + std::map md; + md["comment"] = "uploaded with O2 makeITSCCDBEntryForDCS.C"; + long ts = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + api.storeAsTFileAny(&dpid2DataDesc, "ITS/Config/DCSDPconfig", md, ts, ts + 365L * 10 * 24 * 3600 * 1000); // validity is 10 years + + return 0; +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt index 550e0373e4f44..1adfe28a68e09 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt @@ -18,7 +18,7 @@ o2_add_test_root_macro(CheckClusters.C PUBLIC_LINK_LIBRARIES O2::ITSBase O2::DataFormatsITSMFT O2::ITSMFTSimulation O2::MathUtils O2::SimulationDataFormat - LABELS its) + LABELS its COMPILE_ONLY) o2_add_test_root_macro(CheckDigits.C PUBLIC_LINK_LIBRARIES O2::ITSBase @@ -27,7 +27,7 @@ o2_add_test_root_macro(CheckDigits.C O2::MathUtils O2::SimulationDataFormat O2::DetectorsBase - LABELS its) + LABELS its COMPILE_ONLY) #o2_add_test_root_macro(CheckLUtime.C # PUBLIC_LINK_LIBRARIES O2::ITSMFTReconstruction @@ -41,7 +41,7 @@ o2_add_test_root_macro(CreateDictionaries.C O2::ITSMFTSimulation O2::DataFormatsITSMFT O2::SimulationDataFormat - LABELS its) + LABELS its COMPILE_ONLY) #o2_add_test_root_macro(CheckCOG.C # PUBLIC_LINK_LIBRARIES O2::MathUtils @@ -64,7 +64,7 @@ o2_add_test_root_macro(CheckTracksCA.C O2::ITSBase O2::DataFormatsITS O2::DataFormatsITSMFT - LABELS its) + LABELS its COMPILE_ONLY) o2_add_test_root_macro(CheckSquasher.C PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat @@ -108,3 +108,40 @@ o2_add_test_root_macro(ITSMisaligner.C o2_add_test_root_macro(CompareArtefacts.C LABELS its) + +o2_add_test_root_macro(CheckDuplicates.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::DataFormatsITSMFT + LABELS its) + +o2_add_test_root_macro(CheckDROF.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::DataFormatsITSMFT + LABELS its) + +o2_add_test_root_macro(CheckStaggering.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::DataFormatsITSMFT + O2::ITSMFTReconstruction + O2::DCAFitter + O2::CCDB + O2::DetectorsVertexing + O2::ReconstructionDataFormats + LABELS its COMPILE_ONLY) + +o2_add_test_root_macro(CheckSeeding.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::SimulationDataFormat + O2::Steer + LABELS its COMPILE_ONLY) + +o2_add_test_root_macro(CheckITSTracksVsROF.C + PUBLIC_LINK_LIBRARIES O2::CCDB + O2::CommonConstants + O2::DataFormatsITS + O2::DataFormatsITSMFT + O2::DataFormatsFT0 + O2::DataFormatsFIT + O2::DataFormatsParameters + O2::Framework + LABELS its COMPILE_ONLY) diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C b/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C index 3f17e42b3a712..99c2b091002af 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C @@ -1,6 +1,29 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + /// \file CheckClusters.C /// \brief Simple macro to check ITSU clusters +#include +#include +#include +#include +#include +#include + +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITSBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "ITSMFTSimulation/Hit.h" #if !defined(__CLING__) || defined(__ROOTCLING__) #include #include @@ -191,7 +214,7 @@ void CheckClusters(std::string clusfile = "o2clus_its.root", std::string hitfile auto z0 = locHsta.Z(), dltz = locH.Z() - z0; auto r = (0.5 * (Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff) - y0) / dlty; locH.SetXYZ(x0 + r * dltx, y0 + r * dlty, z0 + r * dltz); - //locH.SetXYZ(0.5 * (locH.X() + locHsta.X()), 0.5 * (locH.Y() + locHsta.Y()), 0.5 * (locH.Z() + locHsta.Z())); + // locH.SetXYZ(0.5 * (locH.X() + locHsta.X()), 0.5 * (locH.Y() + locHsta.Y()), 0.5 * (locH.Z() + locHsta.Z())); std::array data = {(float)lab.getEventID(), (float)trID, locH.X(), locH.Z(), dltx / dlty, dltz / dlty, gloC.X(), gloC.Y(), gloC.Z(), @@ -209,6 +232,18 @@ void CheckClusters(std::string clusfile = "o2clus_its.root", std::string hitfile new TCanvas; nt.Draw("dz:tz", "abs(dz)<0.005 && abs(tz)<2"); + auto canvdXdZ = new TCanvas("canvdXdZ", "", 1600, 800); + canvdXdZ->Divide(2, 2); + canvdXdZ->cd(1)->SetLogz(); + nt.Draw("dx:dz>>h_dx_vs_dz_IB(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id < 3120", "colz"); + canvdXdZ->cd(2)->SetLogz(); + nt.Draw("dx:dz>>h_dx_vs_dz_OB(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id >= 3120", "colz"); + canvdXdZ->cd(3)->SetLogz(); + nt.Draw("dx:dz>>h_dx_vs_dz_IB_z(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id < 3120 && abs(cgz) < 2", "colz"); + canvdXdZ->cd(4)->SetLogz(); + nt.Draw("dx:dz>>h_dx_vs_dz_OB_z(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id >= 3120 && abs(cgz) < 2", "colz"); + canvdXdZ->SaveAs("itsclusters_dx_vs_dz.pdf"); + auto c1 = new TCanvas("p1", "pullX"); c1->cd(); c1->SetLogy(); diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C b/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C new file mode 100644 index 0000000000000..01acd2935981d --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C @@ -0,0 +1,1427 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include + +#include +#include +#include +#include "TH1F.h" +#include +#include "TH2D.h" +#include "TH3D.h" +#include +#include +#include +#include +#include +#include + +#include "ITSBase/GeometryTGeo.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "DetectorsBase/Propagator.h" +#include "SimulationDataFormat/TrackReference.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "SimulationDataFormat/DigitizationContext.h" + +#endif + +using namespace std; + +void plotHistos(TFile* fWO, TFile* f, const char* append = ""); + +struct ParticleInfo { // particle level information for tracks + int event; + int pdg; + float pt; + float eta; + float phi; + int mother; + int first; + float pvx{}; + float pvy{}; + float pvz{}; + float dcaxy; + float dcaz; + unsigned short clusters = 0u; + unsigned char isReco = 0u; + unsigned char isFake = 0u; + bool isPrimary = false; + int bcInROF{-1}; + int rofId{-1}; + unsigned char storedStatus = 2; /// not stored = 2, fake = 1, good = 0 + o2::its::TrackITS track; + + void print() const + { + LOGP(info, "event={} pdg={} pt={} eta={} phi={} mother={} clusters={:7b} isReco={} isFake={} isPrimary={} bcInROF={} rofId={} | {}", event, pdg, pt, eta, phi, mother, clusters, isReco, isFake, isPrimary, bcInROF, rofId, track.asString()); + } + + int getNClusters() const noexcept + { + int nCl{0}; + for (unsigned int bit{0}; bit < sizeof(ParticleInfo::clusters) * 8; ++bit) { + nCl += bool(clusters & (1 << bit)); + } + return nCl; + } + + bool isReconstructable() const noexcept + { + return isPrimary && (7 == getNClusters()) && bcInROF >= 0; + } +}; +#pragma link C++ class ParticleInfo + ; +#pragma link C++ class std::vector < ParticleInfo> + ; + +struct VertexInfo { // Vertex level info + float purity; // fraction of main cont. labels to all + o2::its::Vertex vertex; // reconstructed vertex + int bcInROF{-1}; + int rofId{-1}; + int event{-1}; // corresponding MC event + std::vector labels; // contributor labels + o2::MCCompLabel mainLabel; // main label + + void computeMain() + { + std::unordered_map freq; + size_t totalSet = 0; + + // Count frequencies of set labels + for (auto const& lab : labels) { + if (lab.isSet()) { + ++freq[lab]; + ++totalSet; + } + } + if (totalSet == 0) { + return; + } + // Find the label with maximum count + auto best = std::max_element(freq.begin(), freq.end(), [](auto const& a, auto const& b) { return a.second < b.second; }); + size_t maxCount = best->second; + + // If there's no majority (all counts == 1), fall back to first set label + o2::MCCompLabel mainLab; + if (maxCount == 1) { + for (auto const& lab : labels) { + if (lab.isSet()) { + mainLab = lab; + break; + } + } + } else { + mainLab = best->first; + } + purity = (float)maxCount / (float)labels.size(); + } +}; +#pragma link C++ class VertexInfo + ; + +using namespace o2::itsmft; +using namespace o2::its; + +void CheckDROF(bool plot = false, bool write = false, const std::string& tracfile = "o2trac_its.root", + const std::string& magfile = "o2sim_grp.root", + const std::string& clusfile = "o2clus_its.root", + const std::string& kinefile = "o2sim_Kine.root") +{ + constexpr int64_t roFrameLengthInBC = 198; // for pp=198 + constexpr int64_t roFrameBiasInBC = 64; // ITS delay accounted for in digitization + constexpr float roFbins{roFrameLengthInBC + 2.f}; + constexpr int bcValStart{60}, bcValEnd{140}; // adjustable region of validation train + + if (!plot) { + int trackID, evID, srcID; + bool fake; + + // Magnetic field and Propagator + o2::base::Propagator::initFieldFromGRP(magfile); + float bz = o2::base::Propagator::Instance()->getNominalBz(); + + // Geometry + o2::base::GeometryManager::loadGeometry(); + auto gman = o2::its::GeometryTGeo::Instance(); + + // MC tracks + TFile* file0 = TFile::Open(kinefile.data()); + TTree* mcTree = (TTree*)gFile->Get("o2sim"); + mcTree->SetBranchStatus("*", 0); // disable all branches + mcTree->SetBranchStatus("MCTrack*", 1); + mcTree->SetBranchStatus("MCEventHeader*", 1); + std::vector* mcArr = nullptr; + mcTree->SetBranchAddress("MCTrack", &mcArr); + o2::dataformats::MCEventHeader* mcEvent = nullptr; + mcTree->SetBranchAddress("MCEventHeader.", &mcEvent); + + auto* dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); + const auto& irs = dc->getEventRecords(); + dc->printCollisionSummary(false, 20); + + // Clusters + TFile::Open(clusfile.data()); + TTree* clusTree = (TTree*)gFile->Get("o2sim"); + std::vector* clusArr = nullptr; + clusTree->SetBranchAddress("ITSClusterComp", &clusArr); + + // Cluster MC labels + o2::dataformats::MCTruthContainer* clusLabArr = nullptr; + clusTree->SetBranchAddress("ITSClusterMCTruth", &clusLabArr); + + // Reconstructed tracks + TFile* file1 = TFile::Open(tracfile.data()); + TTree* recTree = (TTree*)gFile->Get("o2sim"); + std::vector* recArr = nullptr; + recTree->SetBranchAddress("ITSTrack", &recArr); + // Track MC labels + std::vector* trkLabArr = nullptr; + recTree->SetBranchAddress("ITSTrackMCTruth", &trkLabArr); + std::vector rofRecVec, *rofRecVecP = &rofRecVec; + recTree->SetBranchAddress("ITSTracksROF", &rofRecVecP); + // Vertices + std::vector* recVerArr = nullptr; + recTree->SetBranchAddress("Vertices", &recVerArr); + std::vector* recVerROFArr = nullptr; + recTree->SetBranchAddress("VerticesROF", &recVerROFArr); + std::vector* recVerLabelsArr = nullptr; + recTree->SetBranchAddress("ITSVertexMCTruth", &recVerLabelsArr); + std::vector* recVerPurityArr = nullptr; + recTree->SetBranchAddress("ITSVertexMCPurity", &recVerPurityArr); + + std::cout << "** Filling particle table ... " << std::flush; + int lastEventIDcl = -1, cf = 0; + const int nev = mcTree->GetEntriesFast(); + std::vector> info; + info.resize(nev); + TH1D* hZvertex = new TH1D("hZvertex", "Z vertex", 100, -20, 20); + for (int n = 0; n < nev; n++) { // loop over MC events + mcTree->GetEvent(n); + info[n].resize(mcArr->size()); + hZvertex->Fill(mcEvent->GetZ()); + const auto& ir = irs[mcEvent->GetEventID() - 1]; // event id start from 1 + for (unsigned int mcI{0}; mcI < mcArr->size(); ++mcI) { + auto part = mcArr->at(mcI); + info[n][mcI].event = n; + info[n][mcI].pdg = part.GetPdgCode(); + info[n][mcI].pvx = mcEvent->GetX(); + info[n][mcI].pvy = mcEvent->GetY(); + info[n][mcI].pvz = mcEvent->GetZ(); + info[n][mcI].pt = part.GetPt(); + info[n][mcI].phi = part.GetPhi(); + info[n][mcI].eta = part.GetEta(); + info[n][mcI].isPrimary = part.isPrimary(); + if (!ir.isDummy()) { + info[n][mcI].bcInROF = (ir.toLong() - roFrameBiasInBC) % roFrameLengthInBC; + info[n][mcI].rofId = (ir.toLong() - roFrameBiasInBC) / roFrameLengthInBC; + } + } + } + std::cout << "done." << std::endl; + + std::cout << "** Creating particle/clusters correspondance ... " << std::flush; + for (int frame = 0; frame < clusTree->GetEntriesFast(); frame++) { // Cluster frames + if (!clusTree->GetEvent(frame)) { + continue; + } + + for (unsigned int iClus{0}; iClus < clusArr->size(); ++iClus) { + auto lab = (clusLabArr->getLabels(iClus))[0]; + if (!lab.isValid() || lab.getSourceID() != 0 || !lab.isCorrect()) { + continue; + } + + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + if (evID < 0 || evID >= (int)info.size()) { + std::cout << "Cluster MC label eventID out of range" << std::endl; + continue; + } + if (trackID < 0 || trackID >= (int)info[evID].size()) { + std::cout << "Cluster MC label trackID out of range" << std::endl; + continue; + } + + const CompClusterExt& c = (*clusArr)[iClus]; + auto layer = gman->getLayer(c.getSensorID()); + info[evID][trackID].clusters |= 1 << layer; + } + } + std::cout << "done." << std::endl; + + std::cout << "** Analysing tracks... " << std::flush; + int unaccounted{0}, good{0}, fakes{0}, total{0}, length{0}; + for (int frame = 0; frame < recTree->GetEntriesFast(); frame++) { // Cluster frames + if (!recTree->GetEvent(frame)) { + continue; + } + total += trkLabArr->size(); + for (unsigned int iTrack{0}; iTrack < trkLabArr->size(); ++iTrack) { + auto lab = trkLabArr->at(iTrack); + if (!lab.isSet()) { + unaccounted++; + continue; + } + lab.get(trackID, evID, srcID, fake); + if (evID < 0 || evID >= (int)info.size()) { + unaccounted++; + continue; + } + if (trackID < 0 || trackID >= (int)info[evID].size()) { + unaccounted++; + continue; + } + info[evID][trackID].isReco += !fake; + info[evID][trackID].isFake += fake; + /// We keep the best track we would keep in the data + if (recArr->at(iTrack).isBetter(info[evID][trackID].track, 1.e9)) { + info[evID][trackID].track = recArr->at(iTrack); + info[evID][trackID].storedStatus = fake; + static float ip[2]{0., 0.}; + info[evID][trackID].track.getImpactParams(info[evID][trackID].pvx, info[evID][trackID].pvy, info[evID][trackID].pvz, bz, ip); + info[evID][trackID].dcaxy = ip[0]; + info[evID][trackID].dcaz = ip[1]; + } + + fakes += fake; + good += !fake; + if (!fake) { + for (unsigned int bit{0}; bit < 7; ++bit) { + length += bool(info[evID][trackID].clusters & (1 << bit)); + } + } + } + } + std::cout << "done." << std::endl; + std::cout << "** Some statistics:" << std::endl; + std::cout << "\t- Total number of tracks: " << total << std::endl; + std::cout << "\t- Total number of tracks not corresponding to particles: " << unaccounted << " (" << unaccounted * 100. / total << "%)" << std::endl; + std::cout << "\t- Total number of fakes: " << fakes << " (" << fakes * 100. / total << "%)" << std::endl; + std::cout << "\t- Total number of good: " << good << " (" << good * 100. / total << "%)" << std::endl; + std::cout << "\t- Average length of good tracks: " << (double)length / (double)good << std::endl; + + TFile* fOut{nullptr}; + if (write) { + fOut = TFile::Open("checkDROF.root", "RECREATE"); + } + + const int nb = 100; + double xbins[nb + 1], ptcutl = 0.01, ptcuth = 10.; + double a = std::log(ptcuth / ptcutl) / nb; + for (int i = 0; i <= nb; i++) { + xbins[i] = ptcutl * std::exp(i * a); + } + + ////////////////////// + // Eff Tracks + { + auto num = new TH2D("num", ";#it{p}_{T} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + num->Sumw2(); + auto fak = new TH2D("fak", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fak->Sumw2(); + auto multiFak = new TH2D("multiFak", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFak->Sumw2(); + auto clone = new TH2D("clone", ";#it{p}_{T} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + clone->Sumw2(); + auto den = new TH2D("den", ";#it{p}_{T} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + den->Sumw2(); + auto numMC = new TH2D("numMC", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + numMC->Sumw2(); + auto fakMC = new TH2D("fakMC", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fakMC->Sumw2(); + auto multiFakMC = new TH2D("multiFakMC", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFakMC->Sumw2(); + auto cloneMC = new TH2D("cloneMC", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + cloneMC->Sumw2(); + auto denMC = new TH2D("denMC", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + denMC->Sumw2(); + + auto numVal = new TH2D("numVal", ";#it{p}_{T} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + numVal->Sumw2(); + auto fakVal = new TH2D("fakVal", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fakVal->Sumw2(); + auto multiFakVal = new TH2D("multiFakVal", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFakVal->Sumw2(); + auto cloneVal = new TH2D("cloneVal", ";#it{p}_{T} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + cloneVal->Sumw2(); + auto denVal = new TH2D("denVal", ";#it{p}_{T} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + denVal->Sumw2(); + auto numMCVal = new TH2D("numMCVal", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + numMCVal->Sumw2(); + auto fakMCVal = new TH2D("fakMCVal", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fakMCVal->Sumw2(); + auto multiFakMCVal = new TH2D("multiFakMCVal", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFakMCVal->Sumw2(); + auto cloneMCVal = new TH2D("cloneMCVal", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + cloneMCVal->Sumw2(); + auto denMCVal = new TH2D("denMCVal", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + denMCVal->Sumw2(); + + auto numMig = new TH2D("numMig", ";#it{p}_{T} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + numMig->Sumw2(); + auto fakMig = new TH2D("fakMig", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fakMig->Sumw2(); + auto multiFakMig = new TH2D("multiFakMig", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFakMig->Sumw2(); + auto cloneMig = new TH2D("cloneMig", ";#it{p}_{T} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + cloneMig->Sumw2(); + auto denMig = new TH2D("denMig", ";#it{p}_{T} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + denMig->Sumw2(); + auto numMCMig = new TH2D("numMCMig", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + numMCMig->Sumw2(); + auto fakMCMig = new TH2D("fakMCMig", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fakMCMig->Sumw2(); + auto multiFakMCMig = new TH2D("multiFakMCMig", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFakMCMig->Sumw2(); + auto cloneMCMig = new TH2D("cloneMCMig", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + cloneMCMig->Sumw2(); + auto denMCMig = new TH2D("denMCMig", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + denMCMig->Sumw2(); + + TProfile* avgClsZ = new TProfile("avgClsZ", "good attachment;z_{MC};", 25, -20, 20); + avgClsZ->SetLineColor(kBlack); + TProfile* avgClsZGood = new TProfile("avgClsZGood", "good attachment;z_{MC};", 25, -20, 20); + avgClsZGood->SetLineColor(kBlue); + TProfile* avgClsZFake = new TProfile("avgClsZFake", "fake attachment;z_{MC};", 25, -20, 20); + avgClsZFake->SetLineColor(kRed); + + for (auto& evInfo : info) { + for (auto& part : evInfo) { + if (!part.isReconstructable()) { + continue; + } + den->Fill(part.track.getPt(), part.track.getNClusters()); + denMC->Fill(part.pt, part.track.getNClusters()); + if (part.isReco) { + num->Fill(part.track.getPt(), part.track.getNClusters()); + numMC->Fill(part.pt, part.track.getNClusters()); + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + clone->Fill(part.track.getPt(), part.track.getNClusters()); + cloneMC->Fill(part.pt, part.track.getNClusters()); + } + } + } + if (part.isFake) { + fak->Fill(part.track.getPt(), part.track.getNClusters()); + fakMC->Fill(part.pt, part.track.getNClusters()); + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + multiFak->Fill(part.track.getPt(), part.track.getNClusters()); + multiFakMC->Fill(part.pt, part.track.getNClusters()); + } + } + } + + // sep into validation and migration region + if (bcValStart < part.bcInROF && part.bcInROF < bcValEnd) { + denVal->Fill(part.track.getPt(), part.track.getNClusters()); + denMCVal->Fill(part.pt, part.track.getNClusters()); + if (part.isReco) { + numVal->Fill(part.track.getPt(), part.track.getNClusters()); + numMCVal->Fill(part.pt, part.track.getNClusters()); + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + cloneVal->Fill(part.track.getPt(), part.track.getNClusters()); + cloneMCVal->Fill(part.pt, part.track.getNClusters()); + } + } + } + if (part.isFake) { + fakVal->Fill(part.track.getPt(), part.track.getNClusters()); + fakMCVal->Fill(part.pt, part.track.getNClusters()); + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + multiFakVal->Fill(part.track.getPt(), part.track.getNClusters()); + multiFakMCVal->Fill(part.pt, part.track.getNClusters()); + } + } + } + } else { + denMig->Fill(part.track.getPt(), part.track.getNClusters()); + denMCMig->Fill(part.pt, part.track.getNClusters()); + if (part.isReco) { + numMig->Fill(part.track.getPt(), part.track.getNClusters()); + numMCMig->Fill(part.pt, part.track.getNClusters()); + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + cloneMig->Fill(part.track.getPt(), part.track.getNClusters()); + cloneMCMig->Fill(part.pt, part.track.getNClusters()); + } + } + } + if (part.isFake) { + fakMig->Fill(part.track.getPt(), part.track.getNClusters()); + fakMCMig->Fill(part.pt, part.track.getNClusters()); + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + multiFakMig->Fill(part.track.getPt(), part.track.getNClusters()); + multiFakMCMig->Fill(part.pt, part.track.getNClusters()); + } + } + } + } + + int nCl = part.getNClusters(); + avgClsZ->Fill(part.pvz, nCl); + if (part.isReco) { + avgClsZGood->Fill(part.pvz, nCl); + } + if (part.isFake) { + avgClsZFake->Fill(part.pvz, nCl); + } + } + } + + auto sum = (TH2D*)num->Clone("sum"); + auto sumMC = (TH2D*)numMC->Clone("sumMC"); + sum->Add(fak); + sumMC->Add(fakMC); + sum->SetLineColor(kBlack); + sumMC->SetLineColor(kBlack); + fak->SetLineColor(2); + fakMC->SetLineColor(2); + multiFak->SetLineColor(kRed + 1); + multiFakMC->SetLineColor(kRed + 1); + + auto sumVal = (TH2D*)numVal->Clone("sumVal"); + auto sumMCVal = (TH2D*)numMCVal->Clone("sumMCVal"); + sumVal->Add(fakVal); + sumMCVal->Add(fakMCVal); + sumVal->SetLineColor(kBlack); + sumMCVal->SetLineColor(kBlack); + fakVal->SetLineColor(2); + fakMCVal->SetLineColor(2); + multiFakVal->SetLineColor(kRed + 1); + multiFakMCVal->SetLineColor(kRed + 1); + + auto sumMig = (TH2D*)numMig->Clone("sumMig"); + auto sumMCMig = (TH2D*)numMCMig->Clone("sumMCMig"); + sumMig->Add(fakMig); + sumMCMig->Add(fakMCMig); + sumMig->SetLineColor(kBlack); + sumMCMig->SetLineColor(kBlack); + fakMig->SetLineColor(2); + fakMCMig->SetLineColor(2); + multiFakMig->SetLineColor(kRed + 1); + multiFakMCMig->SetLineColor(kRed + 1); + + if (write) { + num->Write(); + den->Write(); + sum->Write(); + fak->Write(); + multiFak->Write(); + numMC->Write(); + denMC->Write(); + sumMC->Write(); + fakMC->Write(); + multiFakMC->Write(); + + numVal->Write(); + denVal->Write(); + sumVal->Write(); + fakVal->Write(); + multiFakVal->Write(); + numMCVal->Write(); + denMCVal->Write(); + sumMCVal->Write(); + fakMCVal->Write(); + multiFakMCVal->Write(); + + numMig->Write(); + denMig->Write(); + sumMig->Write(); + fakMig->Write(); + multiFakMig->Write(); + numMCMig->Write(); + denMCMig->Write(); + sumMCMig->Write(); + fakMCMig->Write(); + multiFakMCMig->Write(); + } else { + TCanvas* c1 = new TCanvas; + c1->SetLogx(); + c1->SetGrid(); + gPad->DrawFrame(ptcutl, 0.05, ptcuth, 1.03, ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)"); + + auto denp = den->ProjectionX(); + auto nump = num->ProjectionX(); + auto fakp = fak->ProjectionX(); + auto multiFakp = multiFak->ProjectionX(); + auto sump = sum->ProjectionX(); + auto clonep = clone->ProjectionX(); + + sump->Divide(sump, denp, 1, 1, "B"); + sump->Draw("hist;same"); + nump->Divide(nump, denp, 1, 1, "B"); + nump->Draw("hist;same"); + fakp->Divide(fakp, denp, 1, 1, "B"); + fakp->Draw("hist;same"); + multiFakp->Divide(multiFakp, denp, 1, 1, "B"); + multiFakp->Draw("hist;same"); + clonep->Divide(clonep, denp, 1, 1, "B"); + clonep->SetLineColor(3); + clonep->Draw("hist;same"); + + TCanvas* c2 = new TCanvas; + c2->Divide(2, 1); + c2->cd(1); + hZvertex->Draw(); + c2->cd(2); + avgClsZ->Draw(); + avgClsZGood->Draw("same"); + avgClsZFake->Draw("same"); + } + } + + ////////////////////// + // DROF Tracks + { + auto hBC = new TH1F("hBC", "Distance in BC;bcInROF;counts.", roFbins, -0.5, roFbins - 0.5); + auto hBCTracksDen = new TH2F("hBCTracksDen", "BC Den Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksNum = new TH2F("hBCTracksNum", "BC Num Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksFake = new TH2F("hBCTracksFake", "BC Fake Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksSum = new TH2F("hBCTracksSum", "BC Sum Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + + // control region + auto hBCTracksDenVal = new TH2F("hBCTracksDenVal", "Val BC Den Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksNumVal = new TH2F("hBCTracksNumVal", "Val BC Num Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksFakeVal = new TH2F("hBCTracksFakeVal", "Val BC Fake Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksSumVal = new TH2F("hBCTracksSumVal", "Val BC Sum Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + + // migration region + auto hBCTracksDenMig = new TH2F("hBCTracksDenMig", "MigBC Den Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksNumMig = new TH2F("hBCTracksNumMig", "MigBC Num Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksFakeMig = new TH2F("hBCTracksFakeMig", "MigBC Fake Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksSumMig = new TH2F("hBCTracksSumMig", "MigBC Sum Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + + for (auto& evInfo : info) { + for (auto& part : evInfo) { + if (!part.isReconstructable()) { + continue; + } + hBC->Fill(part.bcInROF); + hBCTracksDen->Fill(part.bcInROF, part.track.getNClusters()); + if (part.isReco) { + hBCTracksNum->Fill(part.bcInROF, part.track.getNClusters()); + } + if (part.isFake) { + hBCTracksFake->Fill(part.bcInROF, part.track.getNClusters()); + } + + if (bcValStart < part.bcInROF && part.bcInROF < bcValEnd) { + hBCTracksDenVal->Fill(part.bcInROF, part.track.getNClusters()); + if (part.isReco) { + hBCTracksNumVal->Fill(part.bcInROF, part.track.getNClusters()); + } + if (part.isFake) { + hBCTracksFakeVal->Fill(part.bcInROF, part.track.getNClusters()); + } + } else { + hBCTracksDenMig->Fill(part.bcInROF, part.track.getNClusters()); + if (part.isReco) { + hBCTracksNumMig->Fill(part.bcInROF, part.track.getNClusters()); + } + if (part.isFake) { + hBCTracksFakeMig->Fill(part.bcInROF, part.track.getNClusters()); + } + } + } + } + + hBCTracksSum->Add(hBCTracksNum); + hBCTracksSum->Add(hBCTracksFake); + hBCTracksSum->SetLineColor(kBlack); + hBCTracksFake->SetLineColor(2); + + hBCTracksSumVal->Add(hBCTracksNum); + hBCTracksSumVal->Add(hBCTracksFake); + hBCTracksSumVal->SetLineColor(kBlack); + hBCTracksFakeVal->SetLineColor(2); + + hBCTracksSumMig->Add(hBCTracksNum); + hBCTracksSumMig->Add(hBCTracksFake); + hBCTracksSumMig->SetLineColor(kBlack); + hBCTracksFakeMig->SetLineColor(2); + + if (write) { + hBCTracksDen->Write(); + hBCTracksNum->Write(); + hBCTracksFake->Write(); + hBCTracksSum->Write(); + + hBCTracksDenVal->Write(); + hBCTracksNumVal->Write(); + hBCTracksFakeVal->Write(); + hBCTracksSumVal->Write(); + + hBCTracksDenMig->Write(); + hBCTracksNumMig->Write(); + hBCTracksFakeMig->Write(); + hBCTracksSumMig->Write(); + } else { + auto hBCTracksDenp = hBCTracksDen->ProjectionX(); + auto hBCTracksSump = hBCTracksSum->ProjectionX(); + auto hBCTracksNump = hBCTracksNum->ProjectionX(); + auto hBCTracksFakep = hBCTracksFake->ProjectionX(); + + hBCTracksSump->Divide(hBCTracksSump, hBCTracksDenp, 1., 1., "B"); + hBCTracksNump->Divide(hBCTracksNump, hBCTracksDenp, 1., 1., "B"); + hBCTracksFakep->Divide(hBCTracksFakep, hBCTracksDenp, 1., 1., "B"); + + auto c = new TCanvas; + c->Divide(2, 1); + c->cd(1); + hBC->Draw(); + c->cd(2); + gPad->DrawFrame(-0.5, 1e-3, roFbins - 0.5, 1.1, "Tracking >4 ITS cls;bcInROF;eff."); + gPad->SetGrid(); + hBCTracksSump->Draw("histe;same"); + hBCTracksNump->Draw("histe;same"); + hBCTracksFakep->Draw("histe;same"); + auto leg = new TLegend; + leg->AddEntry(hBCTracksSump, "Sum"); + leg->AddEntry(hBCTracksNump, "Good"); + leg->AddEntry(hBCTracksFakep, "Fake"); + leg->Draw(); + } + } + + ////////////////////// + // DROF Vertices + if constexpr (false) { + std::vector vertexInfo; + std::cout << "** Creating vertices/particles correspondance ... " << std::flush; + for (int frame = 0; frame < recTree->GetEntriesFast(); frame++) { // Vertices frames + if (!recTree->GetEvent(frame)) { + continue; + } + int contLabIdx{0}; // contributor labels are stored as flat vector + for (size_t iRecord{0}; iRecord < recVerROFArr->size(); ++iRecord) { + auto& rec = recVerROFArr->at(iRecord); + auto verStartIdx = rec.getFirstEntry(), verSize = rec.getNEntries(); + for (int iVertex{rec.getFirstEntry()}; iVertex < verStartIdx + verSize; ++iVertex) { + auto& info = vertexInfo.emplace_back(); + info.vertex = recVerArr->at(iVertex); + info.mainLabel = recVerLabelsArr->at(contLabIdx); + info.purity = recVerPurityArr->at(contLabIdx); + info.event = info.mainLabel.getEventID(); + ++contLabIdx; + if (info.mainLabel.isSet()) { + const auto& ir = irs[info.event]; + // LOGP(info, "iROF={} {} to {}", iRecord, info.mainLabel.asString(), ir.asString()); + if (!ir.isDummy()) { + info.bcInROF = (ir.toLong() - roFrameBiasInBC) % roFrameLengthInBC; + info.rofId = (ir.toLong() - roFrameBiasInBC) / roFrameLengthInBC; + } + } + } + } + } + std::cout << "done." << std::endl; + + auto hMCVtxZ = new TH1F("hMCVtxZ", "MC Vertex;Z", 50, -16, 16); + auto hReVtxZ = new TH1F("hRecoVtxZ", "Reco Vertex;Z", 50, -16, 16); + + auto hBCVtxDen = new TH1F("hBCVtxDen;bcInROF;eff.", "BC Den Vertices", roFbins, -0.5, roFbins - 0.5); + auto hBCVtxNum = new TH1F("hBCVtxNum;bcInROF;eff.", "BC Num Vertices", roFbins, -0.5, roFbins - 0.5); + + auto hBCVtxZDen = new TH2F("hBCVtxDen;bcInROF;z;eff.", "BC Den Vertices vs. z position", roFbins, -0.5, roFbins - 0.5, 40, -20, 20); + auto hBCVtxZNum = new TH2F("hBCVtxNum;bcInROF;z;eff.", "BC Num Vertices vs. z position", roFbins, -0.5, roFbins - 0.5, 40, -20, 20); + + auto pBCPurity = new TProfile("pBCProfile", ";bcInROF;", roFbins, -0.5, roFbins - 0.5); + auto pBCPurityDup = new TProfile("pBCProfileDup", ";bcInROF;", roFbins, -0.5, roFbins - 0.5); + pBCPurityDup->SetLineColor(kRed); + + auto hVtxMCx = new TH2F("hVtxMCx", ";MC_{x};Vtx_{x}", 100, -0.3, 0.3, 100, -0.3, 0.3); + auto hVtxMCy = new TH2F("hVtxMCy", ";MC_{y};Vtx_{y}", 100, -0.3, 0.3, 100, -0.3, 0.3); + auto hVtxMCz = new TH2F("hVtxMCz", ";MC_{z};Vtx_{z}", 100, -20, 20, 100, -20, 20); + + for (int n = 0; n < nev; n++) { // loop over MC events + mcTree->GetEvent(n); + hMCVtxZ->Fill(mcEvent->GetZ()); + const auto& ir = irs[mcEvent->GetEventID() - 1]; // event id start from 1 + if (!ir.isDummy()) { + int bcInROF = (ir.toLong() - roFrameBiasInBC) % roFrameLengthInBC; + hBCVtxDen->Fill(bcInROF); + hBCVtxZDen->Fill(bcInROF, mcEvent->GetZ()); + } + } + std::unordered_map seenMCEvent; + for (const auto& vtx : vertexInfo) { + ++seenMCEvent[vtx.mainLabel]; + } + // for (const auto& [k, f] : seenMCEvent) { + // LOGP(info, "{}:{} -> {} ({:.1f}%)", k.getSourceID(), k.getEventID(), f, 100.f * ((float)f / (float)vertexInfo.size())); + // } + LOGP(info, "received {} unique vertices", seenMCEvent.size()); + for (const auto& vtx : vertexInfo) { + if (!vtx.mainLabel.isValid() || vtx.bcInROF < 0 || vtx.event < 0) { + continue; + } + mcTree->GetEvent(vtx.event); + hVtxMCx->Fill(mcEvent->GetX(), vtx.vertex.getX()); + hVtxMCy->Fill(mcEvent->GetY(), vtx.vertex.getY()); + hVtxMCz->Fill(mcEvent->GetZ(), vtx.vertex.getZ()); + if (seenMCEvent[vtx.mainLabel] > 1) { + pBCPurityDup->Fill(vtx.bcInROF, vtx.purity); + } else { + hReVtxZ->Fill(vtx.vertex.getZ()); + hBCVtxNum->Fill(vtx.bcInROF); + hBCVtxZNum->Fill(vtx.bcInROF, vtx.vertex.getZ()); + pBCPurity->Fill(vtx.bcInROF, vtx.purity); + } + } + + auto hBCVtxNumClone = (TH1F*)hBCVtxNum->Clone(); + hBCVtxNumClone->SetTitle("unique Vertex;bcInROF;efficiency"); + hBCVtxNum->Divide(hBCVtxNum, hBCVtxDen, 1., 1., "b"); + + auto hBCVtxZNumClone = (TH2F*)hBCVtxZNum->Clone(); + hBCVtxZNumClone->SetTitle("unique Vertex;bcInROF;vtx.z;efficiency"); + hBCVtxZNumClone->Divide(hBCVtxZNum, hBCVtxZDen, 1., 1., "b"); + + hReVtxZ->Sumw2(); + hReVtxZ->SetLineColor(kRed); + + auto c = new TCanvas; + c->Divide(3, 2); + + c->cd(1); + auto hRatioVtxZ = new TRatioPlot(hReVtxZ, hMCVtxZ); + hRatioVtxZ->Draw(); + hRatioVtxZ->GetUpperPad()->cd(); + TLegend* legend = new TLegend(0.3, 0.7, 0.7, 0.85); + legend->SetHeader(Form("MC=%.0f Reco=%.0f", hMCVtxZ->GetEntries(), hReVtxZ->GetEntries())); + legend->AddEntry(hReVtxZ, "Reco", "l"); + legend->AddEntry(hMCVtxZ, "MC", "le"); + legend->Draw(); + gPad->Update(); + double max1 = hReVtxZ->GetMaximum(); + double max2 = hMCVtxZ->GetMaximum(); + double maxY = std::max(max1, max2); + hReVtxZ->GetYaxis()->SetRangeUser(0, maxY * 1.1); + + c->cd(2); + gPad->DrawFrame(-0.5, 1e-3, roFbins - 0.5, 1.1, "Vertex ;bcInROF;eff."); + hBCVtxNum->Draw("histe;same"); + + c->cd(3); + gPad->DrawFrame(-0.5, 1e-3, roFbins - 0.5, 1.1, "Purity;bcInROF;"); + pBCPurity->Draw(); + pBCPurityDup->Draw("same"); + c->Draw(); + + c->cd(4); + hBCVtxDen->Draw(); + c->cd(5); + hBCVtxNumClone->Draw(); + c->cd(6); + hBCVtxZNumClone->Draw(); + c->Draw(); + + c = new TCanvas; + c->Divide(3, 1); + c->cd(1); + hVtxMCx->Draw("colz"); + c->cd(2); + hVtxMCy->Draw("colz"); + c->cd(3); + hVtxMCz->Draw("colz"); + c->Draw(); + } + ////////////////////// + // Fake clusters + if (write) { + const int nby{4}, nbz{7}; + double ybins[nby + 1], zbins[nbz + 1]; + for (int i{0}; i < nby + 1; ++i) { + ybins[i] = (4 + i) - 0.5; + } + for (int i{0}; i < nbz + 1; ++i) { + zbins[i] = (0 + i) - 0.5; + } + auto hFakVal = new TH3D("fakClsVal", "Fake cluster attachment;#it{p}_{T} (GeV/#it{c});NCls;Fake;(fake-cluster rate)", nb, xbins, nby, ybins, nbz, zbins); + auto hFakMig = new TH3D("fakClsMig", "Fake cluster attachment;#it{p}_{T} (GeV/#it{c});NCls;Fake;(fake-cluster rate)", nb, xbins, nby, ybins, nbz, zbins); + + for (auto& event : info) { + for (auto& part : event) { + if (!part.isReconstructable()) { + continue; + } + + const auto& trk = part.track; + for (int iL{0}; iL < 7; ++iL) { + if (!trk.hasHitOnLayer(iL) || !trk.isFakeOnLayer(iL) || (part.clusters & (0x1 << iL)) == 0) { + continue; + } + // TODO: figure out how to find hit migration + // if (trk.hasHitInNextROF()) { + // hFakMig->Fill(trk.getPt(), trk.getNClusters(), iL); + // } else { + // hFakVal->Fill(trk.getPt(), trk.getNClusters(), iL); + // } + } + } + } + + hFakMig->Write(); + hFakVal->Write(); + } + if (fOut) { + fOut->Close(); + } + } else { + auto fWO = TFile::Open("checkDROF_wo.root"); + auto f = TFile::Open("checkDROF_w.root"); + plotHistos(fWO, f, ""); + plotHistos(fWO, f, "Val"); + plotHistos(fWO, f, "Mig"); + } +} + +void plotHistos(TFile* fWO, TFile* f, const char* append) +{ + TLegend* leg; + TH1* h; + const int woStyle = 1001; + const int wStyle = 3003; + const int ww{3840}, hh{2160}; + + const char* titlename = ""; + if (strcmp(append, "Val") == 0) { + titlename = ", Validation region"; + } else if (strcmp(append, "Mig") == 0) { + titlename = ", Migration region"; + } + + auto hWODen2 = fWO->Get(Form("den%s", append)); + hWODen2->SetName(Form("%s_wo", hWODen2->GetName())); + auto hWONum2 = fWO->Get(Form("num%s", append)); + hWONum2->SetName(Form("%s_wo", hWONum2->GetName())); + auto hWOFak2 = fWO->Get(Form("fak%s", append)); + hWOFak2->SetName(Form("%s_wo", hWOFak2->GetName())); + auto hWOSum2 = fWO->Get(Form("sum%s", append)); + hWOSum2->SetName(Form("%s_wo", hWOSum2->GetName())); + auto hWOMultiFak2 = fWO->Get(Form("multiFak%s", append)); + hWOMultiFak2->SetName(Form("%s_wo", hWOMultiFak2->GetName())); + + auto hWODenMC2 = fWO->Get(Form("denMC%s", append)); + hWODenMC2->SetName(Form("%s_wo", hWODenMC2->GetName())); + auto hWONumMC2 = fWO->Get(Form("numMC%s", append)); + hWONumMC2->SetName(Form("%s_wo", hWONumMC2->GetName())); + auto hWOFakMC2 = fWO->Get(Form("fakMC%s", append)); + hWOFakMC2->SetName(Form("%s_wo", hWOFakMC2->GetName())); + auto hWOSumMC2 = fWO->Get(Form("sumMC%s", append)); + hWOSumMC2->SetName(Form("%s_wo", hWOSumMC2->GetName())); + auto hWOMultiFakMC2 = fWO->Get(Form("multiFakMC%s", append)); + hWOMultiFakMC2->SetName(Form("%s_wo", hWOMultiFakMC2->GetName())); + + auto hWOBCTracksDen2 = fWO->Get(Form("hBCTracksDen%s", append)); + hWOBCTracksDen2->SetName(Form("%s_wo", hWOBCTracksDen2->GetName())); + auto hWOBCTracksNum2 = fWO->Get(Form("hBCTracksNum%s", append)); + hWOBCTracksNum2->SetName(Form("%s_wo", hWOBCTracksNum2->GetName())); + auto hWOBCTracksFake2 = fWO->Get(Form("hBCTracksFake%s", append)); + hWOBCTracksFake2->SetName(Form("%s_wo", hWOBCTracksFake2->GetName())); + auto hWOBCTracksSum2 = fWO->Get(Form("hBCTracksSum%s", append)); + hWOBCTracksSum2->SetName(Form("%s_wo", hWOBCTracksSum2->GetName())); + + auto setColor = [](TH1* h, EColor c) { + h->SetLineColor(c); + h->SetMarkerColor(c); + }; + auto hDen2 = f->Get(Form("den%s", append)); + setColor(hDen2, kBlack); + auto hNum2 = f->Get(Form("num%s", append)); + setColor(hNum2, kCyan); + auto hFak2 = f->Get(Form("fak%s", append)); + setColor(hFak2, kOrange); + auto hSum2 = f->Get(Form("sum%s", append)); + setColor(hSum2, kGray); + auto hMultiFak2 = f->Get(Form("multiFak%s", append)); + setColor(hMultiFak2, kMagenta); + + auto hDenMC2 = f->Get(Form("denMC%s", append)); + setColor(hDenMC2, kBlack); + auto hNumMC2 = f->Get(Form("numMC%s", append)); + setColor(hNumMC2, kCyan); + auto hFakMC2 = f->Get(Form("fakMC%s", append)); + setColor(hFakMC2, kOrange); + auto hSumMC2 = f->Get(Form("sumMC%s", append)); + setColor(hSumMC2, kGray); + auto hMultiFakMC2 = f->Get(Form("multiFakMC%s", append)); + setColor(hMultiFakMC2, kMagenta); + + auto hBCTracksDen2 = f->Get(Form("hBCTracksDen%s", append)); + setColor(hBCTracksDen2, kBlack); + auto hBCTracksNum2 = f->Get(Form("hBCTracksNum%s", append)); + setColor(hBCTracksNum2, kCyan); + auto hBCTracksFake2 = f->Get(Form("hBCTracksFake%s", append)); + setColor(hBCTracksFake2, kOrange); + auto hBCTracksSum2 = f->Get(Form("hBCTracksSum%s", append)); + setColor(hBCTracksSum2, kGray); + + int k = 0; + TCanvas *cEff = nullptr, *cBC = nullptr, *cCont = nullptr, *cRatio = nullptr; + { + auto plotTrkEff = [&](int i, int j) { + auto hWONum = hWONumMC2->ProjectionX(Form("%s_%d_%d_eff_px", hWONumMC2->GetName(), i, j), i, j); + auto hWODen = hWODenMC2->ProjectionX(Form("%s_%d_%d_eff_px", hWODenMC2->GetName(), i, j), 0, 5); + auto hWOFak = hWOFakMC2->ProjectionX(Form("%s_%d_%d_eff_px", hWOFakMC2->GetName(), i, j), i, j); + auto hWOMultiFak = hWOMultiFakMC2->ProjectionX(Form("%s_%d_%d_eff_px", hWOMultiFakMC2->GetName(), i, j), i, j); + auto hWOSum = (TH1D*)hWONum->Clone(Form("%s_sum_eff__%d", hWONum->GetName(), j)); + hWOSum->Add(hWOFak); + + hWOSum->Divide(hWOSum, hWODen, 1., 1., "B"); + hWOSum->SetFillColorAlpha(hWOSum2->GetLineColor(), 0.5); + hWOSum->SetFillStyle(woStyle); + hWOSum->Draw("histe;same"); + + hWONum->Divide(hWONum, hWODen, 1., 1., "B"); + hWONum->SetFillColorAlpha(hWONum2->GetLineColor(), 0.5); + hWONum->SetFillStyle(woStyle); + hWONum->Draw("histe;same"); + + hWOFak->Divide(hWOFak, hWODen, 1., 1., "B"); + hWOFak->SetFillColorAlpha(hWOFak2->GetLineColor(), 0.5); + hWOFak->SetFillStyle(woStyle); + hWOFak->Draw("histe;same"); + + hWOMultiFak->Divide(hWOMultiFak, hWODen, 1., 1., "B"); + hWOMultiFak->SetLineColor(hWOMultiFak2->GetLineColor()); + hWOMultiFak->SetFillColorAlpha(hWOMultiFak2->GetLineColor(), 0.5); + hWOMultiFak->SetFillStyle(woStyle); + // hWOMultiFak->Draw("histe;same"); + + auto hNum = hNum2->ProjectionX(Form("%s_%d_%d_eff_px", hNumMC2->GetName(), i, j), i, j); + auto hDen = hDen2->ProjectionX(Form("%s_%d_%d_eff_px", hDenMC2->GetName(), i, j), 0, 5); + auto hFak = hFak2->ProjectionX(Form("%s_%d_%d_eff_px", hFakMC2->GetName(), i, j), i, j); + auto hMultiFak = hMultiFak2->ProjectionX(Form("%s_%d_%d_px", hMultiFakMC2->GetName(), i, j), i, j); + auto hSum = (TH1D*)hNum->Clone(Form("%s_sum_eff_%d", hNum->GetName(), j)); + hSum->Add(hFak); + + hSum->Divide(hSum, hDen, 1., 1., "B"); + hSum->SetFillColor(hSum2->GetLineColor()); + hSum->SetLineColor(hSum2->GetLineColor()); + hSum->SetFillStyle(wStyle); + hSum->Draw("histe;same"); + + hNum->Divide(hNum, hDen, 1., 1., "B"); + hNum->SetFillColor(hNum2->GetLineColor()); + hNum->SetLineColor(hNum2->GetLineColor()); + hNum->SetFillStyle(wStyle); + hNum->Draw("histe;same"); + + hFak->Divide(hFak, hDen, 1., 1., "B"); + hFak->SetFillColor(hFak2->GetLineColor()); + hFak->SetLineColor(hFak2->GetLineColor()); + hFak->SetFillStyle(wStyle); + hFak->Draw("histe;same"); + + hMultiFak->Divide(hMultiFak, hDen, 1., 1., "B"); + hMultiFak->SetLineColor(hMultiFak2->GetLineColor()); + hMultiFak->SetFillColor(hMultiFak2->GetLineColor()); + hMultiFak->SetFillStyle(wStyle); + // hMultiFak->Draw("histe;same"); + + if (i == 1 && i == j) { + leg = new TLegend(0.1, 0.1, 0.9, 0.9); + leg->AddEntry((TObject*)0, "deltaRof=0", ""); + leg->AddEntry(hWOSum, "sum"); + leg->AddEntry(hWONum, "good"); + leg->AddEntry(hWOFak, "fake"); + // leg->AddEntry(hWOMultiFak, "multifake"); + leg->AddEntry((TObject*)0, "deltaRof=1", ""); + leg->AddEntry(hSum, "sum"); + leg->AddEntry(hNum, "good"); + leg->AddEntry(hFak, "fake"); + // leg->AddEntry(hMultiFak, "multifake"); + } + }; + + cEff = new TCanvas(Form("pteff%s", append), "", ww, hh); + cEff->Divide(3, 2); + k = 0; + for (int i{1}; i <= 4; ++i) { + if (i == 3) { + ++k; + } + cEff->cd(i + k); + h = gPad->DrawFrame( + 0.02, 0, 10, 1.02, + Form("Tracking Efficiency #times Fraction (7 MC hits, %d-point " + "tracks%s);#it{p}_{T,MC} GeV/#it{c};eff. (fake-rate)", + 3 + i, titlename)); + h->GetXaxis()->SetTitleOffset(1.4); + + plotTrkEff(i, i); + + gPad->SetLogx(); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + } + cEff->cd(3); + h = gPad->DrawFrame( + 0.02, 0, 10, 1.02, + Form("Tracking Efficiency (7 MC hits, all point " + "tracks%s);#it{p}_{T,MC} GeV/#it{c};eff. (fake-rate)", + titlename)); + h->GetXaxis()->SetTitleOffset(1.4); + + plotTrkEff(1, 4); + + gPad->SetLogx(); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + + cEff->cd(6); + leg->Draw(); + } + + { + auto plotRatios = [&](int i, int j, TPad* upper, TPad* lower) { + auto hWONum = hWONumMC2->ProjectionX(Form("%s_%d_%d_ratio_px", hWONumMC2->GetName(), i, j), i, j); + auto hWOFak = hWOFakMC2->ProjectionX(Form("%s_%d_%d_ratio_px", hWOFakMC2->GetName(), i, j), i, j); + + hWONum->SetFillColorAlpha(hWONum2->GetLineColor(), 0.5); + hWONum->SetLineColor(hWONum2->GetLineColor()); + // hWONum->SetFillStyle(woStyle); + + hWOFak->SetFillColorAlpha(hWOFak2->GetLineColor(), 0.5); + hWOFak->SetLineColor(hWOFak2->GetLineColor()); + // hWOFak->SetFillStyle(woStyle); + + auto hNum = hNum2->ProjectionX(Form("%s_%d_%d_ratio_px", hNumMC2->GetName(), i, j), i, j); + auto hFak = hFak2->ProjectionX(Form("%s_%d_%d_ratio_px", hFakMC2->GetName(), i, j), i, j); + + hNum->SetFillColor(hNum2->GetLineColor()); + hNum->SetLineColor(hNum2->GetLineColor()); + // hNum->SetFillStyle(wStyle); + + hFak->SetFillColor(hFak2->GetLineColor()); + hFak->SetLineColor(hFak2->GetLineColor()); + // hFak->SetFillStyle(wStyle); + // + upper->cd(); + upper->SetLogx(); + upper->SetGrid(); + hWONum->Draw("hist"); + hWOFak->Draw("hist same"); + hNum->Draw("hist same"); + hFak->Draw("hist same"); + double ymax = 1.1 * std::max({hNum->GetMaximum(), hFak->GetMaximum(), hWONum->GetMaximum(), hWOFak->GetMaximum()}); + hWONum->GetYaxis()->SetRangeUser(0, ymax); + gPad->RedrawAxis("g"); + + auto rNum = (TH1*)hNum->Clone(Form("rNum_%s_%d_%d", hNum->GetName(), i, j)); + auto rFak = (TH1*)hFak->Clone(Form("rFak_%s_%d_%d", hFak->GetName(), i, j)); + rNum->GetYaxis()->SetTitle("(deltaRof=1) / (deltaRof=0)"); + rNum->Divide(hWONum); + rFak->Divide(hWOFak); + + // rNum->SetMarkerStyle(20); + // rFak->SetMarkerStyle(21); + rNum->SetLineWidth(2); + rFak->SetLineWidth(2); + rNum->SetFillStyle(0); + rFak->SetFillStyle(0); + setColor(rNum, kBlue); + setColor(rFak, kRed); + double ymin = std::min(rNum->GetMinimum(0.0), rFak->GetMinimum(0.0)); + ymax = std::max(rNum->GetMaximum(), rFak->GetMaximum()); + double ypad = 0.1 * (ymax - ymin); + ymin -= ypad; + ymax += ypad; + + lower->cd(); + lower->SetLogx(); + lower->SetGrid(); + rNum->GetYaxis()->SetRangeUser(ymin, ymax); + rNum->Draw("hist"); + rFak->Draw("hist;same"); + gPad->RedrawAxis("g"); + + if (i == 1 && i == j) { + leg = new TLegend(0.1, 0.1, 0.9, 0.9); + leg->AddEntry((TObject*)0, "deltaRof=0", ""); + leg->AddEntry(hWONum, "good"); + leg->AddEntry(hWOFak, "fake"); + leg->AddEntry((TObject*)0, "deltaRof=1", ""); + leg->AddEntry(hNum, "good"); + leg->AddEntry(hFak, "fake"); + leg->AddEntry((TObject*)0, "Ratios", ""); + leg->AddEntry(rNum, "good", "l"); + leg->AddEntry(rFak, "fake", "l"); + } + }; + + cRatio = new TCanvas(Form("ptratio%s", append), "", ww, hh); + cRatio->Divide(3, 2); + k = 0; + for (int i{1}; i <= 4; ++i) { + if (i == 3) { + ++k; + } + cRatio->cd(i + k); + TPad* up = new TPad(Form("up%d", k), "", 0, 0.5, 1, 1); + TPad* dn = new TPad(Form("dn%d", k), "", 0, 0, 1, 0.5); + up->SetBottomMargin(0); + dn->SetTopMargin(0); + up->Draw(); + dn->Draw(); + + plotRatios(i, i, up, dn); + } + cRatio->cd(3); + TPad* up = new TPad(Form("up_e_%d", k), "", 0, 0.5, 1, 1); + TPad* dn = new TPad(Form("dn_e_%d", k), "", 0, 0, 1, 0.5); + up->SetBottomMargin(0); + dn->SetTopMargin(0); + up->Draw(); + dn->Draw(); + plotRatios(1, 4, up, dn); + + cRatio->cd(6); + leg->Draw(); + } + + { + auto plotTrkCont = [&](int i, int j) { + auto hWONum = hWONum2->ProjectionX(Form("%s_%d_%d_cont_px", hWONum2->GetName(), i, j), i, j); + auto hWODen = hWODen2->ProjectionX(Form("%s_%d_%d_cont_px", hWODen2->GetName(), i, j), 0, 5); + auto hWOFak = hWOFak2->ProjectionX(Form("%s_%d_%d_cont_px", hWOFak2->GetName(), i, j), i, j); + auto hWOMultiFak = hWOMultiFak2->ProjectionX(Form("%s_%d_%d_cont_px", hWOMultiFak2->GetName(), i, j), i, j); + auto hWOSum = (TH1D*)hWONum->Clone(Form("%s_sum_cont_%d", hWONum->GetName(), j)); + hWOSum->Add(hWOFak); + + hWOSum->Divide(hWOSum, hWODen, 1., 1., "B"); + hWOSum->SetFillColorAlpha(hWOSum2->GetLineColor(), 0.5); + hWOSum->SetFillStyle(woStyle); + // hWOSum->Draw("histe;same"); + + hWONum->Divide(hWONum, hWODen, 1., 1., "B"); + hWONum->SetFillColorAlpha(hWONum2->GetLineColor(), 0.5); + hWONum->SetFillStyle(woStyle); + hWONum->Draw("histe;same"); + + hWOFak->Divide(hWOFak, hWODen, 1., 1., "B"); + hWOFak->SetFillColorAlpha(hWOFak2->GetLineColor(), 0.5); + hWOFak->SetFillStyle(woStyle); + hWOFak->Draw("histe;same"); + + hWOMultiFak->Divide(hWOMultiFak, hWODen, 1., 1., "B"); + hWOMultiFak->SetLineColor(hWOMultiFak2->GetLineColor()); + hWOMultiFak->SetFillColorAlpha(hWOMultiFak2->GetLineColor(), 0.5); + hWOMultiFak->SetFillStyle(woStyle); + // hWOMultiFak->Draw("histe;same"); + + auto hNum = hNum2->ProjectionX(Form("%s_%d_%d_cont_px", hNum2->GetName(), i, j), i, j); + auto hDen = hDen2->ProjectionX(Form("%s_%d_%d_cont_px", hDen2->GetName(), i, j), 0, 5); + auto hFak = hFak2->ProjectionX(Form("%s_%d_%d_cont_px", hFak2->GetName(), i, j), i, j); + auto hMultiFak = hMultiFak2->ProjectionX(Form("%s_%d_%d_px", hMultiFak2->GetName(), i, j), i, j); + auto hSum = (TH1D*)hNum->Clone(Form("%s_sum_cont_%d", hNum->GetName(), j)); + hSum->Add(hFak); + + hSum->Divide(hSum, hDen, 1., 1., "B"); + hSum->SetFillColor(hSum2->GetLineColor()); + hSum->SetFillStyle(wStyle); + // hSum->Draw("histe;same"); + + hNum->Divide(hNum, hDen, 1., 1., "B"); + hNum->SetFillColor(hNum2->GetLineColor()); + hNum->SetFillStyle(wStyle); + hNum->Draw("histe;same"); + + hFak->Divide(hFak, hDen, 1., 1., "B"); + hFak->SetFillColor(hFak2->GetLineColor()); + hFak->SetFillStyle(wStyle); + hFak->Draw("histe;same"); + + hMultiFak->Divide(hMultiFak, hDen, 1., 1., "B"); + hMultiFak->SetLineColor(hMultiFak2->GetLineColor()); + hMultiFak->SetFillColor(hMultiFak2->GetLineColor()); + hMultiFak->SetFillStyle(wStyle); + // hMultiFak->Draw("histe;same"); + + if (i == 1 && i == j) { + leg = new TLegend(0.1, 0.1, 0.9, 0.9); + leg->AddEntry((TObject*)0, "deltaRof=0", ""); + leg->AddEntry(hWONum, "good"); + leg->AddEntry(hWOFak, "fake"); + // leg->AddEntry(hWOMultiFak, "multifake"); + leg->AddEntry((TObject*)0, "deltaRof=1", ""); + leg->AddEntry(hNum, "DROF:good"); + leg->AddEntry(hFak, "DROF:fake"); + // leg->AddEntry(hMultiFak, "DROF:multifake"); + } + }; + + cCont = new TCanvas(Form("ptcont%s", append), "", ww, hh); + cCont->Divide(3, 2); + k = 0; + for (int i{1}; i <= 4; ++i) { + if (i == 3) { + ++k; + } + cCont->cd(i + k); + h = gPad->DrawFrame( + 0.02, 0, 10, 1.02, + Form("Tracking Contribution #times Fraction (7 MC hits, %d-point " + "tracks%s);#it{p}_{T,Reco} GeV/#it{c};contribtution", + 3 + i, titlename)); + h->GetXaxis()->SetTitleOffset(1.4); + + plotTrkCont(i, i); + + gPad->SetLogx(); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + } + cCont->cd(3); + h = gPad->DrawFrame(0.02, 0, 10, 1.02, + Form("Tracking Contribution (7 MC hits, all point " + "tracks%s);#it{p}_{T,Reco} GeV/#it{c};contribution", + titlename)); + h->GetXaxis()->SetTitleOffset(1.4); + + plotTrkCont(1, 4); + + gPad->SetLogx(); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + + cCont->cd(6); + leg->Draw(); + } + + { + auto plotBCEff = [&](int i, int j) { + auto hWOBCTracksNum = hWOBCTracksNum2->ProjectionX(Form("%s_%d_%d_bc_px", hWOBCTracksNum2->GetName(), i, j), i, j); + auto hWOBCTracksDen = hWOBCTracksDen2->ProjectionX(Form("%s_%d_%d_bc_px", hWOBCTracksDen2->GetName(), i, j), 0, 5); + auto hWOBCTracksFake = hWOBCTracksFake2->ProjectionX(Form("%s_%d_%d_bc_px", hWOBCTracksFake2->GetName(), i, j), i, j); + auto hWOBCTracksSum = (TH1F*)hWOBCTracksNum->Clone(Form("%s_%d_sum", hWOBCTracksNum->GetName(), j)); + hWOBCTracksSum->Add(hWOBCTracksFake); + + hWOBCTracksSum->Divide(hWOBCTracksSum, hWOBCTracksDen, 1., 1., "B"); + hWOBCTracksSum->SetLineColor(hWOSum2->GetLineColor()); + hWOBCTracksSum->SetFillColorAlpha(hWOSum2->GetLineColor(), 0.5); + hWOBCTracksSum->SetFillStyle(woStyle); + hWOBCTracksSum->Draw("histe;same"); + + hWOBCTracksNum->Divide(hWOBCTracksNum, hWOBCTracksDen, 1., 1., "B"); + hWOBCTracksNum->SetLineColor(hWONum2->GetLineColor()); + hWOBCTracksNum->SetFillColorAlpha(hWONum2->GetLineColor(), 0.5); + hWOBCTracksNum->SetFillStyle(woStyle); + hWOBCTracksNum->Draw("histe;same"); + + hWOBCTracksFake->Divide(hWOBCTracksFake, hWOBCTracksDen, 1., 1., "B"); + hWOBCTracksFake->SetLineColor(hWOFak2->GetLineColor()); + hWOBCTracksFake->SetFillColorAlpha(hWOFak2->GetLineColor(), 0.5); + hWOBCTracksFake->SetFillStyle(woStyle); + hWOBCTracksFake->Draw("histe;same"); + + auto hBCTracksNum = hBCTracksNum2->ProjectionX(Form("%s_%d_%d_bc_px", hBCTracksNum2->GetName(), i, j), i, j); + auto hBCTracksDen = hBCTracksDen2->ProjectionX(Form("%s_%d_%d_bc_px", hBCTracksDen2->GetName(), i, j), 0, 5); + auto hBCTracksFake = hBCTracksFake2->ProjectionX(Form("%s_%d_%d_bc_px", hBCTracksFake2->GetName(), i, j), i, j); + auto hBCTracksSum = (TH1F*)hBCTracksNum->Clone(Form("%s_%d_sum", hBCTracksNum->GetName(), j)); + hBCTracksSum->Add(hBCTracksFake); + + hBCTracksSum->Divide(hBCTracksSum, hBCTracksDen, 1., 1., "B"); + hBCTracksSum->SetLineColor(hSum2->GetLineColor()); + hBCTracksSum->SetFillColor(hSum2->GetLineColor()); + hBCTracksSum->SetFillStyle(wStyle); + hBCTracksSum->Draw("histe;same"); + + hBCTracksNum->Divide(hBCTracksNum, hBCTracksDen, 1., 1., "B"); + hBCTracksNum->SetLineColor(hNum2->GetLineColor()); + hBCTracksNum->SetFillColor(hNum2->GetLineColor()); + hBCTracksNum->SetFillStyle(wStyle); + hBCTracksNum->Draw("histe;same"); + + hBCTracksFake->Divide(hBCTracksFake, hBCTracksDen, 1., 1., "B"); + hBCTracksFake->SetLineColor(hFak2->GetLineColor()); + hBCTracksFake->SetFillColor(hFak2->GetLineColor()); + hBCTracksFake->SetFillStyle(wStyle); + hBCTracksFake->Draw("histe;same"); + + if (i == 1 && i == j) { + leg = new TLegend(0.1, 0.1, 0.9, 0.9); + leg->AddEntry((TObject*)0, "deltaRof=0", ""); + leg->AddEntry(hWOBCTracksNum, "good"); + leg->AddEntry(hWOBCTracksFake, "fake"); + leg->AddEntry(hWOBCTracksSum, "sum"); + leg->AddEntry((TObject*)0, "deltaRof=1", ""); + leg->AddEntry(hBCTracksNum, "good"); + leg->AddEntry(hBCTracksFake, "fake"); + leg->AddEntry(hBCTracksSum, "sum"); + } + }; + + cBC = new TCanvas(Form("bceff%s", append), "", ww, hh); + cBC->Divide(3, 2); + k = 0; + for (int i{1}; i <= 4; ++i) { + if (i == 3) { + ++k; + } + cBC->cd(i + k); + gPad->DrawFrame(-0.5, 0, 200 - 0.5, 1.02, + Form("Tracking Efficiency #times Fraction (#it{p}_{T} " + "integrated, %d-point " + "tracks%s);BC in " + "ROF;eff. (fake-rate)", + 3 + i, titlename)); + plotBCEff(i, i); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + } + cBC->cd(3); + gPad->DrawFrame(-0.5, 0, 200 - 0.5, 1.02, + Form("Tracking Efficiency (#it{p}_{T} " + "integrated, all point " + "tracks%s);BC in " + "ROF;eff. (fake-rate)", + titlename)); + plotBCEff(1, 4); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + + cBC->cd(6); + leg->Draw(); + } + + TString outname = TString::Format("trkeff%s.pdf", append); + cEff->cd(); + cEff->Update(); + cEff->Print(TString::Format("%s(", outname.Data()), "Title:Tracking Efficiency"); + cRatio->cd(); + cRatio->Update(); + cRatio->Print(outname.Data(), "Title:Ratios"); + cCont->cd(); + cCont->Update(); + cCont->Print(outname.Data(), "Title:Contribution"); + cBC->cd(); + cBC->Update(); + cBC->Print(TString::Format("%s)", outname.Data()), "Title:BC"); +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C b/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C index 832839c5bed0d..78503dc79fd48 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C @@ -1,3 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + /// \file CheckDigits.C /// \brief Simple macro to check ITSU digits @@ -91,8 +102,8 @@ void CheckDigits(std::string digifile = "itsdigits.root", std::string hitfile = // >> build min and max MC events used by each ROF for (int imc = MC2ROFRecordArrrayRef.size(); imc--;) { const auto& mc2rof = MC2ROFRecordArrrayRef[imc]; - printf("MCRecord: "); - mc2rof.print(); + // printf("MCRecord: "); + // mc2rof.print(); if (mc2rof.rofRecordID < 0) { continue; // this MC event did not contribute to any ROF @@ -198,10 +209,44 @@ void CheckDigits(std::string digifile = "itsdigits.root", std::string hitfile = } // end loop on ROFRecords array - new TCanvas; - nt->Draw("y:x"); - new TCanvas; - nt->Draw("dx:dz", "abs(dx)<0.01 && abs(dz)<0.01"); + auto canvXY = new TCanvas("canvXY", "", 1600, 1600); + canvXY->Divide(2, 2); + canvXY->cd(1); + nt->Draw("y:x>>h_y_vs_x_IB(1000, -5, 5, 1000, -5, 5)", "id < 3120 ", "colz"); + canvXY->cd(2); + nt->Draw("y:z>>h_y_vs_z_IB(1000, -15, 15, 1000, -5, 5)", "id < 3120 ", "colz"); + canvXY->cd(3); + nt->Draw("y:x>>h_y_vs_x_OB(1000, -50, 50, 1000, -50, 50)", "id >= 3120 ", "colz"); + canvXY->cd(4); + nt->Draw("y:z>>h_y_vs_z_OB(1000, -100, 100, 1000, -50, 50)", "id >= 3120 ", "colz"); + canvXY->SaveAs("itsdigits_y_vs_x_vs_z.pdf"); + + auto canvdXdZ = new TCanvas("canvdXdZ", "", 1600, 800); + canvdXdZ->Divide(2, 2); + canvdXdZ->cd(1); + nt->Draw("dx:dz>>h_dx_vs_dz_IB(500, -0.02, 0.02, 500, -0.01, 0.01)", "id < 3120", "colz"); + auto h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_IB"); + Info("IB", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("IB", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(2); + nt->Draw("dx:dz>>h_dx_vs_dz_OB(500, -0.02, 0.02, 500, -0.02, 0.02)", "id >= 3120", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OB"); + Info("OB", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OB", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(3); + nt->Draw("dx:dz>>h_dx_vs_dz_IB_z(500, -0.005, 0.005, 500, -0.005, 0.005)", "id < 3120 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_IB_z"); + Info("IB |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("IB |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(4); + nt->Draw("dx:dz>>h_dx_vs_dz_OB_z(500, -0.005, 0.005, 500, -0.005, 0.005)", "id >= 3120 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OB_z"); + Info("OB |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OB |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->SaveAs("itsdigits_dx_vs_dz.pdf"); + + f->Write(); + f->Close(); f->Write(); f->Close(); diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckDuplicates.C b/Detectors/ITSMFT/ITS/macros/test/CheckDuplicates.C new file mode 100644 index 0000000000000..abe707225ab30 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckDuplicates.C @@ -0,0 +1,249 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CheckDuplicates.C +/// \brief Macro to look for duplicate tracks across a ROF windows + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormatsITS/TrackITS.h" +#endif +#include "DataFormatsITSMFT/ROFRecord.h" + +void CheckDuplicates(TString tracfile = "./o2trac_its.root", TString output = ".", TString rootSave = "", int rofStart = 0, int rofEnd = -1, int windowSize = 2, bool includeSame = 1) +{ + gErrorIgnoreLevel = kWarning; + + // Obtain data from files: + TFile* file1 = TFile::Open(tracfile); + TTree* recTree = (TTree*)gFile->Get("o2sim"); + // Extract track information + std::vector* recArr = nullptr; + recTree->SetBranchAddress("ITSTrack", &recArr); + // Extract information on which ROFs contain which tracks + std::vector* rofArr = nullptr; + recTree->SetBranchAddress("ITSTracksROF", &rofArr); + + // Specify plot parameters: + int phiBins{101}, etaBins{101}; // number of bins for dPhi and dEta axes + double_t phiMin{-2 * TMath::Pi()}, phiMax{2 * TMath::Pi()}, etaMin{-4}, etaMax{4}; // dPhi and dEta axes limits + double_t phiLim{0.01}, etaLim{0.01}; // limits for determining duplicate status + auto h1 = new TH2D("h1", "", phiBins, phiMin, phiMax, etaBins, etaMin, etaMax); // Distribution of all comparisons over dPhi and dEta + auto h2 = new TH2D("h2", "All pairs; #||{#Delta#phi} [rad]; #||{#Delta#eta}", phiBins, 0, phiMax, etaBins, 0, etaMax); // Distribution of all comparisons over |dPhi| and |dEta| + auto h3 = new TH2D("dupl", "Duplicates; #Delta#phi; #Delta#eta", 100, -1 * phiLim, phiLim, 100, -1 * etaLim, etaLim); // Distribution of duplicates pairs over dPhi and dEta + auto h4 = new TH2D("h4", "Duplicates; avg. #phi; avg. #eta", 50, 0, phiMax, 50, etaMin, etaMax); // Distribution of duplicate pairs over avg. phi and avg. eta + + int comps{0}, dupl{0}, zeroes{0}, totalROFs{0}; // Collect summary information + double_t eta1, eta2, phi1, phi2, dPhi, dEta, avgPhi, avgEta; + int rofLow, rofHigh, rof1Low, rof1High, rof2Low, rof2High; + int rofFrameEnd = rofEnd; // In case frames have different numbers of ROFs, store the final ROF index separately for each frame inside rofFrameEnd + int totalFrames = recTree->GetEntriesFast(); + for (int frame = 0; frame < totalFrames; frame++) { // Loop over timeframes + LOGP(info, "Proceeding to frame {} of {}", frame + 1, totalFrames); + recTree->GetEvent(frame); + LOGP(info, "{} ROFs in frame {}/{}", rofArr->size(), frame + 1, totalFrames); + totalROFs += rofArr->size(); + if (rofEnd >= (int long)rofArr->size() || rofEnd < 0) { // Check whether rofEnd is greater than the total number of ROFs in this dataset, or negative; if so, set to maximum + LOGP(warn, "Frame {}/{}: currently set upper ROF limit {} out of bounds; set to maximum {} instead.", frame + 1, totalFrames, rofEnd, rofArr->size() - 1); + rofFrameEnd = rofArr->size() - 1; + } + + // First loop: for each pair of ROFs in the first window, compare the tracks within them + for (int rof1 = rofStart; rof1 < rofStart + windowSize - 1; rof1++) { + for (int rof2 = rof1 + 1; rof2 <= rofStart + windowSize - 1; rof2++) { + rof1Low = rofArr->at(rof1).getEntry().getFirstEntry(); + rof1High = rofArr->at(rof1).getEntry().getEntriesBound(); + rof2Low = rofArr->at(rof2).getEntry().getFirstEntry(); + rof2High = rofArr->at(rof2).getEntry().getEntriesBound(); + for (int iTrack1 = rof1Low; iTrack1 < rof1High; ++iTrack1) { + for (int iTrack2 = rof2Low; iTrack2 < rof2High; ++iTrack2) { + phi1 = recArr->at(iTrack1).getPhi(); + phi2 = recArr->at(iTrack2).getPhi(); + eta1 = recArr->at(iTrack1).getEta(); + eta2 = recArr->at(iTrack2).getEta(); + + dPhi = phi1 - phi2; + dEta = eta1 - eta2; + avgPhi = (phi1 + phi2) / 2; + avgEta = (eta1 + eta2) / 2; + h1->Fill(dPhi, dEta); + h2->Fill(abs(dPhi), abs(dEta)); + + if (abs(dPhi) < phiLim && abs(dEta) < etaLim) { + if (dPhi == 0 && dEta == 0) { + LOGP(warning, "Zero found; ROFs: {}, {}. Tracks: {}, {}. dPhi=dEta=0. Not included in plots.", rof1, rof2, iTrack1, iTrack2); + zeroes++; + } else { + LOGP(info, "Possible duplicate found. ROFs: {}, {}. Tracks: {}, {}. dPhi={}, dEta={}.", rof1, rof2, iTrack1, iTrack2, dPhi, dEta); + dupl++; + h3->Fill(dPhi, dEta); + h4->Fill(avgPhi, avgEta); + } + } + ++comps; + } + } + } + } + LOGP(info, "Frame {}/{}: initial ROF window {}-{} complete. {} possible duplicates out of {} total comparisons.", frame + 1, totalFrames, rofStart, rofStart + windowSize - 1, dupl, comps); + + // Next loops: for each new window, pair the last ROF with the other ROFs and run comparisons. We avoid other ROF pairs since they have already been done + for (int rofWLow = rofStart + 1; rofWLow <= rofFrameEnd - windowSize + 1; rofWLow++) { + int rof2 = rofWLow + windowSize - 1; + for (int rof1 = rofWLow; rof1 < rof2; rof1++) { + rof1Low = rofArr->at(rof1).getEntry().getFirstEntry(); + rof1High = rofArr->at(rof1).getEntry().getEntriesBound(); + rof2Low = rofArr->at(rof2).getEntry().getFirstEntry(); + rof2High = rofArr->at(rof2).getEntry().getEntriesBound(); + for (int iTrack1 = rof1Low; iTrack1 < rof1High; ++iTrack1) { + for (int iTrack2 = rof2Low; iTrack2 < rof2High; ++iTrack2) { + phi1 = recArr->at(iTrack1).getPhi(); + phi2 = recArr->at(iTrack2).getPhi(); + eta1 = recArr->at(iTrack1).getEta(); + eta2 = recArr->at(iTrack2).getEta(); + + dPhi = phi1 - phi2; + dEta = eta1 - eta2; + avgPhi = (phi1 + phi2) / 2; + avgEta = (eta1 + eta2) / 2; + h1->Fill(dPhi, dEta); + h2->Fill(abs(dPhi), abs(dEta)); + + if (abs(dPhi) < phiLim && abs(dEta) < etaLim) { + if (dPhi == 0 && dEta == 0) { + LOGP(warning, "Zero found; ROFs: {}, {}. Tracks: {}, {}. dPhi=dEta=0. Not included in plots.", rof1, rof2, iTrack1, iTrack2); + zeroes++; + } else { + LOGP(info, "Possible duplicate found. ROFs: {}, {}. Tracks: {}, {}. dPhi={}, dEta={}.", rof1, rof2, iTrack1, iTrack2, dPhi, dEta); + dupl++; + h3->Fill(dPhi, dEta); + h4->Fill(avgPhi, avgEta); + } + } + ++comps; + } + } + } + } + LOGP(info, "Frame {}/{}: inter-ROF analysis complete. {} possible duplicates out of {} total comparisons.", frame + 1, totalFrames, dupl, comps); + + if (includeSame) { // If includeSame == 1, then we also run comparisons between tracks in the same ROF. Otherwise, skip + for (int rof = rofStart; rof <= rofFrameEnd; rof++) { + rofLow = rofArr->at(rof).getEntry().getFirstEntry(); + rofHigh = rofArr->at(rof).getEntry().getEntriesBound(); + for (int iTrack1 = rofLow; iTrack1 < rofHigh; ++iTrack1) { + for (int iTrack2 = iTrack1 + 1; iTrack2 < rofHigh; ++iTrack2) { + phi1 = recArr->at(iTrack1).getPhi(); + phi2 = recArr->at(iTrack2).getPhi(); + eta1 = recArr->at(iTrack1).getEta(); + eta2 = recArr->at(iTrack2).getEta(); + + dPhi = phi1 - phi2; + dEta = eta1 - eta2; + avgPhi = (phi1 + phi2) / 2; + avgEta = (eta1 + eta2) / 2; + h1->Fill(dPhi, dEta); + h2->Fill(abs(dPhi), abs(dEta)); + + if (abs(dPhi) < phiLim && abs(dEta) < etaLim) { + if (dPhi == 0 && dEta == 0) { + LOGP(warning, "Zero found; ROFs: {}, {}. Tracks: {}, {}. dPhi=dEta=0. Not included in plots.", rof, rof, iTrack1, iTrack2); + zeroes++; + } else { + LOGP(info, "Possible duplicate found. ROFs: {}, {}. Tracks: {}, {}. dPhi={}, dEta={}.", rof, rof, iTrack1, iTrack2, dPhi, dEta); + dupl++; + h3->Fill(dPhi, dEta); + h4->Fill(avgPhi, avgEta); + } + } + ++comps; + } + } + } + LOGP(info, "Frame {}/{}: intra-ROF analysis complete. {} possible duplicates out of {} total comparisons.", frame + 1, totalFrames, dupl, comps); + } else + LOGP(info, "Frame {}/{}: intra-ROF analysis not included as per parameters (includeSame={})", frame + 1, totalFrames, includeSame); + } + // NOTE: comparisons between ROFs in different frames are intentionally left out. Only if reconstruction was performed on contiguous CTFs do comparisons between + // ROFs in different frames make sense. + + TString prefix = ""; // insert any more information you'd like to see on the plot title here, like run numbers + h1->SetTitle(Form("%s/%i ROFs/window size %i;#Delta#phi [rad];#Delta#eta", prefix.Data(), totalROFs, windowSize)); + + gStyle->SetOptStat("ne"); + auto c1 = new TCanvas("c1", "", 0, 0, 1200, 800); + h1->SetStats(1); + h1->GetYaxis()->SetTitleOffset(1.4); + h1->GetXaxis()->SetTitleOffset(1.4); + h1->GetXaxis()->CenterTitle(true); + h1->GetYaxis()->CenterTitle(true); + h1->Draw("lego"); + + auto c2 = new TCanvas("c2", "", 0, 0, 1200, 800); + h2->SetStats(0); + h2->Draw("COLZ"); + + auto c3 = new TCanvas("c3", "", 0, 0, 1200, 800); + h3->SetStats(1); + h3->GetXaxis()->SetMaxDigits(1); + h3->GetXaxis()->CenterTitle(true); + h3->GetYaxis()->CenterTitle(true); + TGaxis::SetExponentOffset(0, -0.05, "x"); + h3->GetYaxis()->SetMaxDigits(1); + h3->GetYaxis()->SetTitleOffset(0.8); + h3->Draw("COLZ"); + + auto c4 = new TCanvas("c4", "", 0, 0, 1200, 800); + h4->SetStats(0); + h4->Draw("COLZ"); + + auto c5 = new TCanvas("c5", "", 0, 0, 1200, 800); + TH1D* h5 = h4->ProjectionX("pPhi"); + h5->SetTitle("Duplicate distribution over #phi"); + c5->SetLogy(); + h5->SetStats(0); + h5->Draw(); + + auto c6 = new TCanvas("c6", "", 0, 0, 1200, 800); + TH1D* h6 = h4->ProjectionY("pEta"); + h6->SetTitle("Duplicate distribution over #eta"); + c6->SetLogy(); + h6->SetStats(0); + h6->Draw(); + + if (rootSave.Length() > 0) { + TFile output(Form("%s/projections.root", rootSave.Data()), "RECREATE"); + h5->Write(); + h6->Write(); + output.Close(); + } + + TString filename = Form("%s/plots.pdf", output.Data()); + c1->Print(filename + "["); + c1->Print(filename); + c2->Print(filename); + c3->Print(filename); + c4->Print(filename); + c5->Print(filename); + c6->Print(filename); + c6->Print(filename + "]"); + LOGP(info, "Summary:"); + LOGP(info, "Zeroes: {}; duplicates: {}; total comparisons: {}.", zeroes, dupl, comps); +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckITSTracksVsROF.C b/Detectors/ITSMFT/ITS/macros/test/CheckITSTracksVsROF.C new file mode 100644 index 0000000000000..38483a2c0d075 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckITSTracksVsROF.C @@ -0,0 +1,397 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +int32_t nBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; +int32_t nBCsPerITSROF = 198; +int32_t offsetITSROF = 64; // from current analysis of delay of ITS ROF with respect to other detectors +constexpr float minCollTimeFT0A = -5.f; +constexpr float maxCollTimeFT0A = 5.f; +constexpr float minCollTimeFT0C = -5.f; +constexpr float maxCollTimeFT0C = 5.f; +constexpr float minCollTimeFV0A = -5.f; +constexpr float maxCollTimeFV0A = 5.f; +constexpr float maxZv = 10.f; +constexpr float maxTrackEta = 0.8f; + +// Simple macro to check ITS tracks in AO2D files as a function of bunch crossing within a ROF + +void CheckITSTracksVsROF(int maxFiles = 6, int maxDF = 99999) +{ + + printf("nBCsPerOrbit = %d\n", nBCsPerOrbit); + // define histograms + TH1F* hTrNoColl = new TH1F("hTrNoColl", "", 2, -0.5, 1.5); + hTrNoColl->GetXaxis()->SetBinLabel(1, "No collision assoc."); + hTrNoColl->GetXaxis()->SetBinLabel(2, "With collision assoc."); + TH1F* hTrTime = new TH1F("hTrTime", ";time;entries", 100, -10000., 10000.); + TH1F* hCollTime = new TH1F("hCollTime", ";time;entries", 100, -20., 20.); + TH1F* hVz = new TH1F("hVz", ";z_{v} (cm);entries", 100, -20., 20.); + TH2F* hTrVsCollTime = new TH2F("hTrVsCollTime", ";coll time;track time", 100, -20., 20., 100, -10000., 10000.); + int nBinsROF = 395; + float minROF = -197.5; + float maxROF = 197.5; + TH2F* hCluTrackVsBCinROF = new TH2F("hCluTrackVsBC", ";bcInROF;N_{ITSclusters}", nBinsROF, minROF, maxROF, 8, -0.5, 7.5); + TH1F* hNEventsVsBC = new TH1F("hNEventsVsBC", ";bc;N_{Collisions}", 3601, -0.5, 3600.5); + TH1F* hNTracksVsBC = new TH1F("hNTracksVsBC", ";bc;N_{Tracks}", 3601, -0.5, 3600.5); + TH1F* hNTracksPerEventVsBC = new TH1F("hNTracksPerEventVsBC", ";bc;N_{Tracks} per event", 3601, -0.5, 3600.5); + TH1F* hNEventsVsBCinROF = new TH1F("hNEventsVsBCinROF", ";bcInROF;N_{Collisions}", nBinsROF, minROF, maxROF); + TH1F* hNTracksVsBCinROF = new TH1F("hNTracksVsBCinROF", ";bcInROF;N_{Tracks}", nBinsROF, minROF, maxROF); + TH1F* hNTracksPerEventVsBCinROF = new TH1F("hNTracksPerEventVsBCinROF", ";bcInROF;N_{Tracks} per event", nBinsROF, minROF, maxROF); + TH2F* hNPVContribVsBCinROF = new TH2F("hNPVContribVsBC", ";bcInROF;N_{PVcontrib}", nBinsROF, minROF, maxROF, 151, -0.5, 150.5); + + // process all AO2D files in subdirectories of the current directory + gSystem->Exec("find ./ -name AO2D.root | sort > filelist.txt"); + int nFiles = 0; + std::ifstream aodList("filelist.txt"); + std::string line; + std::vector filNames; + while (std::getline(aodList, line)) { + if (!line.empty()) { + filNames.push_back(line); + ++nFiles; + } + } + if (nFiles > maxFiles) + nFiles = maxFiles; + + for (int jf = 0; jf < nFiles; ++jf) { + TFile* fil = TFile::Open(filNames[jf].c_str()); + if (!fil || fil->IsZombie()) { + printf("Cannot open %s\n", filNames[jf].c_str()); + continue; + } + TList* l = (TList*)fil->GetListOfKeys(); + int nKeys = l->GetEntries(); + for (int j = 0; j < nKeys; ++j) { + if (j >= maxDF) + break; + TKey* k = (TKey*)l->At(j); + TString cname = k->GetClassName(); + if (cname == "TDirectoryFile") { + TDirectoryFile* d = (TDirectoryFile*)fil->Get(k->GetName()); + printf("%s %s\n", fil->GetName(), d->GetName()); + + // Access TTree with BC info + TTree* tb = (TTree*)d->Get("O2bc_001"); + if (!tb) { + printf("TTree O2bc_001 missing\n"); + continue; + } + printf(" BC Tree entries = %lld\n", tb->GetEntries()); + ULong64_t gloBC; + int nRun; + tb->SetBranchAddress("fRunNumber", &nRun); + tb->SetBranchAddress("fGlobalBC", &gloBC); + + // Access TTree with collision info + TTree* tc = (TTree*)d->Get("O2collision_001"); + if (!tc) { + printf("TTree O2collision_001 missing\n"); + continue; + } + printf(" Collision Tree entries = %lld\n", tc->GetEntries()); + float xv, yv, zv, ctime; + int iBC; + ushort nPVcontrib; + tc->SetBranchAddress("fPosX", &xv); + tc->SetBranchAddress("fPosY", &yv); + tc->SetBranchAddress("fPosZ", &zv); + tc->SetBranchAddress("fCollisionTime", &ctime); + tc->SetBranchAddress("fNumContrib", &nPVcontrib); + tc->SetBranchAddress("fIndexBCs", &iBC); + + // Access TTree with FT0 info + TTree* tft = (TTree*)d->Get("O2ft0"); + if (!tft) { + printf("TTree O2ft0 missing\n"); + continue; + } + printf(" FT0 Tree entries = %lld\n", tft->GetEntries()); + float timeft0a, timeft0c; + int iBCft0; + UChar_t trigMask; + tft->SetBranchAddress("fIndexBCs", &iBCft0); + tft->SetBranchAddress("fTimeA", &timeft0a); + tft->SetBranchAddress("fTimeC", &timeft0c); + tft->SetBranchAddress("fTriggerMask", &trigMask); + + // Access TTree with FV0 info + TTree* tfv = (TTree*)d->Get("O2fv0a"); + if (!tfv) { + printf("TTree O2fv0a missing\n"); + continue; + } + printf(" FV0A Tree entries = %lld\n", tfv->GetEntries()); + float timefv0a; + int iBCfv0; + tfv->SetBranchAddress("fIndexBCs", &iBCfv0); + tfv->SetBranchAddress("fTime", &timefv0a); + + // Access TTree with track parameters at their innermost update + TTree* tt = (TTree*)d->Get("O2track_iu"); + if (!tt) { + printf("TTree O2track_iu missing\n"); + continue; + } + printf(" Track Tree entries = %lld\n", tt->GetEntries()); + // 5 parameters defining the track helix: y, z, sin(phi), tan(lambda), q/pt + // x is a coordinate along the track + // alpha: angle between track reference system and global reference system + float x, alp, y, z, snp, tgl, qpt; + int iColl; + tt->SetBranchAddress("fIndexCollisions", &iColl); + tt->SetBranchAddress("fX", &x); + tt->SetBranchAddress("fAlpha", &alp); + tt->SetBranchAddress("fY", &y); + tt->SetBranchAddress("fZ", &z); + tt->SetBranchAddress("fSnp", &snp); + tt->SetBranchAddress("fTgl", &tgl); + tt->SetBranchAddress("fSigned1Pt", &qpt); + + // Access TTree with track extra information + TTree* te = (TTree*)d->Get("O2trackextra_002"); + if (!te) + te = (TTree*)d->Get("O2trackextra_001"); + if (!te) { + printf("TTree O2trackextra_002 and O2trackextra_001 both missing\n"); + continue; + } + uint itsclusiz; + uint8_t nTPCclu; + uint trflag; + float itschi2, tpcchi2, trtime; + te->SetBranchAddress("fITSClusterSizes", &itsclusiz); + te->SetBranchAddress("fITSChi2NCl", &itschi2); + te->SetBranchAddress("fTPCNClsFindable", &nTPCclu); + te->SetBranchAddress("fTPCChi2NCl", &tpcchi2); + te->SetBranchAddress("fTrackTime", &trtime); + te->SetBranchAddress("fFlags", &trflag); + + // get run information + if (tb->GetEntries() == 0) { + printf("Empty BC tree\n"); + continue; + } + tb->GetEntry(0); + auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), nRun); + // uint64_t sorTimestamp = runInfo.sor; + // uint64_t eorTimestamp = runInfo.eor; + int64_t bcSOR = runInfo.orbitSOR * nBCsPerOrbit; + int64_t nBCsPerTF = runInfo.orbitsPerTF * nBCsPerOrbit; + auto calcBcInROF = [&](ULong64_t bcGlobal) { + int bc = (bcGlobal + nBCsPerOrbit) % nBCsPerITSROF; + bc -= offsetITSROF; + return bc; + }; + + // fill maps for BC and FT0 + int nFT0 = tft->GetEntries(); + std::unordered_map bcToFT0; + bcToFT0.reserve(nFT0); + for (int jft = 0; jft < nFT0; ++jft) { + tft->GetEntry(jft); + bcToFT0[iBCft0] = jft; + } + // fill maps for BC and FV0 + int nFV0 = tfv->GetEntries(); + std::unordered_map bcToFV0; + bcToFV0.reserve(nFV0); + for (int jfv = 0; jfv < nFV0; ++jfv) { + tfv->GetEntry(jfv); + bcToFV0[iBCfv0] = jfv; + } + // tag good collisions + int nColl = tc->GetEntries(); + std::vector ft0indices(nColl, -1); + std::vector fv0indices(nColl, -1); + std::vector isGoodColl(nColl, false); + for (int i = 0; i < nColl; ++i) { + ft0indices[i] = -1; + tc->GetEntry(i); + if (iBC < 0 || iBC >= tb->GetEntries()) { + printf("ERROR: iBC out of range\n"); + continue; + } + tb->GetEntry(iBC); + auto it = bcToFT0.find(iBC); + if (it != bcToFT0.end()) + ft0indices[i] = it->second; + auto iv = bcToFV0.find(iBC); + if (iv != bcToFV0.end()) + fv0indices[i] = iv->second; + if (std::abs(zv) > maxZv) + continue; + int jft0 = ft0indices[i]; + int jfv0 = fv0indices[i]; + if (jft0 >= 0 && jfv0 >= 0) { + tft->GetEntry(jft0); + tfv->GetEntry(jfv0); + bool isTVX = TESTBIT(trigMask, o2::ft0::Triggers::bitVertex); + int64_t bcInTF = (static_cast(gloBC) - bcSOR) % nBCsPerTF; + bool isTFBorderOK = (bcInTF > 300 && bcInTF < nBCsPerTF - 4000); + if (isTFBorderOK && isTVX && + timeft0a > minCollTimeFT0A && timeft0a < maxCollTimeFT0A && + timeft0c > minCollTimeFT0C && timeft0c < maxCollTimeFT0C && + timefv0a > minCollTimeFV0A && timefv0a < maxCollTimeFV0A) { + isGoodColl[i] = true; + int bcInITSROF = calcBcInROF(gloBC); + hNEventsVsBC->Fill(gloBC % nBCsPerOrbit); + hNEventsVsBCinROF->Fill(bcInITSROF); + hCollTime->Fill(ctime); + hVz->Fill(zv); + } + } + } + + // loop over tracks + int nTracks = tt->GetEntries(); + for (int i = 0; i < nTracks; ++i) { + // O2track_iu and O2trackextra_xxx entries are aligned 1-to-1 + tt->GetEntry(i); + te->GetEntry(i); + // compute the ITS cluster map, which can be used in track selecion + int nITSclu = 0; + uint itsCluMap = 0; + for (int jLay = 0; jLay < 7; ++jLay) { + if ((itsclusiz >> (jLay * 4)) & 0xf) { + nITSclu++; + itsCluMap |= (1 << jLay); + } + } + // track selections + if (!(trflag & o2::aod::track::PVContributor)) + continue; + if (nITSclu == 0 || itschi2 < 0. || tpcchi2 < 0.) + continue; + // compute eta from track dip angle lambda via tgl = tan(lambda) + float cl = 1. / std::sqrt(1. + tgl * tgl); + float sl = cl * tgl; + float eta = 0.5 * std::log((1 + sl) / (1 - sl)); + if (std::abs(eta) > maxTrackEta) + continue; + // collision selections + if (iColl >= 0 && iColl < nColl && isGoodColl[iColl]) { + tc->GetEntry(iColl); + hTrTime->Fill(trtime); + hTrVsCollTime->Fill(ctime, trtime); + tb->GetEntry(iBC); + int bcInITSROF = calcBcInROF(gloBC); + hNTracksVsBC->Fill(gloBC % nBCsPerOrbit); + hCluTrackVsBCinROF->Fill(bcInITSROF, nITSclu); + hNTracksVsBCinROF->Fill(bcInITSROF); + hNPVContribVsBCinROF->Fill(bcInITSROF, nPVcontrib); + hTrNoColl->Fill(1); + } else { + hTrNoColl->Fill(0); + } + } + } + } + } + + // plotting + TProfile* pctr = hNPVContribVsBCinROF->ProfileX(); + TProfile* pclu = hCluTrackVsBCinROF->ProfileX(); + TH1F* hFrac7 = new TH1F("hFrac7", ";bcInROF;Fraction of tracks with 7 clusters", hCluTrackVsBCinROF->GetXaxis()->GetNbins(), hCluTrackVsBCinROF->GetXaxis()->GetXmin(), hCluTrackVsBCinROF->GetXaxis()->GetXmax()); + for (int jx = 1; jx <= hCluTrackVsBCinROF->GetXaxis()->GetNbins(); jx++) { + double tot = 0.; + for (int jy = 1; jy <= hCluTrackVsBCinROF->GetYaxis()->GetNbins(); jy++) { + double c = hCluTrackVsBCinROF->GetBinContent(jx, jy); + tot += c; + } + double seven = hCluTrackVsBCinROF->GetBinContent(jx, 8); + double f = 0.; + double ef = 0.; + if (tot > 0) { + f = seven / tot; + ef = std::sqrt(f * (1 - f) / tot); + } + hFrac7->SetBinContent(jx, f); + hFrac7->SetBinError(jx, ef); + } + for (int jx = 1; jx <= hNTracksVsBC->GetXaxis()->GetNbins(); jx++) { + double nt = hNTracksVsBC->GetBinContent(jx); + double ne = hNEventsVsBC->GetBinContent(jx); + if (ne > 0) + hNTracksPerEventVsBC->SetBinContent(jx, nt / ne); + } + for (int jx = 1; jx <= hNTracksVsBCinROF->GetXaxis()->GetNbins(); jx++) { + double nt = hNTracksVsBCinROF->GetBinContent(jx); + double ne = hNEventsVsBCinROF->GetBinContent(jx); + if (ne > 0) + hNTracksPerEventVsBCinROF->SetBinContent(jx, nt / ne); + } + + hCluTrackVsBCinROF->SetStats(0); + hFrac7->SetStats(0); + hNPVContribVsBCinROF->SetStats(0); + + TCanvas* ctr = new TCanvas("ctr", "", 1400, 500); + ctr->Divide(3, 1); + ctr->cd(1); + hTrNoColl->Draw(); + ctr->cd(2); + hCollTime->Draw(); + ctr->cd(3); + hVz->Draw(); + + TCanvas* cbc = new TCanvas("cbc", "", 1400, 800); + cbc->Divide(1, 3); + cbc->cd(1); + hNEventsVsBC->Draw(); + cbc->cd(2); + hNTracksVsBC->Draw(); + cbc->cd(3); + hNTracksPerEventVsBC->Draw(); + + TCanvas* crof = new TCanvas("crof", "", 1400, 800); + crof->Divide(3, 2); + crof->cd(1); + hNEventsVsBCinROF->Draw(); + crof->cd(2); + hNTracksVsBCinROF->Draw(); + crof->cd(3); + hNTracksPerEventVsBCinROF->Draw(); + crof->cd(4); + gPad->SetLogz(); + hCluTrackVsBCinROF->Draw("colz"); + pclu->SetLineWidth(2); + pclu->Draw("same"); + crof->cd(5); + hFrac7->SetMinimum(0.); + hFrac7->SetMaximum(1.); + hFrac7->SetLineWidth(2); + hFrac7->Draw(); + crof->cd(6); + gPad->SetLogz(); + hNPVContribVsBCinROF->Draw("colz"); + pctr->SetLineWidth(2); + pctr->Draw("same"); + crof->SaveAs("ITSTracksVsBCinROF.png"); +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C b/Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C new file mode 100644 index 0000000000000..915f2dda75032 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C @@ -0,0 +1,706 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormatsITS/Vertex.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/O2DatabasePDG.h" +#include "Steer/MCKinematicsReader.h" +#endif + +constexpr const char* tracFile = "o2trac_its.root"; +constexpr const char* collContextFile = "collisioncontext.root"; + +namespace +{ +namespace fs = std::filesystem; + +constexpr float MinPt = 0.05f; +constexpr float MaxEta = 1.1f; +constexpr int NMultiplicityBins = 11; +constexpr std::array MultiplicityLabels{{"2", "3", "4-5", "6-8", "9-13", "14-21", "22-33", "34-52", "53-83", "84-128", "128+"}}; + +struct TruthInfo { + int multiplicity = 0; + float x = 0.f; + float y = 0.f; + float z = 0.f; +}; + +struct BestRecoInfo { + o2::its::Vertex vertex; + float purity = -1.f; +}; + +struct GaussianSummary { + bool valid = false; + double mean = 0.; + double sigma = 0.; +}; + +bool isTrueVertexLabel(const o2::MCCompLabel& label) +{ + return label.isValid() && !label.isFake() && label.getSourceID() == 0; +} + +bool isChargedPrimary(const o2::MCTrack& track) +{ + if (!track.isPrimary() || track.GetPt() < MinPt || std::abs(track.GetEta()) > MaxEta) { + return false; + } + auto* pdg = o2::O2DatabasePDG::Instance()->GetParticle(track.GetPdgCode()); + return pdg != nullptr && pdg->Charge() != 0.; +} + +bool isBetterReco(const o2::its::Vertex& candidate, float candidatePurity, const o2::its::Vertex& current, float currentPurity) +{ + if (candidatePurity != currentPurity) { + return candidatePurity > currentPurity; + } + if (candidate.getNContributors() != current.getNContributors()) { + return candidate.getNContributors() > current.getNContributors(); + } + return candidate.getChi2() < current.getChi2(); +} + +int getMultiplicityCategory(int multiplicity) +{ + if (multiplicity <= 2) { + return 1; + } + if (multiplicity <= 3) { + return 2; + } + if (multiplicity <= 5) { + return 3; + } + if (multiplicity <= 8) { + return 4; + } + if (multiplicity <= 13) { + return 5; + } + if (multiplicity <= 21) { + return 6; + } + if (multiplicity <= 33) { + return 7; + } + if (multiplicity <= 52) { + return 8; + } + if (multiplicity <= 83) { + return 9; + } + if (multiplicity <= 128) { + return 10; + } + return 11; +} + +void fillMultiplicityHistogram(TH1* hist, int multiplicity) +{ + hist->Fill(getMultiplicityCategory(multiplicity)); +} + +GaussianSummary fitGaussianCore(TH1* hist, const char* funcName) +{ + if (hist == nullptr || hist->GetEntries() < 20) { + return {}; + } + const auto rms = hist->GetRMS(); + if (!(rms > 0.)) { + return {}; + } + + TF1 fit(funcName, "gaus", hist->GetMean() - 2. * rms, hist->GetMean() + 2. * rms); + fit.SetParameters(hist->GetMaximum(), hist->GetMean(), rms); + hist->Fit(&fit, "Q0R"); + + const auto mean = fit.GetParameter(1); + const auto sigma = std::abs(fit.GetParameter(2)); + if (!(sigma > 0.)) { + return {}; + } + + fit.SetRange(mean - 2. * sigma, mean + 2. * sigma); + hist->Fit(&fit, "Q0R"); + return {true, fit.GetParameter(1), std::abs(fit.GetParameter(2))}; +} + +TH1D* makeNormalizedCopy(const TH1D* source, const char* name, const char* title) +{ + auto* copy = static_cast(source->Clone(name)); + copy->SetTitle(title); + const auto integral = copy->Integral("width"); + if (integral > 0.) { + copy->Scale(1. / integral); + } + return copy; +} + +void setMultiplicityBinLabels(TH1* hist) +{ + for (int i = 0; i < NMultiplicityBins; ++i) { + hist->GetXaxis()->SetBinLabel(i + 1, MultiplicityLabels[i]); + } +} + +void setHistogramStyle(TH1* hist, int color, int marker) +{ + hist->SetLineColor(color); + hist->SetMarkerColor(color); + hist->SetMarkerStyle(marker); + hist->SetLineWidth(2); +} + +void printGaussianByMultiplicity(const std::array& summaries, const char* title) +{ + std::printf("%s:\n", title); + for (int i = 0; i < NMultiplicityBins; ++i) { + if (summaries[i].valid) { + std::printf(" %-4s : mean=%.6g sigma=%.6g\n", MultiplicityLabels[i], summaries[i].mean, summaries[i].sigma); + } else { + std::printf(" %-4s : n/a\n", MultiplicityLabels[i]); + } + } +} + +fs::path resolveContextFile(const fs::path& dir) +{ + const std::array candidates{ + dir / collContextFile, + dir.parent_path() / collContextFile, + fs::current_path() / collContextFile}; + for (const auto& candidate : candidates) { + if (!candidate.empty() && fs::exists(candidate) && fs::is_regular_file(candidate)) { + return candidate; + } + } + return {}; +} + +void printBinnedFractions(const TH1* numerator, const TH1* denominator, const char* title) +{ + if (numerator == nullptr || denominator == nullptr) { + return; + } + std::printf("%s:\n", title); + for (int iBin = 1; iBin <= denominator->GetNbinsX(); ++iBin) { + const auto den = denominator->GetBinContent(iBin); + const auto num = numerator->GetBinContent(iBin); + const auto value = den > 0. ? num / den : 0.; + std::printf(" %-4s : %.4f (%g / %g)\n", denominator->GetXaxis()->GetBinLabel(iBin), value, num, den); + } +} + +std::vector findDirs(const std::string& roots) +{ + fs::path root = roots.empty() ? fs::current_path() : fs::path{roots}; + std::vector result; + const auto hasFiles = [](const fs::path& dir) { + const auto tracPath = dir / tracFile; + return fs::exists(tracPath) && fs::is_regular_file(tracPath); + }; + + if (fs::is_directory(root) && hasFiles(root)) { + result.push_back(root); + return result; + } + + for (const auto& entry : fs::recursive_directory_iterator(root)) { + if (entry.is_directory() && hasFiles(entry.path())) { + result.push_back(entry.path()); + } + } + std::sort(result.begin(), result.end()); + return result; +} + +} // namespace + +void CheckSeeding(const std::string& dir = "") +{ + using Vertex = o2::its::Vertex; + const auto cwd = fs::current_path(); + gStyle->SetOptStat(0); + TH1::AddDirectory(kFALSE); + + auto dirs = findDirs(dir); + std::printf("Will iterate over %zu input dirs\n", dirs.size()); + if (dirs.empty()) { + std::printf("No input directories containing %s were found.\n", tracFile); + return; + } + + auto* hTruthMultiplicityFindable = new TH1D("hTruthMultiplicityFindable", + "Findable truth vertices;truth multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hTruthMultiplicityFound = new TH1D("hTruthMultiplicityFound", + "Found truth vertices;truth multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hRecoMultiplicityTrue = new TH1D("hRecoMultiplicityTrue", + "True reconstructed vertices;reco multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hRecoMultiplicityFake = new TH1D("hRecoMultiplicityFake", + "Fake reconstructed vertices;reco multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hDx = new TH1D("hDx", "Matched vertex residuals;x_{reco}-x_{MC} (cm);vertices", 400, -0.02, 0.02); + auto* hDy = new TH1D("hDy", "Matched vertex residuals;y_{reco}-y_{MC} (cm);vertices", 400, -0.02, 0.02); + auto* hDz = new TH1D("hDz", "Matched vertex residuals;z_{reco}-z_{MC} (cm);vertices", 400, -0.02, 0.02); + auto* hPullX = new TH1D("hPullX", "Matched vertex pulls;x pull;vertices", 600, -30., 30.); + auto* hPullY = new TH1D("hPullY", "Matched vertex pulls;y pull;vertices", 600, -30., 30.); + auto* hPullZ = new TH1D("hPullZ", "Matched vertex pulls;z pull;vertices", 600, -30., 30.); + std::array hPullXByMult{}; + std::array hPullYByMult{}; + std::array hPullZByMult{}; + for (int i = 0; i < NMultiplicityBins; ++i) { + const auto nameX = std::string("hPullX_") + std::to_string(i + 1); + const auto nameY = std::string("hPullY_") + std::to_string(i + 1); + const auto nameZ = std::string("hPullZ_") + std::to_string(i + 1); + const auto titleX = std::string("x pull ") + MultiplicityLabels[i] + ";x pull;vertices"; + const auto titleY = std::string("y pull ") + MultiplicityLabels[i] + ";y pull;vertices"; + const auto titleZ = std::string("z pull ") + MultiplicityLabels[i] + ";z pull;vertices"; + hPullXByMult[i] = new TH1D(nameX.c_str(), titleX.c_str(), 600, -30., 30.); + hPullYByMult[i] = new TH1D(nameY.c_str(), titleY.c_str(), 600, -30., 30.); + hPullZByMult[i] = new TH1D(nameZ.c_str(), titleZ.c_str(), 600, -30., 30.); + } + + setMultiplicityBinLabels(hTruthMultiplicityFindable); + setMultiplicityBinLabels(hTruthMultiplicityFound); + setMultiplicityBinLabels(hRecoMultiplicityTrue); + setMultiplicityBinLabels(hRecoMultiplicityFake); + setHistogramStyle(hTruthMultiplicityFindable, kGray + 2, 20); + setHistogramStyle(hTruthMultiplicityFound, kAzure + 2, 20); + setHistogramStyle(hRecoMultiplicityTrue, kAzure + 2, 20); + setHistogramStyle(hRecoMultiplicityFake, kOrange + 7, 24); + setHistogramStyle(hDx, kAzure + 2, 20); + setHistogramStyle(hDy, kGreen + 2, 21); + setHistogramStyle(hDz, kRed + 1, 24); + setHistogramStyle(hPullX, kAzure + 2, 20); + setHistogramStyle(hPullY, kGreen + 2, 21); + setHistogramStyle(hPullZ, kRed + 1, 24); + + size_t findable = 0; + size_t totalFound = 0; + size_t trueFound = 0; + size_t fakeFound = 0; + size_t uniqueTrueReco = 0; + size_t uniqueFindableFound = 0; + size_t sigmaXCount = 0; + size_t sigmaYCount = 0; + size_t sigmaZCount = 0; + double sumSigmaX = 0.; + double sumSigmaY = 0.; + double sumSigmaZ = 0.; + + for (const auto& inputDir : dirs) { + fs::current_path(inputDir); + std::printf("Working on %s\n", inputDir.c_str()); + const auto contextPath = resolveContextFile(inputDir); + if (contextPath.empty()) { + std::printf("Skipping %s: could not locate %s\n", inputDir.c_str(), collContextFile); + continue; + } + + o2::steer::MCKinematicsReader mcReader(contextPath.string()); + if (!mcReader.isInitialized()) { + std::printf("Skipping %s: failed to initialize MCKinematicsReader from %s\n", inputDir.c_str(), contextPath.c_str()); + continue; + } + + std::unordered_map findableTruths; + std::unordered_set uniqueTrueLabelsReco; + std::unordered_set uniqueFindableTruthFound; + std::unordered_map bestRecoByTruth; + + const int iSrc = 0; + const auto nEvents = static_cast(mcReader.getNEvents(iSrc)); + for (int iEve = 0; iEve < nEvents; ++iEve) { + const auto& tracks = mcReader.getTracks(iSrc, iEve); + const auto contributors = static_cast(std::count_if(tracks.begin(), tracks.end(), isChargedPrimary)); + if (contributors >= 2) { + const auto& header = mcReader.getMCEventHeader(iSrc, iEve); + findableTruths.emplace(iEve, TruthInfo{contributors, (float)header.GetX(), (float)header.GetY(), (float)header.GetZ()}); + fillMultiplicityHistogram(hTruthMultiplicityFindable, contributors); + } + mcReader.releaseTracksForSourceAndEvent(iSrc, iEve); + } + + auto* tracFileHandle = TFile::Open((inputDir / tracFile).c_str()); + if (tracFileHandle == nullptr || tracFileHandle->IsZombie()) { + std::printf("Skipping %s: failed to open %s\n", inputDir.c_str(), tracFile); + delete tracFileHandle; + continue; + } + + auto* tracTree = tracFileHandle->Get("o2sim"); + if (tracTree == nullptr) { + std::printf("Skipping %s: missing o2sim tree in %s\n", inputDir.c_str(), tracFile); + tracFileHandle->Close(); + delete tracFileHandle; + continue; + } + + if (tracTree->GetBranch("Vertices") == nullptr || tracTree->GetBranch("ITSVertexMCTruth") == nullptr) { + std::printf("Skipping %s: missing vertex branches in %s\n", inputDir.c_str(), tracFile); + tracFileHandle->Close(); + delete tracFileHandle; + continue; + } + + std::vector* vertices = nullptr; + std::vector* labels = nullptr; + std::vector* purities = nullptr; + const bool hasPurityBranch = tracTree->GetBranch("ITSVertexMCPurity") != nullptr; + + tracTree->SetBranchAddress("Vertices", &vertices); + tracTree->SetBranchAddress("ITSVertexMCTruth", &labels); + if (hasPurityBranch) { + tracTree->SetBranchAddress("ITSVertexMCPurity", &purities); + } + + const auto nEntries = tracTree->GetEntriesFast(); + for (Long64_t iEntry = 0; iEntry < nEntries; ++iEntry) { + tracTree->GetEntry(iEntry); + if (vertices == nullptr || labels == nullptr) { + continue; + } + auto nVertices = std::min(vertices->size(), labels->size()); + if (hasPurityBranch && purities != nullptr) { + nVertices = std::min(nVertices, purities->size()); + } + + for (size_t iVtx = 0; iVtx < nVertices; ++iVtx) { + const auto& vertex = (*vertices)[iVtx]; + const auto& label = (*labels)[iVtx]; + const auto multiplicity = static_cast(vertex.getNContributors()); + ++totalFound; + + if (!isTrueVertexLabel(label)) { + ++fakeFound; + fillMultiplicityHistogram(hRecoMultiplicityFake, multiplicity); + continue; + } + + ++trueFound; + const auto eventID = label.getEventID(); + uniqueTrueLabelsReco.insert(eventID); + fillMultiplicityHistogram(hRecoMultiplicityTrue, multiplicity); + + const auto truthIt = findableTruths.find(eventID); + if (truthIt == findableTruths.end()) { + continue; + } + + uniqueFindableTruthFound.insert(eventID); + const auto purity = (hasPurityBranch && purities != nullptr) ? (*purities)[iVtx] : -1.f; + const auto bestIt = bestRecoByTruth.find(eventID); + if (bestIt == bestRecoByTruth.end() || isBetterReco(vertex, purity, bestIt->second.vertex, bestIt->second.purity)) { + bestRecoByTruth[eventID] = BestRecoInfo{vertex, purity}; + } + } + } + + tracFileHandle->Close(); + delete tracFileHandle; + + findable += findableTruths.size(); + uniqueTrueReco += uniqueTrueLabelsReco.size(); + uniqueFindableFound += uniqueFindableTruthFound.size(); + + for (const auto eventID : uniqueFindableTruthFound) { + const auto truthIt = findableTruths.find(eventID); + if (truthIt != findableTruths.end()) { + fillMultiplicityHistogram(hTruthMultiplicityFound, truthIt->second.multiplicity); + } + } + + for (const auto& [eventID, reco] : bestRecoByTruth) { + const auto truthIt = findableTruths.find(eventID); + if (truthIt == findableTruths.end()) { + continue; + } + const auto dx = reco.vertex.getX() - truthIt->second.x; + const auto dy = reco.vertex.getY() - truthIt->second.y; + const auto dz = reco.vertex.getZ() - truthIt->second.z; + hDx->Fill(dx); + hDy->Fill(dy); + hDz->Fill(dz); + if (reco.vertex.getSigmaX() > 0.f) { + const auto pullX = dx / reco.vertex.getSigmaX(); + hPullX->Fill(pullX); + hPullXByMult[getMultiplicityCategory(reco.vertex.getNContributors()) - 1]->Fill(pullX); + sumSigmaX += reco.vertex.getSigmaX(); + ++sigmaXCount; + } + if (reco.vertex.getSigmaY() > 0.f) { + const auto pullY = dy / reco.vertex.getSigmaY(); + hPullY->Fill(pullY); + hPullYByMult[getMultiplicityCategory(reco.vertex.getNContributors()) - 1]->Fill(pullY); + sumSigmaY += reco.vertex.getSigmaY(); + ++sigmaYCount; + } + if (reco.vertex.getSigmaZ() > 0.f) { + const auto pullZ = dz / reco.vertex.getSigmaZ(); + hPullZ->Fill(pullZ); + hPullZByMult[getMultiplicityCategory(reco.vertex.getNContributors()) - 1]->Fill(pullZ); + sumSigmaZ += reco.vertex.getSigmaZ(); + ++sigmaZCount; + } + } + fs::current_path(cwd); + } + + auto* hTruthMultiplicityEfficiency = static_cast(hTruthMultiplicityFound->Clone("hTruthMultiplicityEfficiency")); + hTruthMultiplicityEfficiency->SetTitle("Unique efficiency vs truth multiplicity;truth multiplicity bin;efficiency"); + hTruthMultiplicityEfficiency->Divide(hTruthMultiplicityFound, hTruthMultiplicityFindable, 1., 1., "B"); + setMultiplicityBinLabels(hTruthMultiplicityEfficiency); + hTruthMultiplicityEfficiency->SetMinimum(0.); + hTruthMultiplicityEfficiency->SetMaximum(1.05); + + auto* hRecoMultiplicityTotal = static_cast(hRecoMultiplicityTrue->Clone("hRecoMultiplicityTotal")); + hRecoMultiplicityTotal->SetTitle("All reconstructed vertices;reco multiplicity bin;vertices"); + hRecoMultiplicityTotal->Add(hRecoMultiplicityFake); + setMultiplicityBinLabels(hRecoMultiplicityTotal); + + auto* hRecoMultiplicityPurity = static_cast(hRecoMultiplicityTrue->Clone("hRecoMultiplicityPurity")); + hRecoMultiplicityPurity->SetTitle("Purity vs reconstructed multiplicity;reco multiplicity bin;purity"); + hRecoMultiplicityPurity->Divide(hRecoMultiplicityTrue, hRecoMultiplicityTotal, 1., 1., "B"); + setMultiplicityBinLabels(hRecoMultiplicityPurity); + hRecoMultiplicityPurity->SetMinimum(0.); + hRecoMultiplicityPurity->SetMaximum(1.05); + + const auto duplicates = trueFound >= uniqueTrueReco ? (trueFound - uniqueTrueReco) : 0UL; + + const double uniqueEfficiency = findable > 0 ? static_cast(uniqueFindableFound) / findable : 0.; + const double purity = totalFound > 0 ? static_cast(trueFound) / totalFound : 0.; + const double fakeRate = totalFound > 0 ? static_cast(fakeFound) / totalFound : 0.; + const double duplicateRate = trueFound > 0 ? static_cast(duplicates) / trueFound : 0.; + const double f1 = (uniqueEfficiency + purity) > 0. ? 2. * uniqueEfficiency * purity / (uniqueEfficiency + purity) : 0.; + + const auto dxFit = fitGaussianCore(hDx, "fitDx"); + const auto dyFit = fitGaussianCore(hDy, "fitDy"); + const auto dzFit = fitGaussianCore(hDz, "fitDz"); + const auto pullXFit = fitGaussianCore(hPullX, "fitPullX"); + const auto pullYFit = fitGaussianCore(hPullY, "fitPullY"); + const auto pullZFit = fitGaussianCore(hPullZ, "fitPullZ"); + std::array pullXByMultFit{}; + std::array pullYByMultFit{}; + std::array pullZByMultFit{}; + for (int i = 0; i < NMultiplicityBins; ++i) { + const auto fitX = std::string("fitPullX_") + std::to_string(i + 1); + const auto fitY = std::string("fitPullY_") + std::to_string(i + 1); + const auto fitZ = std::string("fitPullZ_") + std::to_string(i + 1); + pullXByMultFit[i] = fitGaussianCore(hPullXByMult[i], fitX.c_str()); + pullYByMultFit[i] = fitGaussianCore(hPullYByMult[i], fitY.c_str()); + pullZByMultFit[i] = fitGaussianCore(hPullZByMult[i], fitZ.c_str()); + } + + std::printf("\nVertex validation summary\n"); + std::printf(" findable truth vertices : %zu\n", findable); + std::printf(" total reconstructed vertices : %zu\n", totalFound); + std::printf(" true reconstructed vertices : %zu\n", trueFound); + std::printf(" fake reconstructed vertices : %zu\n", fakeFound); + std::printf(" unique true labels (all) : %zu\n", uniqueTrueReco); + std::printf(" unique findable truth found : %zu\n", uniqueFindableFound); + std::printf(" unique efficiency : %.5f\n", uniqueEfficiency); + std::printf(" purity : %.5f\n", purity); + std::printf(" fake rate : %.5f\n", fakeRate); + std::printf(" duplicate rate : %.5f\n", duplicateRate); + std::printf(" F1(purity,efficiency) : %.5f\n", f1); + std::printf(" mean reported sigma x/y/z : %.6g / %.6g / %.6g cm\n", + sigmaXCount > 0 ? sumSigmaX / sigmaXCount : 0., + sigmaYCount > 0 ? sumSigmaY / sigmaYCount : 0., + sigmaZCount > 0 ? sumSigmaZ / sigmaZCount : 0.); + + if (dxFit.valid) { + std::printf(" x residual Gaussian: mean=%.6g cm sigma=%.6g cm\n", dxFit.mean, dxFit.sigma); + } + if (dyFit.valid) { + std::printf(" y residual Gaussian: mean=%.6g cm sigma=%.6g cm\n", dyFit.mean, dyFit.sigma); + } + if (dzFit.valid) { + std::printf(" z residual Gaussian: mean=%.6g cm sigma=%.6g cm\n", dzFit.mean, dzFit.sigma); + } + if (pullXFit.valid) { + std::printf(" x pull Gaussian : mean=%.6g sigma=%.6g\n", pullXFit.mean, pullXFit.sigma); + } + if (pullYFit.valid) { + std::printf(" y pull Gaussian : mean=%.6g sigma=%.6g\n", pullYFit.mean, pullYFit.sigma); + } + if (pullZFit.valid) { + std::printf(" z pull Gaussian : mean=%.6g sigma=%.6g\n", pullZFit.mean, pullZFit.sigma); + } + printGaussianByMultiplicity(pullXByMultFit, "x pull Gaussian by reconstructed multiplicity"); + printGaussianByMultiplicity(pullYByMultFit, "y pull Gaussian by reconstructed multiplicity"); + printGaussianByMultiplicity(pullZByMultFit, "z pull Gaussian by reconstructed multiplicity"); + + printBinnedFractions(hTruthMultiplicityFound, hTruthMultiplicityFindable, "Efficiency vs truth multiplicity"); + printBinnedFractions(hRecoMultiplicityTrue, hRecoMultiplicityTotal, "Purity vs reconstructed multiplicity"); + + auto* cValidation = new TCanvas("cVertexValidation", "Vertex validation summary", 1800, 1000); + cValidation->Divide(3, 2); + + cValidation->cd(1); + gPad->SetMargin(0.05, 0.05, 0.05, 0.05); + auto* summary = new TPaveText(0.02, 0.02, 0.98, 0.98, "NDC"); + summary->SetBorderSize(0); + summary->SetFillColor(0); + summary->SetTextAlign(12); + summary->SetTextFont(42); + char line[256]; + std::snprintf(line, sizeof(line), "Findable truth vertices : %zu", findable); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Total reconstructed : %zu", totalFound); + summary->AddText(line); + std::snprintf(line, sizeof(line), "True reconstructed : %zu", trueFound); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Fake reconstructed : %zu", fakeFound); + summary->AddText(line); + summary->AddText(""); + std::snprintf(line, sizeof(line), "Unique truth found : %zu", uniqueFindableFound); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Unique efficiency : %.5f", uniqueEfficiency); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Purity : %.5f", purity); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Fake rate : %.5f", fakeRate); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Duplicate rate : %.5f", duplicateRate); + summary->AddText(line); + std::snprintf(line, sizeof(line), "F1 : %.5f", f1); + summary->AddText(line); + std::snprintf(line, sizeof(line), "mean sigma x/y/z cm : %.3g / %.3g / %.3g", + sigmaXCount > 0 ? sumSigmaX / sigmaXCount : 0., + sigmaYCount > 0 ? sumSigmaY / sigmaYCount : 0., + sigmaZCount > 0 ? sumSigmaZ / sigmaZCount : 0.); + summary->AddText(line); + summary->AddText(""); + if (dxFit.valid) { + std::snprintf(line, sizeof(line), "dx fit mean/sigma cm : %.3g / %.3g", dxFit.mean, dxFit.sigma); + summary->AddText(line); + } + if (dyFit.valid) { + std::snprintf(line, sizeof(line), "dy fit mean/sigma cm : %.3g / %.3g", dyFit.mean, dyFit.sigma); + summary->AddText(line); + } + if (dzFit.valid) { + std::snprintf(line, sizeof(line), "dz fit mean/sigma cm : %.3g / %.3g", dzFit.mean, dzFit.sigma); + summary->AddText(line); + } + if (pullXFit.valid) { + std::snprintf(line, sizeof(line), "pull x sigma : %.3g", pullXFit.sigma); + summary->AddText(line); + } + if (pullYFit.valid) { + std::snprintf(line, sizeof(line), "pull y sigma : %.3g", pullYFit.sigma); + summary->AddText(line); + } + if (pullZFit.valid) { + std::snprintf(line, sizeof(line), "pull z sigma : %.3g", pullZFit.sigma); + summary->AddText(line); + } + summary->Draw(); + + cValidation->cd(2); + gPad->SetGridy(); + hTruthMultiplicityEfficiency->Draw("hist e1"); + + cValidation->cd(3); + gPad->SetGridy(); + const auto maxReco = std::max(hRecoMultiplicityTrue->GetMaximum(), hRecoMultiplicityFake->GetMaximum()); + hRecoMultiplicityTrue->SetMaximum(1.2 * std::max(1., maxReco)); + hRecoMultiplicityTrue->Draw("hist e1"); + hRecoMultiplicityFake->Draw("hist e1 same"); + auto hRecoMultiplicitySum = (TH1D*)hRecoMultiplicityTrue->Clone("hRecoMultiplicitySum"); + hRecoMultiplicitySum->Add(hRecoMultiplicityFake); + setHistogramStyle(hRecoMultiplicitySum, kBlack, 23); + hRecoMultiplicitySum->Draw("hist e1 same"); + { + auto* legend = new TLegend(0.58, 0.75, 0.88, 0.88); + legend->SetBorderSize(0); + legend->AddEntry(hRecoMultiplicityTrue, "true", "lep"); + legend->AddEntry(hRecoMultiplicityFake, "fake", "lep"); + legend->AddEntry(hRecoMultiplicitySum, "sum", "lep"); + legend->Draw(); + } + + cValidation->cd(4); + gPad->SetGridy(); + hRecoMultiplicityPurity->Draw("hist e1"); + + cValidation->cd(5); + gPad->SetGridy(); + auto* hDxNorm = makeNormalizedCopy(hDx, "hDxNorm", "Matched vertex residuals;residual (cm);normalized entries"); + auto* hDyNorm = makeNormalizedCopy(hDy, "hDyNorm", "Matched vertex residuals;residual (cm);normalized entries"); + auto* hDzNorm = makeNormalizedCopy(hDz, "hDzNorm", "Matched vertex residuals;residual (cm);normalized entries"); + const auto maxResidual = std::max({hDxNorm->GetMaximum(), hDyNorm->GetMaximum(), hDzNorm->GetMaximum()}); + hDzNorm->SetMaximum(1.2 * std::max(1., maxResidual)); + hDzNorm->Draw("hist"); + hDxNorm->Draw("hist same"); + hDyNorm->Draw("hist same"); + { + auto* legend = new TLegend(0.62, 0.72, 0.88, 0.88); + legend->SetBorderSize(0); + legend->AddEntry(hDxNorm, "dx", "l"); + legend->AddEntry(hDyNorm, "dy", "l"); + legend->AddEntry(hDzNorm, "dz", "l"); + legend->Draw(); + } + + cValidation->cd(6); + gPad->SetGridy(); + auto* hPullXNorm = makeNormalizedCopy(hPullX, "hPullXNorm", "Matched vertex pulls;pull;normalized entries"); + auto* hPullYNorm = makeNormalizedCopy(hPullY, "hPullYNorm", "Matched vertex pulls;pull;normalized entries"); + auto* hPullZNorm = makeNormalizedCopy(hPullZ, "hPullZNorm", "Matched vertex pulls;pull;normalized entries"); + const auto maxPull = std::max({hPullXNorm->GetMaximum(), hPullYNorm->GetMaximum(), hPullZNorm->GetMaximum()}); + hPullZNorm->SetMaximum(1.2 * std::max(1., maxPull)); + hPullZNorm->Draw("hist"); + hPullXNorm->Draw("hist same"); + hPullYNorm->Draw("hist same"); + { + auto* legend = new TLegend(0.62, 0.72, 0.88, 0.88); + legend->SetBorderSize(0); + legend->AddEntry(hPullXNorm, "pull x", "l"); + legend->AddEntry(hPullYNorm, "pull y", "l"); + legend->AddEntry(hPullZNorm, "pull z", "l"); + legend->Draw(); + } + + cValidation->cd(); + cValidation->Update(); + cValidation->SaveAs("checkSeeding.pdf"); +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C b/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C index df26dabf38895..4e4dfcc41b928 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -32,18 +33,39 @@ #include #endif -void getClusterPatterns(std::vector& pattVec, std::vector* ITSclus, std::vector* ITSpatt, o2::itsmft::TopologyDictionary& mdict); +static bool invPal = false; +void getClusterPatterns(std::vector& pattVec, + std::vector* ITSclus, + std::vector* ITSpatt, + o2::itsmft::TopologyDictionary& mdict); -void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsigned int nRofs = 3, const string fname = "o2clus_its.root") +void drawClustersInChipInRof(const uint chipId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame, + TH2D* hHitMapSuperimposed = nullptr); + +void drawClustersInStaveInRof(const int staveId, + const int layerId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame); + +void CheckSquasher1(const uint chipId = 0, const uint startingROF = 0, const uint nRofs = 3, bool showSuperimposed = false, const string fname = "o2clus_its.root") { - TColor::InvertPalette(); + if (!invPal) { + TColor::InvertPalette(); + invPal = true; + } + gStyle->SetOptStat(0); // Geometry - o2::base::GeometryManager::loadGeometry(""); - auto gman = o2::its::GeometryTGeo::Instance(); + auto& cc = o2::ccdb::BasicCCDBManager::instance(); + cc.setTimestamp(o2::ccdb::getCurrentTimestamp()); + auto* gman = cc.get("ITS/Config/Geometry"); gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + // Topology dictionary - auto& cc = o2::ccdb::BasicCCDBManager::instance(); auto mdict = cc.get("ITS/Calib/ClusterDictionary"); auto fITSclus = TFile::Open(fname.data(), "r"); auto treeITSclus = (TTree*)fITSclus->Get("o2sim"); @@ -60,6 +82,8 @@ void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsi auto clSpan = gsl::span(ITSclus->data(), ITSclus->size()); std::vector hHitMapsVsFrame(nRofs); + TH2D* hHitMapSuperimposed = nullptr; + hHitMapSuperimposed = new TH2D(Form("chip%i_superimposed", chipId), Form("chip %i superimposed; ; ; Counts", chipId), 1024, -0.5, 1023.5, 512, -0.5, 511.5); treeITSclus->GetEvent(0); LOGP(info, "there are {} rofs in this TF", ITSrof->size()); @@ -69,62 +93,17 @@ void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsi getClusterPatterns(pattVec, ITSclus, ITSpatt, *mdict); for (unsigned int iR{0}; iR < nRofs; iR++) { - LOGP(info, "Processing rof {}", iR + startingROF); + LOGP(info, "===============\n \tProcessing rof {} \n\t===============", iR + startingROF); hHitMapsVsFrame[iR] = new TH2D(Form("chip%i_rof%i", chipId, startingROF + iR), Form("chip %i rof %i; ; ; Counts", chipId, startingROF + iR), 1024, -0.5, 1023.5, 512, -0.5, 511.5); // work on data const auto& rof = (*ITSrof)[startingROF + iR]; auto clustersInFrame = rof.getROFData(*ITSclus); auto patternsInFrame = rof.getROFData(pattVec); - // auto pattIt = ITSpatt->cbegin(); - - for (unsigned int clusInd{0}; clusInd < clustersInFrame.size(); clusInd++) { - const auto& clus = clustersInFrame[clusInd]; - auto sID = clus.getSensorID(); - - if (sID == chipId) { - clus.print(); - - // auto labels = clusLabArr->getLabels(clusInd); - // extract pattern info - auto col = clus.getCol(); - auto row = clus.getRow(); - - std::cout << patternsInFrame[clusInd]; - - std::cout << std::endl; - int ic = 0, ir = 0; - - auto colSpan = patternsInFrame[clusInd].getColumnSpan(); - auto rowSpan = patternsInFrame[clusInd].getRowSpan(); - auto nBits = rowSpan * colSpan; - - for (int i = 2; i < patternsInFrame[clusInd].getUsedBytes() + 2; i++) { - unsigned char tempChar = patternsInFrame[clusInd].getByte(i); - int s = 128; // 0b10000000 - while (s > 0) { - if ((tempChar & s) != 0) // checking active pixels - { - hHitMapsVsFrame[iR]->Fill(col + ic, row + ir); - } - ic++; - s >>= 1; - if ((ir + 1) * ic == nBits) { - break; - } - if (ic == colSpan) { - ic = 0; - ir++; - } - if ((ir + 1) * ic == nBits) { - break; - } - } - } - } - } + drawClustersInChipInRof(chipId, clustersInFrame, patternsInFrame, hHitMapsVsFrame[iR], hHitMapSuperimposed); } auto canvas = new TCanvas(Form("chip%d", chipId), Form("chip%d", chipId), nRofs * 1000, 600); + auto canvasSuperimposition = new TCanvas(Form("chip%d_superimposed", chipId), Form("chip%d_superimposed", chipId), 600, 600); canvas->Divide(nRofs, 1); for (unsigned int i{0}; i < nRofs; ++i) { @@ -133,6 +112,84 @@ void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsi gPad->SetGridy(); hHitMapsVsFrame[i]->Draw("colz"); } + canvasSuperimposition->cd(); + gPad->SetGridx(); + gPad->SetGridy(); + if (showSuperimposed) { + hHitMapSuperimposed->Draw("colz"); + } else { + delete canvasSuperimposition; + } +} + +void CheckSquasher(const int staveId, const uint layerId, const uint startingROF = 0, const uint nRofs = 3, const string fname = "o2clus_its.root") +{ + std::array staves{12, 14, 16}; + if (!invPal) { + TColor::InvertPalette(); + invPal = true; + } + + gStyle->SetOptStat(0); + // Geometry + auto& cc = o2::ccdb::BasicCCDBManager::instance(); + cc.setTimestamp(o2::ccdb::getCurrentTimestamp()); + auto* gman = cc.get("ITS/Config/Geometry"); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + + // Topology dictionary + auto mdict = cc.get("ITS/Calib/ClusterDictionary"); + auto fITSclus = TFile::Open(fname.data(), "r"); + auto treeITSclus = (TTree*)fITSclus->Get("o2sim"); + + std::vector* ITSclus = nullptr; + std::vector* ITSrof = nullptr; + std::vector* ITSpatt = nullptr; + o2::dataformats::MCTruthContainer* clusLabArr = nullptr; + + treeITSclus->SetBranchAddress("ITSClusterComp", &ITSclus); + treeITSclus->SetBranchAddress("ITSClustersROF", &ITSrof); + treeITSclus->SetBranchAddress("ITSClusterPatt", &ITSpatt); + + auto clSpan = gsl::span(ITSclus->data(), ITSclus->size()); + std::vector hHitMapsVsFrame(nRofs); + TH2D* hHitMapSuperimposed = nullptr; + std::vector hHitMapStaves; + hHitMapStaves.resize(staveId < 0 ? staves[layerId] : 1); + for (int iStave{0}; iStave < (staveId < 0 ? staves[layerId] : 1); ++iStave) { + hHitMapStaves[staveId < 0 ? iStave : 0] = new TH2D(Form("stave_%d", staveId < 0 ? iStave : staveId), "", 1024 * 9, -0.5, 1023.5 * 9, 512, -0.5, 511.5); + } + + treeITSclus->GetEvent(0); + LOGP(info, "there are {} rofs in this TF", ITSrof->size()); + + // Get patterns + std::vector pattVec; + getClusterPatterns(pattVec, ITSclus, ITSpatt, *mdict); + + for (unsigned int iR{0}; iR < nRofs; iR++) { + LOGP(info, " ===============\n \tProcessing rof {} \n\t===============", iR + startingROF); + // work on data + const auto& rof = (*ITSrof)[startingROF + iR]; + auto clustersInFrame = rof.getROFData(*ITSclus); + auto patternsInFrame = rof.getROFData(pattVec); + for (int iStave{0}; iStave < (staveId < 0 ? staves[layerId] : 1); ++iStave) { + drawClustersInStaveInRof(staveId < 0 ? iStave : staveId, layerId, clustersInFrame, patternsInFrame, hHitMapStaves[staveId < 0 ? iStave : 0]); + } + } + + auto canvas = new TCanvas(Form("stave%d", staveId), Form("stave%d", staveId), 9 * 1000, (staveId < 0 ? staves[layerId] : 1) * 1000); + if (staveId < 0) { + canvas->Divide(1, staves[layerId]); + } + for (int iStave{0}; iStave < (staveId < 0 ? staves[layerId] : 1); ++iStave) { + if (staveId < 0) { + canvas->cd(iStave + 1); + } + gPad->SetGridx(); + gPad->SetGridy(); + hHitMapStaves[staveId < 0 ? iStave : 0]->Draw("colz"); + } } void getClusterPatterns(std::vector& pattVec, std::vector* ITSclus, std::vector* ITSpatt, o2::itsmft::TopologyDictionary& mdict) @@ -159,3 +216,113 @@ void getClusterPatterns(std::vector& pattVec, std::v pattVec.push_back(patt); } } + +void drawClustersInChipInRof(const uint chipId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame, + TH2D* hHitMapSuperimposed) +{ + for (unsigned int clusInd{0}; clusInd < clustersInFrame.size(); clusInd++) { + const auto& clus = clustersInFrame[clusInd]; + auto sID = clus.getSensorID(); + + if (sID == chipId) { + LOGP(info, "Processing cluster {}", clusInd); + clus.print(); + + // extract pattern info + auto col = clus.getCol(); + auto row = clus.getRow(); + + std::cout << patternsInFrame[clusInd]; + + std::cout << std::endl; + int ic = 0, ir = 0; + + auto colSpan = patternsInFrame[clusInd].getColumnSpan(); + auto rowSpan = patternsInFrame[clusInd].getRowSpan(); + auto nBits = rowSpan * colSpan; + + for (int i = 2; i < patternsInFrame[clusInd].getUsedBytes() + 2; i++) { + unsigned char tempChar = patternsInFrame[clusInd].getByte(i); + int s = 128; // 0b10000000 + while (s > 0) { + if ((tempChar & s) != 0) // checking active pixels + { + hHitMapsVsFrame->Fill(col + ic, row + ir); + hHitMapSuperimposed == nullptr ?: hHitMapSuperimposed->Fill(col + ic, row + ir); + } + ic++; + s >>= 1; + if ((ir + 1) * ic == nBits) { + break; + } + if (ic == colSpan) { + ic = 0; + ir++; + } + if ((ir + 1) * ic == nBits) { + break; + } + } + } + } + } +} + +void drawClustersInStaveInRof(const int staveId, + const int layerId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame) +{ + o2::itsmft::ChipMappingITS chipMapping; + int lay, sta, ssta, mod, chipInMod; + std::array sensorIDs; // sIDs in the staves of the IB. + for (unsigned int clusInd{0}; clusInd < clustersInFrame.size(); clusInd++) { + const auto& clus = clustersInFrame[clusInd]; + auto sID = clus.getSensorID(); + + chipMapping.expandChipInfoHW(sID, lay, sta, ssta, mod, chipInMod); + + if (sta == staveId && lay == layerId) { + // extract pattern info + auto col = clus.getCol(); + auto row = clus.getRow(); + + int ic = 0, ir = 0; + + auto colSpan = patternsInFrame[clusInd].getColumnSpan(); + auto rowSpan = patternsInFrame[clusInd].getRowSpan(); + auto nBits = rowSpan * colSpan; + + for (int i = 2; i < patternsInFrame[clusInd].getUsedBytes() + 2; i++) { + unsigned char tempChar = patternsInFrame[clusInd].getByte(i); + int s = 128; // 0b10000000 + while (s > 0) { + if ((tempChar & s) != 0) // checking active pixels + { + // Normalize the chip ID to be within the range [0, 8] + int normalizedChipID = sID % 9; // Hardcode for the ITS IB + double x = col + ic + normalizedChipID * 1024; + double y = row + ir; + hHitMapsVsFrame->Fill(x, y); + } + ic++; + s >>= 1; + if ((ir + 1) * ic == nBits) { + break; + } + if (ic == colSpan) { + ic = 0; + ir++; + } + if ((ir + 1) * ic == nBits) { + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C b/Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C new file mode 100644 index 0000000000000..11ebcbbb1b1f1 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C @@ -0,0 +1,527 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DetectorsBase/Propagator.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "CommonConstants/LHCConstants.h" +#include "DataFormatsParameters/GRPMagField.h" +#include "DataFormatsParameters/GRPLHCIFData.h" +#include "ReconstructionDataFormats/Vertex.h" +#include "DetectorsVertexing/SVertexHypothesis.h" +#include "DCAFitter/DCAFitterN.h" +#endif + +constexpr const char* tracFile = "o2trac_its.root"; +constexpr const char* clsFile = "o2clus_its.root"; + +struct PairCandidate { + int posIdx; + int negIdx; + double dca; + double mass; +}; + +std::vector findDirs(const std::string&); + +void CheckStaggering(int runNumber, int max = -1, const std::string& dir = "") +{ + gStyle->SetOptStat(0); + auto dirs = findDirs(dir); + printf("Will iterate over %zu input dirs", dirs.size()); + if (dirs.empty()) { + printf("No input found"); + return; + } + if (max > 0 && (int)dirs.size() > max) { + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(dirs.begin(), dirs.end(), g); + dirs.resize(max); + printf("restricting to %ddirs", max); + } + + auto& ccdbmgr = o2::ccdb::BasicCCDBManager::instance(); + ccdbmgr.setURL("https://alice-ccdb.cern.ch"); + auto runDuration = ccdbmgr.getRunDuration(runNumber); + auto tRun = runDuration.first + ((runDuration.second - runDuration.first) / 2); // time stamp for the middle of the run duration + ccdbmgr.setTimestamp(tRun); + printf("Run %d has TS %ld", runNumber, tRun); + auto geoAligned = ccdbmgr.get("GLO/Config/GeometryAligned"); + auto magField = ccdbmgr.get("GLO/Config/GRPMagField"); + auto grpLHC = ccdbmgr.get("GLO/Config/GRPLHCIF"); + auto bcFill = grpLHC->getBunchFilling(); + bcFill.print(-1); + const o2::base::MatLayerCylSet* matLut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdbmgr.get("GLO/Param/MatLUT")); + o2::base::Propagator::initFieldFromGRP(magField); + auto prop = o2::base::Propagator::Instance(); + prop->setMatLUT(matLut); + const float bz = prop->getNominalBz(); + + auto hNTrkCls = new TH1D("hNTrkCls", "Number of cluster per track;nCls;entries", 4, 3.5, 7.5); + std::array hTrkTS{nullptr}; + for (int i{0}; i < 5; ++i) { + hTrkTS[i] = new TH1D(Form("hTrkTS_%d", i), Form("track time t0 (%s);t0 (BC)", i == 0 ? "all" : Form("NCls=%d", 3 + i)), o2::constants::lhc::LHCMaxBunches, 0, o2::constants::lhc::LHCMaxBunches); + } + auto hTrkTSE = new TH1D("hTrkTSE", "assigned time errors; tE (BC)", 198, -0.5, 198 - 0.5); + + // K0 && Phi-Meson + const float mMinITSPt{0.15}; + // + const int phiMinNCls{7}; + const float phiMaxDCAR2MVTX{0.05}; // max distance to mean vtx + auto hPhiMeson = new TH1D("hPhiMeson", "#phi meson;mass (GeV/c^{2})", 200, 0.96, 1.1); + auto hPhiMesonBkg = new TH1D("hPhiMesonBkg", "#phi meson background;mass (GeV/c^{2})", 200, 0.96, 1.1); + + const int mK0MinNCls{7}; + const float mK0minCosPAXYMeanVertex = 0.98; + const float mK0MaxDCAXY2ToMeanVertex = 0.2; + const float mK0MaxTgl2V0 = 1; + const float mK0MinPt2V0 = 0.3; + const float mK0MinCosPA = 0.999; + o2::vertexing::DCAFitterN<2> k0Ft; + k0Ft.setBz(bz); + k0Ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + k0Ft.setMaxR(30); + k0Ft.setMaxDZIni(0.1); + k0Ft.setMaxDXYIni(0.1); + k0Ft.setMinParamChange(1e-3); + k0Ft.setMinRelChi2Change(0.9); + k0Ft.setMaxChi2(5); + k0Ft.setUseAbsDCA(true); + auto hK0 = new TH1D("hK0", "K0;mass (GeV/c^{2})", 100, 0.4, 0.6); + o2::vertexing::SVertexHypothesis k0Hyp; + const float k0Par[] = {0., 20, 0., 5.0, 0.0, 1.09004e-03, 2.62291e-04, 8.93179e-03, 2.83121}; + k0Hyp.set(o2::track::PID::K0, o2::track::PID::Pion, o2::track::PID::Pion, k0Par, bz); + + auto hVtxXY = new TH2F("hVtxXY", "seeding vertices XY", 200, -0.3, 0.3, 200, -0.3, 0.3); + auto hVtxZ = new TH1F("hVtxZ", "seeding vertices Z", 200, -16, 16); + auto hVtxNCont = new TH1F("hVtxNCont", "seeding vertices contributors", 100, 0, 100); + auto hVtxZNCont = new TProfile("hVtxZNCont", "seeding vertices z-contributors", 200, -16, 16); + auto hVtxCls = new TProfile("hVtxCls", ";Cls/TF;Cls/Vtx", 2000, 600000, 900000); + auto hVtxTS = new TH1D("hVtxTS", "vtx time t0;t0 (BC)", o2::constants::lhc::LHCMaxBunches, 0, o2::constants::lhc::LHCMaxBunches); + + const float minVtxWeight{5}; + float meanVtxWeight{0}; + o2::dataformats::VertexBase meanVtx; + auto accountVtx = [&](o2::its::Vertex const& vtx) { + const float w = vtx.getNContributors(); + if (w >= minVtxWeight) { + meanVtx.setX((meanVtx.getX() * meanVtxWeight + vtx.getX() * w) / (meanVtxWeight + w)); + meanVtx.setY((meanVtx.getY() * meanVtxWeight + vtx.getY() * w) / (meanVtxWeight + w)); + meanVtxWeight += w; + } + }; + + std::vector trkArr, *trkArrPtr{&trkArr}; + std::vector vtxArr, *vtxArrPtr{&vtxArr}; + std::array*, 7> clsArr{nullptr}; + for (size_t iDir{0}; iDir < dirs.size(); ++iDir) { + int progress = static_cast((iDir + 1) * 100 / dirs.size()); + printf("\rProgress: ["); + int barWidth = 50; + int pos = barWidth * progress / 100; + for (int j = 0; j < barWidth; ++j) { + if (j < pos) { + printf("="); + } else if (j == pos) { + printf(">"); + } else { + printf(" "); + } + } + printf("] %d%%", progress); + fflush(stdout); + + const auto& d = dirs[iDir]; + auto fTrks = TFile::Open((d / tracFile).c_str()); + auto fCls = TFile::Open((d / clsFile).c_str()); + if (!fTrks || !fCls || fTrks->IsZombie() || fCls->IsZombie()) { + continue; + } + auto tTrks = fTrks->Get("o2sim"); + auto tCls = fCls->Get("o2sim"); + if (!tTrks || !tCls) { + continue; + } + + tTrks->SetBranchAddress("ITSTrack", &trkArrPtr); + tTrks->SetBranchAddress("Vertices", &vtxArrPtr); + if (tCls->GetBranchStatus("ITSClusterComp")) { + tCls->SetBranchAddress("ITSClusterComp", &clsArr[0]); + } else { + for (int i{0}; i < 7; ++i) { + tCls->SetBranchAddress(Form("ITSClusterComp_%d", i), &clsArr[i]); + } + } + + for (int iTF{0}; tTrks->LoadTree(iTF) >= 0; ++iTF) { + tTrks->GetEntry(iTF); + tCls->GetEntry(iTF); + + size_t ncls = 0; + for (int i{0}; i < 7; ++i) { + if (clsArr[i]) { + ncls += clsArr[i]->size(); + } + } + + // for each TF built pool of positive and negaitve tracks + std::vector posPool, negPool; + + for (const auto& trk : trkArr) { + hNTrkCls->Fill(trk.getNClusters()); + hTrkTS[0]->Fill(std::fmod(trk.getTimeStamp().getTimeStamp(), o2::constants::lhc::LHCMaxBunches)); + hTrkTS[trk.getNClusters() - 3]->Fill(std::fmod(trk.getTimeStamp().getTimeStamp(), o2::constants::lhc::LHCMaxBunches)); + hTrkTSE->Fill(trk.getTimeStamp().getTimeStampError()); + + if (trk.getPt() > mMinITSPt) { + if (trk.getCharge() > 0) { + posPool.push_back(&trk); + } else { + negPool.push_back(&trk); + } + } + } + + for (const auto& vtx : vtxArr) { + hVtxXY->Fill(vtx.getX(), vtx.getY()); + hVtxZ->Fill(vtx.getZ()); + hVtxNCont->Fill(vtx.getNContributors()); + hVtxZNCont->Fill(vtx.getZ(), vtx.getNContributors()); + hVtxTS->Fill(vtx.getTimeStamp().getTimeStamp()); + accountVtx(vtx); + } + hVtxCls->Fill(ncls, (float)ncls / (float)vtxArr.size()); + + std::vector k0Cands; + for (int iPos{0}; iPos < (int)posPool.size(); ++iPos) { + const auto pos = posPool[iPos]; + for (int iNeg{0}; iNeg < (int)negPool.size(); ++iNeg) { + const auto neg = negPool[iNeg]; + bool overlap = std::abs(pos->getTimeStamp().getTimeStamp() - neg->getTimeStamp().getTimeStamp()) <= (pos->getTimeStamp().getTimeStampError() + neg->getTimeStamp().getTimeStampError()); + if (!overlap) { + continue; + } + + // phi-meson + if (pos->getNClusters() >= phiMinNCls && neg->getNClusters() >= phiMinNCls) { + o2::dataformats::DCA posDCA, negDCA; + o2::track::TrackParCov posPhi = *pos; + posPhi.setPID(o2::track::PID::Kaon); + o2::track::TrackParCov negPhi = *neg; + negPhi.setPID(o2::track::PID::Kaon); + if (prop->propagateToDCA(meanVtx, posPhi, bz, 2.0, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &posDCA) && prop->propagateToDCA(meanVtx, negPhi, bz, 2.0, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &negDCA)) { + if (posDCA.getR2() < phiMaxDCAR2MVTX && negDCA.getR2() < phiMaxDCAR2MVTX) { + std::array pP{}, pN{}; + posPhi.getPxPyPzGlo(pP); + negPhi.getPxPyPzGlo(pN); + TLorentzVector p1, p2; + p1.SetXYZM(pP[0], pP[1], pP[2], posPhi.getPID().getMass()); + p2.SetXYZM(pN[0], pN[1], pN[2], negPhi.getPID().getMass()); + TLorentzVector mother = p1 + p2; + hPhiMeson->Fill(mother.M()); + // rotate on daughter track to estimate background + for (int i{0}; i < 10; ++i) { + double theta = gRandom->Uniform(165.f, 195.f) * TMath::DegToRad(); + double pxN = pN[0] * cos(theta) - pN[1] * sin(theta); + double pyN = pN[0] * sin(theta) + pN[1] * cos(theta); + double pxP = pP[0] * cos(-theta) - pP[1] * sin(-theta); + double pyP = pP[0] * sin(-theta) + pP[1] * cos(-theta); + p1.SetXYZM(pxP, pyP, pP[2], posPhi.getPID().getMass()); + p2.SetXYZM(pxN, pyN, pN[2], negPhi.getPID().getMass()); + mother = p1 + p2; + hPhiMesonBkg->Fill(mother.M()); + } + } + } + } + // K0 + if (pos->getNClusters() >= mK0MinNCls && neg->getNClusters() >= mK0MinNCls) { + o2::track::TrackParCov posPion = *pos; + posPion.setPID(o2::track::PID::Pion); + o2::track::TrackParCov negPion = *neg; + negPion.setPID(o2::track::PID::Pion); + int ncand = k0Ft.process(posPion, negPion); + const int cand = 0; + if (ncand) { + const auto& v0XYZ = k0Ft.getPCACandidate(); + float dxv0 = v0XYZ[0] - meanVtx.getX(), dyv0 = v0XYZ[1] - meanVtx.getY(), r2v0 = dxv0 * dxv0 + dyv0 * dyv0; + if (!k0Ft.isPropagateTracksToVertexDone(cand) && !k0Ft.propagateTracksToVertex(cand)) { + continue; + } + const auto& trPProp = k0Ft.getTrack(0, cand); + const auto& trNProp = k0Ft.getTrack(1, cand); + std::array pP{}, pN{}; + trPProp.getPxPyPzGlo(pP); + trNProp.getPxPyPzGlo(pN); + // estimate DCA of neutral V0 track to beamline: straight line with parametric equation + // x = X0 + pV0[0]*t, y = Y0 + pV0[1]*t reaches DCA to beamline (Xv, Yv) at + // t = -[ (x0-Xv)*pV0[0] + (y0-Yv)*pV0[1]) ] / ( pT(pV0)^2 ) + // Similar equation for 3D distance involving pV0[2] + std::array pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + float pt2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1], prodXYv0 = dxv0 * pV0[0] + dyv0 * pV0[1], tDCAXY = prodXYv0 / pt2V0; + if (pt2V0 < mK0MinPt2V0) { // pt cut + continue; + } + if (pV0[2] * pV0[2] / pt2V0 > mK0MaxTgl2V0) { // tgLambda cut + continue; + } + float dcaX = dxv0 - pV0[0] * tDCAXY, dcaY = dyv0 - pV0[1] * tDCAXY, dca2 = dcaX * dcaX + dcaY * dcaY; + float cosPAXY = prodXYv0 / std::sqrt(r2v0 * pt2V0); + if (dca2 > mK0MaxDCAXY2ToMeanVertex || cosPAXY < mK0minCosPAXYMeanVertex) { + continue; + } + float p2V0 = pt2V0 + pV0[2] * pV0[2], ptV0 = std::sqrt(pt2V0); + float p2Pos = pP[0] * pP[0] + pP[1] * pP[1] + pP[2] * pP[2], p2Neg = pN[0] * pN[0] + pN[1] * pN[1] + pN[2] * pN[2]; + if (!k0Hyp.check(p2Pos, p2Neg, p2V0, ptV0)) { + continue; + } + + float bestCosPA = mK0MinCosPA; + bool candFound = false; + for (const auto& vtx : vtxArr) { + if (vtx.getNContributors() > minVtxWeight) { + const auto vtxT = vtx.getTimeStamp().makeSymmetrical(); + bool overlapPos = std::abs(pos->getTimeStamp().getTimeStamp() - vtxT.getTimeStamp()) <= (pos->getTimeStamp().getTimeStampError() + vtxT.getTimeStampError()); + bool overlapNeg = std::abs(neg->getTimeStamp().getTimeStamp() - vtxT.getTimeStamp()) <= (neg->getTimeStamp().getTimeStampError() + vtxT.getTimeStampError()); + if (overlapPos && overlapNeg) { + float dx = v0XYZ[0] - vtx.getX(), dy = v0XYZ[1] - vtx.getY(), dz = v0XYZ[2] - vtx.getZ(), prodXYZv0 = dx * pV0[0] + dy * pV0[1] + dz * pV0[2]; + float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0); + if (cosPA > bestCosPA) { + bestCosPA = cosPA; + candFound = true; + } + } + } + } + if (candFound) { + TLorentzVector p1, p2; + p1.SetXYZM(pP[0], pP[1], pP[2], posPion.getPID().getMass()); + p2.SetXYZM(pN[0], pN[1], pN[2], negPion.getPID().getMass()); + TLorentzVector mother = p1 + p2; + double mass = mother.M(); + k0Cands.emplace_back(iPos, iNeg, k0Ft.getChi2AtPCACandidate(cand), mass); + } + } + } + } + } + + // disambiguiate candidates by using the smallest DCA one + std::sort(k0Cands.begin(), k0Cands.end(), [](const auto& a, const auto& b) { return a.dca < b.dca; }); + std::vector posUsed(posPool.size(), false); + std::vector negUsed(negPool.size(), false); + for (const auto& c : k0Cands) { + if (!posUsed[c.posIdx] && !negUsed[c.negIdx]) { + posUsed[c.posIdx] = true; + negUsed[c.negIdx] = true; + hK0->Fill(c.mass); + } + } + } + + fTrks->Close(); + fCls->Close(); + } + + auto drawBCPattern = [&]() { + gPad->Update(); + // draw BC pattern + double ymin = gPad->GetUymin(); + double ymax = gPad->GetUymax(); + auto interactingBC = bcFill.getPattern(); + TLine* lastLine{nullptr}; + for (int iBC{0}; iBC < (int)interactingBC.size(); ++iBC) { + if (interactingBC.test(iBC)) { + TLine* line = new TLine(iBC, ymin, iBC, ymax); + line->SetLineColor(kRed); + line->SetLineWidth(1); + line->SetLineStyle(kDashed); + line->Draw(); + lastLine = line; + } + } + return lastLine; + }; + + { + TFile out(Form("check_%d.root", runNumber), "RECREATE"); + out.WriteTObject(hNTrkCls); + for (int i{0}; i < 5; ++i) { + out.WriteTObject(hTrkTS[i]); + } + out.WriteTObject(hTrkTSE); + out.WriteTObject(hPhiMeson); + out.WriteTObject(hPhiMesonBkg); + out.WriteTObject(hK0); + out.WriteTObject(hVtxXY); + out.WriteTObject(hVtxZ); + out.WriteTObject(hVtxNCont); + out.WriteTObject(hVtxZNCont); + out.WriteTObject(hVtxTS); + out.WriteTObject(hVtxCls); + } + + // fitK0(hK0, runNumber); + // fitPhiMeson(hPhiMeson, runNumber); + { + auto c = new TCanvas(); + hNTrkCls->Draw(); + c->Draw(); + c->SaveAs(Form("trk_%d.pdf", runNumber)); + } + { + auto c = new TCanvas(); + c->Divide(3, 2); + for (int i{0}; i < 5; ++i) { + c->cd(i + 1); + hTrkTS[i]->Draw(); + auto lastLine = drawBCPattern(); + if (i == 0) { + auto leg = new TLegend(0.6, 0.6, 0.9, 0.9); + leg->AddEntry(lastLine, "Interacting BCs", "l"); + leg->AddEntry(hTrkTS[i], "Track timestamp"); + leg->Draw(); + } + } + c->cd(6); + hTrkTSE->Draw(); + c->Draw(); + c->SaveAs(Form("time_%d.pdf", runNumber)); + } + { + auto c = new TCanvas(); + c->Divide(2, 1); + { + c->cd(1); + hPhiMeson->Draw(); + gPad->Update(); + double ymin = gPad->GetUymin(); + double ymax = gPad->GetUymax(); + const float mass = 1.019461; + TLine* line = new TLine(mass, ymin, mass, ymax); + line->SetLineColor(kRed); + line->SetLineWidth(1); + line->SetLineStyle(kDashed); + line->Draw(); + } + { + c->cd(2); + hK0->Draw(); + gPad->Update(); + double ymin = gPad->GetUymin(); + double ymax = gPad->GetUymax(); + const float mass = 0.497611; + TLine* line = new TLine(mass, ymin, mass, ymax); + line->SetLineColor(kRed); + line->SetLineWidth(1); + line->SetLineStyle(kDashed); + line->Draw(); + } + c->Draw(); + c->SaveAs(Form("mass_%d.pdf", runNumber)); + } + { + auto c = new TCanvas(); + c->Divide(3, 2); + { + c->cd(1); + hVtxXY->Draw("col"); + } + { + c->cd(2); + hVtxZ->Draw(); + } + { + c->cd(3); + hVtxNCont->Draw(); + } + { + c->cd(4); + hVtxZNCont->Draw(); + } + { + c->cd(5); + hVtxCls->Draw(); + } + { + c->cd(6); + hVtxTS->Draw(); + auto lastLine = drawBCPattern(); + auto leg = new TLegend(0.6, 0.6, 0.9, 0.9); + leg->AddEntry(lastLine, "Interacting BCs", "l"); + leg->AddEntry(hVtxTS, "Track timestamp"); + leg->Draw(); + } + c->Draw(); + c->SaveAs(Form("vertex_%d.pdf", runNumber)); + } +} + +std::vector findDirs(const std::string& roots) +{ + std::filesystem::path root; + if (roots.empty()) { + root = std::filesystem::current_path(); + } else { + root = roots; + } + namespace fs = std::filesystem; + std::vector result; + auto has_files = [](const fs::path& dir) { + auto s = dir / tracFile; + return fs::exists(dir / tracFile) && fs::exists(dir / clsFile) && + fs::is_regular_file(dir / tracFile) && fs::is_regular_file(dir / clsFile); + }; + if (fs::is_directory(root) && has_files(root)) { + result.push_back(root); + return result; + } + for (const auto& entry : fs::recursive_directory_iterator(root)) { + if (!entry.is_directory()) { + continue; + } + const fs::path dir = entry.path(); + if (has_files(dir)) { + result.push_back(dir); + } + } + return result; +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C b/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C index 79b2f52ad29df..11b03531d4af3 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C @@ -15,16 +15,16 @@ #include "TGeoGlobalMagField.h" #include "Field/MagneticField.h" -#include "DetectorsBase/Propagator.h" #include "ITSBase/GeometryTGeo.h" -#include "SimulationDataFormat/TrackReference.h" +#include "DataFormatsITS/TrackITS.h" #include "SimulationDataFormat/MCTrack.h" #include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/TrackReference.h" +#include "DetectorsBase/Propagator.h" #endif +#include "DataFormatsITSMFT/CompCluster.h" +#include "SimulationDataFormat/MCTruthContainer.h" using namespace std; diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C b/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C index f1a3d2af4e469..e185be83a389f 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C @@ -1,3 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + /// \file CheckTracksCA.C /// \brief Simple macro to check ITSU tracks @@ -10,24 +21,44 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include #include "ITSBase/GeometryTGeo.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "DetectorsBase/Propagator.h" #include "SimulationDataFormat/TrackReference.h" +#include "SimulationDataFormat/O2DatabasePDG.h" #include "SimulationDataFormat/MCTrack.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITS/TrackITS.h" #endif +#include "DataFormatsITSMFT/CompCluster.h" using namespace std; +// chi2 PDF with amplitude A, degrees of freedom k, scale s +Double_t chi2_pdf(Double_t* x, Double_t* par) +{ + const Double_t xx = x[0]; + const Double_t A = par[0]; + const Double_t k = par[1]; + const Double_t s = par[2]; + if (xx <= 0.0 || k <= 0.0 || s <= 0.0) + return 0.0; + const Double_t coef = 1.0 / (TMath::Power(2.0 * s, k * 0.5) * TMath::Gamma(k * 0.5)); + return A * coef * TMath::Power(xx, k * 0.5 - 1.0) * TMath::Exp(-xx / (2.0 * s)); +} + struct ParticleInfo { int event; int pdg; @@ -36,22 +67,39 @@ struct ParticleInfo { float phi; int mother; int first; + float pvx{}; + float pvy{}; + float pvz{}; + float dcaxy; + float dcaz; unsigned short clusters = 0u; unsigned char isReco = 0u; unsigned char isFake = 0u; bool isPrimary = 0u; unsigned char storedStatus = 2; /// not stored = 2, fake = 1, good = 0 o2::its::TrackITS track; + o2::MCTrack mcTrack; }; #pragma link C++ class ParticleInfo + ; -void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its.root", std::string clusfile = "o2clus_its.root", std::string kinefile = "o2sim_Kine.root") +void CheckTracksCA(bool doEffStud = true, + bool doFakeClStud = false, + bool doPullStud = false, + bool createOutput = false, + std::string tracfile = "o2trac_its.root", + std::string magfile = "o2sim_grp.root", + std::string clusfile = "o2clus_its.root", + std::string kinefile = "o2sim_Kine.root") { using namespace o2::itsmft; using namespace o2::its; + // Magnetic field and Propagator + o2::base::Propagator::initFieldFromGRP(magfile); + float bz = o2::base::Propagator::Instance()->getNominalBz(); + // Geometry o2::base::GeometryManager::loadGeometry(); auto gman = o2::its::GeometryTGeo::Instance(); @@ -59,11 +107,14 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its // MC tracks TFile* file0 = TFile::Open(kinefile.data()); TTree* mcTree = (TTree*)gFile->Get("o2sim"); - mcTree->SetBranchStatus("*", 0); //disable all branches + mcTree->SetBranchStatus("*", 0); // disable all branches mcTree->SetBranchStatus("MCTrack*", 1); + mcTree->SetBranchStatus("MCEventHeader*", 1); std::vector* mcArr = nullptr; mcTree->SetBranchAddress("MCTrack", &mcArr); + o2::dataformats::MCEventHeader* mcEvent = nullptr; + mcTree->SetBranchAddress("MCEventHeader.", &mcEvent); // Clusters TFile::Open(clusfile.data()); @@ -87,18 +138,28 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its std::cout << "** Filling particle table ... " << std::flush; int lastEventIDcl = -1, cf = 0; int nev = mcTree->GetEntriesFast(); - std::vector> info(nev); + std::vector> info; + info.resize(nev); + TH1D* hZvertex = new TH1D("hZvertex", "Z vertex", 100, -20, 20); for (int n = 0; n < nev; n++) { // loop over MC events mcTree->GetEvent(n); info[n].resize(mcArr->size()); + hZvertex->Fill(mcEvent->GetZ()); for (unsigned int mcI{0}; mcI < mcArr->size(); ++mcI) { - auto part = mcArr->at(mcI); + const auto part = mcArr->at(mcI); + if (!o2::O2DatabasePDG::Instance()->GetParticle(part.GetPdgCode())) { + continue; + } info[n][mcI].event = n; info[n][mcI].pdg = part.GetPdgCode(); + info[n][mcI].pvx = mcEvent->GetX(); + info[n][mcI].pvy = mcEvent->GetY(); + info[n][mcI].pvz = mcEvent->GetZ(); info[n][mcI].pt = part.GetPt(); info[n][mcI].phi = part.GetPhi(); info[n][mcI].eta = part.GetEta(); info[n][mcI].isPrimary = part.isPrimary(); + info[n][mcI].mcTrack = part; } } std::cout << "done." << std::endl; @@ -159,8 +220,12 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its info[evID][trackID].isFake += fake; /// We keep the best track we would keep in the data if (recArr->at(iTrack).isBetter(info[evID][trackID].track, 1.e9)) { - info[evID][trackID].storedStatus = fake; info[evID][trackID].track = recArr->at(iTrack); + info[evID][trackID].storedStatus = fake; + static float ip[2]{0., 0.}; + info[evID][trackID].track.getImpactParams(info[evID][trackID].pvx, info[evID][trackID].pvy, info[evID][trackID].pvz, bz, ip); + info[evID][trackID].dcaxy = ip[0]; + info[evID][trackID].dcaz = ip[1]; } fakes += fake; @@ -175,113 +240,108 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its std::cout << "\t- Total number of fakes: " << fakes << " (" << fakes * 100. / total << "%)" << std::endl; std::cout << "\t- Total number of good: " << good << " (" << good * 100. / total << "%)" << std::endl; - int nb = 100; - double xbins[nb + 1], ptcutl = 0.01, ptcuth = 10.; - double a = std::log(ptcuth / ptcutl) / nb; - for (int i = 0; i <= nb; i++) - xbins[i] = ptcutl * std::exp(i * a); - TH1D* num = new TH1D("num", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", nb, xbins); - num->Sumw2(); - TH1D* numEta = new TH1D("numEta", ";#eta;Number of tracks", 60, -3, 3); - numEta->Sumw2(); - TH1D* numChi2 = new TH1D("numChi2", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", 200, 0, 100); - - TH1D* fak = new TH1D("fak", ";#it{p}_{T} (GeV/#it{c});Fak", nb, xbins); - fak->Sumw2(); - TH1D* multiFak = new TH1D("multiFak", ";#it{p}_{T} (GeV/#it{c});Fak", nb, xbins); - multiFak->Sumw2(); - TH1D* fakChi2 = new TH1D("fakChi2", ";#it{p}_{T} (GeV/#it{c});Fak", 200, 0, 100); - - TH1D* clone = new TH1D("clone", ";#it{p}_{T} (GeV/#it{c});Clone", nb, xbins); - clone->Sumw2(); - - TH1D* den = new TH1D("den", ";#it{p}_{T} (GeV/#it{c});Den", nb, xbins); - den->Sumw2(); - - for (auto& evInfo : info) { - for (auto& part : evInfo) { - if ((part.clusters & 0x7f) != 0x7f) { - // part.clusters != 0x3f && part.clusters != 0x3f << 1 && - // part.clusters != 0x1f && part.clusters != 0x1f << 1 && part.clusters != 0x1f << 2 && - // part.clusters != 0x0f && part.clusters != 0x0f << 1 && part.clusters != 0x0f << 2 && part.clusters != 0x0f << 3) { - continue; - } - if (!part.isPrimary) { - continue; - } - den->Fill(part.pt); - if (part.isReco) { - num->Fill(part.pt); - numEta->Fill(part.eta); - if (part.isReco > 1) { - for (int _i{0}; _i < part.isReco - 1; ++_i) { - clone->Fill(part.pt); + TFile* file{nullptr}; + if (createOutput) { + file = TFile::Open("CheckTracksCA.root", "recreate"); + } + + if (doEffStud) { + std::cout << "Calculating efficiencies... "; + const int nb = 100; + double xbins[nb + 1], ptcutl = 0.01, ptcuth = 10.; + double a = std::log(ptcuth / ptcutl) / nb; + for (int i = 0; i <= nb; i++) + xbins[i] = ptcutl * std::exp(i * a); + TH1D* num = new TH1D("num", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", nb, xbins); + num->Sumw2(); + TH1D* numEta = new TH1D("numEta", ";#eta;Number of tracks", 60, -3, 3); + numEta->Sumw2(); + TH1D* numChi2 = new TH1D("numChi2", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", 200, 0, 100); + + TH1D* fak = new TH1D("fak", ";#it{p}_{T} (GeV/#it{c});Fak", nb, xbins); + fak->Sumw2(); + TH1D* multiFak = new TH1D("multiFak", ";#it{p}_{T} (GeV/#it{c});Fak", nb, xbins); + multiFak->Sumw2(); + TH1D* fakChi2 = new TH1D("fakChi2", ";#it{p}_{T} (GeV/#it{c});Fak", 200, 0, 100); + + TH1D* clone = new TH1D("clone", ";#it{p}_{T} (GeV/#it{c});Clone", nb, xbins); + clone->Sumw2(); + + TH1D* den = new TH1D("den", ";#it{p}_{T} (GeV/#it{c});Den", nb, xbins); + den->Sumw2(); + + for (auto& evInfo : info) { + for (auto& part : evInfo) { + if ((part.clusters & 0x7f) != 0x7f) { + // part.clusters != 0x3f && part.clusters != 0x3f << 1 && + // part.clusters != 0x1f && part.clusters != 0x1f << 1 && part.clusters != 0x1f << 2 && + // part.clusters != 0x0f && part.clusters != 0x0f << 1 && part.clusters != 0x0f << 2 && part.clusters != 0x0f << 3) { + continue; + } + if (!part.isPrimary) { + continue; + } + den->Fill(part.pt); + if (part.isReco) { + num->Fill(part.pt); + numEta->Fill(part.eta); + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + clone->Fill(part.pt); + } } } - } - if (part.isFake) { - fak->Fill(part.pt); - if (part.isFake > 1) { - for (int _i{0}; _i < part.isFake - 1; ++_i) { - multiFak->Fill(part.pt); + if (part.isFake) { + fak->Fill(part.pt); + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + multiFak->Fill(part.pt); + } } } } } - } - TCanvas* c1 = new TCanvas; - c1->SetLogx(); - c1->SetGridx(); - c1->SetGridy(); - TH1* sum = (TH1*)num->Clone("sum"); - sum->Add(fak); - sum->Divide(sum, den, 1, 1); - sum->SetLineColor(kBlack); - sum->Draw("hist"); - num->Divide(num, den, 1, 1, "b"); - num->Draw("histesame"); - fak->Divide(fak, den, 1, 1, "b"); - fak->SetLineColor(2); - fak->Draw("histesame"); - multiFak->Divide(multiFak, den, 1, 1, "b"); - multiFak->SetLineColor(kRed + 1); - multiFak->Draw("histsame"); - clone->Divide(clone, den, 1, 1, "b"); - clone->SetLineColor(3); - clone->Draw("histesame"); - - std::cout << "** Streaming output TTree to file ... " << std::flush; - TFile file("CheckTracksCA.root", "recreate"); - TTree tree("ParticleInfo", "ParticleInfo"); - ParticleInfo pInfo; - tree.Branch("particle", &pInfo); - for (auto& event : info) { - for (auto& part : event) { - int nCl{0}; - for (unsigned int bit{0}; bit < sizeof(pInfo.clusters) * 8; ++bit) { - nCl += bool(part.clusters & (1 << bit)); - } - if (nCl < 3) { - continue; - } - pInfo = part; - tree.Fill(); + TCanvas* c1 = new TCanvas; + c1->SetLogx(); + c1->SetGridx(); + c1->SetGridy(); + TH1* sum = (TH1*)num->Clone("sum"); + sum->Add(fak); + sum->Divide(sum, den, 1, 1); + sum->SetLineColor(kBlack); + sum->Draw("hist"); + num->Divide(num, den, 1, 1, "b"); + num->Draw("histesame"); + fak->Divide(fak, den, 1, 1, "b"); + fak->SetLineColor(2); + fak->Draw("histesame"); + multiFak->Divide(multiFak, den, 1, 1, "b"); + multiFak->SetLineColor(kRed + 1); + multiFak->Draw("histsame"); + clone->Divide(clone, den, 1, 1, "b"); + clone->SetLineColor(3); + clone->Draw("histesame"); + TCanvas* c2 = new TCanvas; + c2->SetGridx(); + c2->SetGridy(); + hZvertex->DrawClone(); + + if (createOutput) { + sum->Write("total"); + fak->Write("singleFake"); + num->Write("efficiency"); + numEta->Write("etaDist"); + multiFak->Write("multiFake"); + clone->Write("clones"); } + std::cout << " done." << std::endl; } - tree.Write(); - sum->Write("total"); - fak->Write("singleFake"); - num->Write("efficiency"); - numEta->Write("etaDist"); - multiFak->Write("multiFake"); - clone->Write("clones"); - file.Close(); - std::cout << " done." << std::endl; ////////////////////// // Fake clusters study if (doFakeClStud) { + std::cout << "Creating fake cluster study... "; std::vector histLength, histLength1Fake, histLengthNoCl, histLength1FakeNoCl; std::vector stackLength, stackLength1Fake; std::vector legends, legends1Fake; @@ -321,10 +381,10 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its stackLength1Fake[iH - 4]->Add(histLength1FakeNoCl[iH - 4]); } - for (auto& event : info) { - for (auto& part : event) { + for (const auto& event : info) { + for (const auto& part : event) { int nCl{0}; - for (unsigned int bit{0}; bit < sizeof(pInfo.clusters) * 8; ++bit) { + for (unsigned int bit{0}; bit < sizeof(part.clusters) * 8; ++bit) { nCl += bool(part.clusters & (1 << bit)); } if (nCl < 3) { @@ -366,5 +426,237 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its gPad->BuildLegend(); } canvas->SaveAs("fakeClusters.png", "recreate"); + std::cout << " done\n"; + } + + if (doPullStud) { + std::cout << "Creating pull study... "; + const int nBins{30}; + const float xWidth{10}; + // Pulls + auto hYPull = new TH1F("hYPull", "Pull Y", nBins, -xWidth, xWidth); + auto hZPull = new TH1F("hZPull", "Pull Z", nBins, -xWidth, xWidth); + auto hSPhiPull = new TH1F("hSPhiPull", "Pull Sin(Phi)", nBins, -xWidth, xWidth); + auto hTglPull = new TH1F("hTglPull", "Pull Tg(Lambda)", nBins, -xWidth, xWidth); + auto hQoPtPull = new TH1F("hQoPtPull", "Pull Q/Pt", nBins, -xWidth, xWidth); + // Correlation + float maxY2{1e-6}, maxZ2{1e-6}, maxSnp2{2e-6}, maxTgl2{2e-6}, max1Pt2{0.01}; + auto hCorYZ = new TH2F("hCorYZ", ";#sigma_{Z}^{2};#sigma_{Y}^{2}", nBins, 0, maxZ2, nBins, 0, maxY2); + auto hCorYSPhi = new TH2F("hCorYSPhi", ";#sigma_{snp}^{2};#sigma_{Y}^{2}", nBins, 0, maxSnp2, nBins, 0, maxY2); + auto hCorYTgl = new TH2F("hCorYTgl", ";#sigma_{tgl}^{2};#sigma_{Y}^{2}", nBins, 0, maxTgl2, nBins, 0, maxY2); + auto hCorYQoPt = new TH2F("hCorYQoPt", ";#sigma_{Q/Pt}^{2};#sigma_{Y}^{2}", nBins, 0, max1Pt2, nBins, 0, maxY2); + + auto hCorZSPhi = new TH2F("hCorZSPhi", ";#sigma_{snp}^{2};#sigma_{Z}^{2}", nBins, 0, maxSnp2, nBins, 0, maxZ2); + auto hCorZTgl = new TH2F("hCorZTgl", ";#sigma_{tgl}^{2};#sigma_{Z}^{2}", nBins, 0, maxTgl2, nBins, 0, maxZ2); + auto hCorZQoPt = new TH2F("hCorZQoPt", ";#sigma_{Q/Pt}^{2};#sigma_{Z}^{2}", nBins, 0, max1Pt2, nBins, 0, maxZ2); + + auto hCorSPhiTgl = new TH2F("hCorSPhiTgl", ";#sigma_{tgl}^{2};#sigma_{snp}^{2}", nBins, 0, maxTgl2, nBins, 0, maxSnp2); + auto hCorSPhiQoPt = new TH2F("hCorSPhiQoPt", ";#sigma_{Q/Pt}^{2};#sigma_{snp}^{2}", nBins, 0, max1Pt2, nBins, 0, maxSnp2); + + auto hCorTglQoPt = new TH2F("hCorTglQoPt", ";#sigma_{Q/Pt}^{2};#sigma_{tgl}^{2}", nBins, 0, max1Pt2, nBins, 0, maxTgl2); + + auto calcMahalanobisDist = [&](const auto& trk, const auto& mc) -> float { + o2::math_utils::SMatrix> cov; + cov(o2::track::kY, o2::track::kY) = trk.getSigmaY2(); + cov(o2::track::kZ, o2::track::kY) = trk.getSigmaZY(); + cov(o2::track::kZ, o2::track::kZ) = trk.getSigmaZ2(); + cov(o2::track::kSnp, o2::track::kY) = trk.getSigmaSnpY(); + cov(o2::track::kSnp, o2::track::kZ) = trk.getSigmaSnpZ(); + cov(o2::track::kSnp, o2::track::kSnp) = trk.getSigmaSnp2(); + cov(o2::track::kTgl, o2::track::kY) = trk.getSigmaTglY(); + cov(o2::track::kTgl, o2::track::kZ) = trk.getSigmaTglZ(); + cov(o2::track::kTgl, o2::track::kSnp) = trk.getSigmaTglSnp(); + cov(o2::track::kTgl, o2::track::kTgl) = trk.getSigmaTgl2(); + cov(o2::track::kQ2Pt, o2::track::kY) = trk.getSigma1PtY(); + cov(o2::track::kQ2Pt, o2::track::kZ) = trk.getSigma1PtZ(); + cov(o2::track::kQ2Pt, o2::track::kSnp) = trk.getSigma1PtSnp(); + cov(o2::track::kQ2Pt, o2::track::kTgl) = trk.getSigma1PtTgl(); + cov(o2::track::kQ2Pt, o2::track::kQ2Pt) = trk.getSigma1Pt2(); + if (!cov.Invert()) { + return -1.f; + } + o2::math_utils::SVector trkPar(trk.getParams(), o2::track::kNParams), mcPar(mc.getParams(), o2::track::kNParams); + auto res = trkPar - mcPar; + return std::sqrt(ROOT::Math::Similarity(cov, res)); + }; + auto hMahDist = new TH1F("hMahDist", ";Mahalanobis distance;n. entries", 100, 0, 10); + TF1* fchi = new TF1("fchi", chi2_pdf, 0, 6, 3); + fchi->SetParNames("A", "k", "s"); + fchi->SetParameter(0, 1); + fchi->SetParameter(1, 5); + fchi->SetParameter(2, 1); + + for (const auto& event : info) { + for (const auto& part : event) { + if (((part.clusters & 0x7f) != 0x7f) && !part.isPrimary) { + continue; + } + + // prepare mc truth parameters + std::array xyz{(float)part.mcTrack.GetStartVertexCoordinatesX(), (float)part.mcTrack.GetStartVertexCoordinatesY(), (float)part.mcTrack.GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)part.mcTrack.GetStartVertexMomentumX(), (float)part.mcTrack.GetStartVertexMomentumY(), (float)part.mcTrack.GetStartVertexMomentumZ()}; + o2::track::TrackPar mcTrack(xyz, pxyz, TMath::Nint(o2::O2DatabasePDG::Instance()->GetParticle(part.mcTrack.GetPdgCode())->Charge() / 3), false); + if (!mcTrack.rotate(part.track.getAlpha()) || + !o2::base::Propagator::Instance()->propagateTo(mcTrack, part.track.getX())) { + continue; + } + + const float sY = part.track.getSigmaY2(); + const float sZ = part.track.getSigmaZ2(); + const float sSnp = part.track.getSigmaSnp2(); + const float sTgl = part.track.getSigmaTgl2(); + const float s1Pt = part.track.getSigma1Pt2(); + + hYPull->Fill((part.track.getY() - mcTrack.getY()) / std::sqrt(part.track.getSigmaY2())); + hZPull->Fill((part.track.getZ() - mcTrack.getZ()) / std::sqrt(part.track.getSigmaZ2())); + hSPhiPull->Fill((part.track.getSnp() - mcTrack.getSnp()) / std::sqrt(part.track.getSigmaSnp2())); + hTglPull->Fill((part.track.getTgl() - mcTrack.getTgl()) / std::sqrt(part.track.getSigmaTgl2())); + hQoPtPull->Fill((part.track.getQ2Pt() - mcTrack.getQ2Pt()) / std::sqrt(part.track.getSigma1Pt2())); + + hCorYZ->Fill(part.track.getSigmaZ2(), part.track.getSigmaY2()); + hCorYSPhi->Fill(part.track.getSigmaSnp2(), part.track.getSigmaY2()); + hCorYTgl->Fill(part.track.getSigmaTgl2(), part.track.getSigmaY2()); + hCorYQoPt->Fill(part.track.getSigma1Pt2(), part.track.getSigmaY2()); + + hCorZSPhi->Fill(part.track.getSigmaSnp2(), part.track.getSigmaZ2()); + hCorZTgl->Fill(part.track.getSigmaTgl2(), part.track.getSigmaZ2()); + hCorZQoPt->Fill(part.track.getSigma1Pt2(), part.track.getSigmaZ2()); + + hCorSPhiTgl->Fill(part.track.getSigmaTgl2(), part.track.getSigmaSnp2()); + hCorSPhiQoPt->Fill(part.track.getSigma1Pt2(), part.track.getSigmaSnp2()); + + hCorTglQoPt->Fill(part.track.getSigma1Pt2(), part.track.getSigmaTgl2()); + + hMahDist->Fill(calcMahalanobisDist(part.track, mcTrack)); + } + } + + // normalise, set axis, fit and draw + auto doPullCalc = [](TH1F* h) { + h->Scale(1. / h->Integral("width")); + h->GetYaxis()->SetRangeUser(1e-5, 1.); + gPad->SetLogy(); + h->Draw("hist"); + h->Fit("gaus", "QMR", "", -3, 3); + if (auto f = h->GetFunction("gaus")) { + f->SetLineColor(kRed); + f->SetLineWidth(2); + f->Draw("same"); + const double mean = f->GetParameter(1); + const double sigma = f->GetParameter(2); + TLatex lat; + lat.SetNDC(); + lat.SetTextFont(42); + lat.SetTextSize(0.04); + lat.DrawLatex(0.62, 0.85, Form("#mu = %.4f", mean)); + lat.DrawLatex(0.62, 0.79, Form("#sigma = %.4f", sigma)); + } + }; + hMahDist->Scale(1. / hMahDist->Integral("width")); + TFitResultPtr fitres = hMahDist->Fit(fchi, "RMQS"); + + auto c = new TCanvas("cPull", "", 2000, 1000); + c->Divide(5, 5); + c->cd(1); + doPullCalc(hYPull); + c->cd(2); + hCorYZ->Draw("colz"); + c->cd(3); + hCorYSPhi->Draw("colz"); + c->cd(4); + hCorYTgl->Draw("colz"); + c->cd(5); + hCorYQoPt->Draw("colz"); + + c->cd(7); + doPullCalc(hZPull); + c->cd(8); + hCorZSPhi->Draw("colz"); + c->cd(9); + hCorZTgl->Draw("colz"); + c->cd(10); + hCorZQoPt->Draw("colz"); + + c->cd(13); + doPullCalc(hSPhiPull); + c->cd(14); + hCorSPhiTgl->Draw("colz"); + c->cd(15); + hCorSPhiQoPt->Draw("colz"); + + c->cd(19); + doPullCalc(hTglPull); + c->cd(20); + hCorTglQoPt->Draw("colz"); + + c->cd(); + const double xlow = 0.0; + const double xup = 0.4; + const double ylow = 0.0; + const double yup = 0.4; + auto pMahBig = new TPad("pMahBig", "Mahalanobis Distance", xlow, ylow, xup, yup); + pMahBig->SetFillStyle(4000); + pMahBig->SetBorderMode(0); + pMahBig->SetLeftMargin(0.12); + pMahBig->SetRightMargin(0.02); + pMahBig->SetBottomMargin(0.12); + pMahBig->SetTopMargin(0.05); + pMahBig->Draw(); + pMahBig->cd(); + hMahDist->Draw("hist"); + fchi->SetLineColor(kRed); + fchi->SetLineWidth(2); + fchi->Draw("same"); + const Double_t A_fit = fchi->GetParameter(0); + const Double_t k_fit = fchi->GetParameter(1); + const Double_t s_fit = fchi->GetParameter(2); + const Double_t A_err = fchi->GetParError(0); + const Double_t k_err = fchi->GetParError(1); + const Double_t s_err = fchi->GetParError(2); + const Double_t chi2 = fchi->GetChisquare(); + const Int_t ndf = fchi->GetNDF(); + TLatex lat; + lat.SetNDC(); + lat.SetTextFont(42); + lat.SetTextSize(0.038); + lat.SetTextAlign(11); + const Double_t xText = 0.55; + Double_t yText = 0.85; + const Double_t dy = 0.06; + lat.DrawLatex(xText, yText, Form("A = %.3g #pm %.3g", A_fit, A_err)); + yText -= dy; + lat.DrawLatex(xText, yText, Form("k (ndf) = %.3f #pm %.3f", k_fit, k_err)); + yText -= dy; + lat.DrawLatex(xText, yText, Form("scale s = %.3g #pm %.3g", s_fit, s_err)); + yText -= dy; + lat.DrawLatex(xText, yText, Form("#chi^{2}/ndf = %.2f / %d", chi2, ndf)); + yText -= dy; + if (fitres.Get()) { + lat.DrawLatex(xText, yText, Form("Fit status = %d", fitres->Status())); + yText -= dy; + } + + c->cd(25); + doPullCalc(hQoPtPull); + c->Draw(); + std::cout << " done\n"; + } + + if (createOutput) { + std::cout << "** Streaming output TTree to file ... " << std::flush; + TTree tree("ParticleInfo", "ParticleInfo"); + ParticleInfo pInfo; + tree.Branch("particle", &pInfo); + for (const auto& event : info) { + for (const auto& part : event) { + if (((part.clusters & 0x7f) != 0x7f) && !part.isPrimary) { + continue; + } + pInfo = part; + tree.Fill(); + } + } + tree.Write(); + file->Close(); } } diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckVertices.C b/Detectors/ITSMFT/ITS/macros/test/CheckVertices.C index f348272425b77..2da3ed9b97a28 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckVertices.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckVertices.C @@ -29,6 +29,7 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITS/TrackITS.h" #endif +#include "DataFormatsITSMFT/CompCluster.h" o2::MCCompLabel getMainLabel(std::vector& labs); @@ -335,4 +336,4 @@ void CheckVertices(const int dumprof = -1, std::string path = "tf1/", std::strin LOGP(info, "Summary:"); LOGP(info, "Found {} vertices in {} usable out of {} simulated", nvt, nevts, simVerts.size()); LOGP(info, "Average good vertexing efficiency: {}%", (addeff / (float)nroffilled) * 100); -} \ No newline at end of file +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CreateDictionaries.C b/Detectors/ITSMFT/ITS/macros/test/CreateDictionaries.C index f0a2ef0de6398..3f906ebc04e17 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CreateDictionaries.C +++ b/Detectors/ITSMFT/ITS/macros/test/CreateDictionaries.C @@ -140,7 +140,7 @@ void CreateDictionaries(bool saveDeltas = false, } clusTree->GetEntry(0); if (clusTree->GetEntries() > 1 && !hitfile.empty()) { - LOGP(error, "Hits are provided but the cluster tree containes {} entries, looks like real data"); + LOGP(error, "Hits are provided but the cluster tree containes {} entries, looks like real data", clusTree->GetEntries()); return; } diff --git a/Detectors/ITSMFT/ITS/macros/test/ITSMisaligner.C b/Detectors/ITSMFT/ITS/macros/test/ITSMisaligner.C index e04c2ca572804..eb6cb7a39b41c 100644 --- a/Detectors/ITSMFT/ITS/macros/test/ITSMisaligner.C +++ b/Detectors/ITSMFT/ITS/macros/test/ITSMisaligner.C @@ -78,7 +78,7 @@ void ITSMisaligner(const std::string& ccdbHost = "http://localhost:8080", long t std::string path = objectPath.empty() ? o2::base::DetectorNameConf::getAlignmentPath(detITS) : objectPath; LOGP(info, "Storing alignment object on {}/{}", ccdbHost, path); o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdbHost.c_str()); // or http://localhost:8080 for a local installation // store abitrary user object in strongly typed manner api.storeAsTFileAny(¶ms, path, metadata, tmin, tmax); diff --git a/Detectors/ITSMFT/ITS/macros/test/run_digi2rawVarPage_its.C b/Detectors/ITSMFT/ITS/macros/test/run_digi2rawVarPage_its.C index 253726f9c90fe..1c682fc25fff1 100644 --- a/Detectors/ITSMFT/ITS/macros/test/run_digi2rawVarPage_its.C +++ b/Detectors/ITSMFT/ITS/macros/test/run_digi2rawVarPage_its.C @@ -12,9 +12,9 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsParameters/GRPObject.h" #include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTSimulation/MC2RawEncoder.h" #endif +#include "ITSMFTSimulation/MC2RawEncoder.h" // demo macro the MC->raw conversion with new (variable page size) format void setupLinks(o2::itsmft::MC2RawEncoder& m2r, const std::string& outPrefix); diff --git a/Detectors/ITSMFT/ITS/postprocessing/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/CMakeLists.txt new file mode 100644 index 0000000000000..83bb2ec86dbca --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +add_subdirectory(studies) +add_subdirectory(workflow) \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt new file mode 100644 index 0000000000000..08ced25d0f5dc --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(ITSPostprocessing +SOURCES src/ImpactParameter.cxx + src/AvgClusSize.cxx + src/PIDStudy.cxx + src/ITSStudiesConfigParam.cxx + src/AnomalyStudy.cxx + src/ITSBeamBackgroundStudy.cxx + src/TrackCheck.cxx + src/TrackExtension.cxx + src/Efficiency.cxx + src/Helpers.cxx +PUBLIC_LINK_LIBRARIES O2::GlobalTracking + O2::GlobalTrackingWorkflowReaders + O2::GlobalTrackingWorkflowHelpers + O2::DataFormatsGlobalTracking + O2::DetectorsVertexing + O2::DetectorsBase) + +o2_target_root_dictionary(ITSPostprocessing +HEADERS include/ITSStudies/ITSStudiesConfigParam.h + include/ITSStudies/TrackCuts.h + include/ITSStudies/TrackMethods.h +LINKDEF src/ITSStudiesLinkDef.h) + +add_subdirectory(macros) diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AnomalyStudy.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AnomalyStudy.h new file mode 100644 index 0000000000000..946b5ef08fd48 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AnomalyStudy.h @@ -0,0 +1,29 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ANOMALY_STUDY_H +#define O2_ANOMALY_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2::its +{ +namespace study +{ + +using mask_t = o2::dataformats::GlobalTrackID::mask_t; + +o2::framework::DataProcessorSpec getAnomalyStudy(mask_t srcClustersMask, bool useMC); +} // namespace study +} // namespace o2::its + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AvgClusSize.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AvgClusSize.h new file mode 100644 index 0000000000000..766bb1f94ca1f --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AvgClusSize.h @@ -0,0 +1,36 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AvgClusSize.h +/// \author Tucker Hwang mhwang@cern.ch + +#ifndef O2_AVGCLUSSIZE_STUDY_H +#define O2_AVGCLUSSIZE_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ + +using mask_t = o2::dataformats::GlobalTrackID::mask_t; + +o2::framework::DataProcessorSpec getAvgClusSizeStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader); +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Efficiency.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Efficiency.h new file mode 100644 index 0000000000000..b6f43bb772390 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Efficiency.h @@ -0,0 +1,57 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_EFFICIENCY_STUDY_H +#define O2_EFFICIENCY_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2 +{ +namespace steer +{ +class MCKinematicsReader; +} +namespace its +{ +namespace study +{ +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +o2::framework::DataProcessorSpec getEfficiencyStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader); + +float mEtaCuts[2] = {-1.0, 1.0}; +float mPtCuts[2] = {0, 10}; /// no cut for B=0 + +// values obtained from the dca study for B=5 +// float dcaXY[3] = {-0.000326, -0.000217, -0.000187}; +// float dcaZ[3] = {0.000020, -0.000004, 0.000032}; +// float sigmaDcaXY[3] = {0.001375, 0.001279, 0.002681}; +// float sigmaDcaZ[3] = {0.002196, 0.002083, 0.004125}; + +// values obtained from the dca study for B=0 +float dcaXY[3] = {-0.000328, -0.000213, -0.000203}; +float dcaZ[3] = {-0.000000543, -0.000013, 0.000001}; +float sigmaDcaXY[3] = {0.00109, 0.000895, 0.001520}; +float sigmaDcaZ[3] = {0.001366, 0.001149, 0.001868}; + +int dcaCut = 8; + +float mDCACutsXY[3][2] = {{dcaXY[0] - dcaCut * sigmaDcaXY[0], dcaXY[0] + dcaCut* sigmaDcaXY[0]}, {dcaXY[1] - dcaCut * sigmaDcaXY[1], dcaXY[1] + dcaCut* sigmaDcaXY[1]}, {dcaXY[2] - dcaCut * sigmaDcaXY[2], dcaXY[2] + dcaCut* sigmaDcaXY[2]}}; // cuts at 8 sigma for each layer for xy. The values represent m-8sigma and m+8sigma +float mDCACutsZ[3][2] = {{dcaZ[0] - dcaCut * sigmaDcaZ[0], dcaZ[0] + dcaCut* sigmaDcaZ[0]}, {dcaZ[1] - dcaCut * sigmaDcaZ[1], dcaZ[1] + dcaCut* sigmaDcaZ[1]}, {dcaZ[2] - dcaCut * sigmaDcaZ[2], dcaZ[2] + dcaCut* sigmaDcaZ[2]}}; + +/// excluding bad chips in MC that are not present in data: to be checked based on the anchoring +std::vector mExcludedChipMC = {66, 67, 68, 75, 76, 77, 84, 85, 86, 93, 94, 95, 102, 103, 104, 265, 266, 267, 274, 275, 276, 283, 284, 285, 413, 414, 415, 422, 423, 424, 431, 432, 433}; + +} // namespace study +} // namespace its +} // namespace o2 +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Helpers.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Helpers.h new file mode 100644 index 0000000000000..1df602f7d7f17 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Helpers.h @@ -0,0 +1,67 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// @author Antonio Palasciano, antonio.palasciano@ba.infn.it +/// + +#ifndef O2_ITS_STUDY_HELPERS_H +#define O2_ITS_STUDY_HELPERS_H + +#include +#include "TH1F.h" +#include "TH2F.h" +#include "TGraphAsymmErrors.h" +#include "TCanvas.h" +#include "TPaveText.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ +namespace helpers +{ + +/// Some utility functions for postprocessing ITS +/// + +/// get a vector containing binning info for constant sized bins on a log axis +std::vector makeLogBinning(const int nbins, const double min, const double max); + +/// Set nice style for single 1D histograms +void setStyleHistogram1D(TH1F& histo, int color); +void setStyleHistogram1D(TH1F& histo, int color, TString title, TString titleYaxis, TString titleXaxis); +void setStyleHistogram1DMeanValues(TH1F& histo, int color); +void setStyleHistogram2D(TH2F& histo); + +/// prepare canvas with two TH1F plots +TCanvas* prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TH1F& h2, int color2); +TCanvas* prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2, bool logScale = true); +TCanvas* prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2, TString intRate); +TCanvas* prepareSimpleCanvas2DcaMeanValues(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2); + +/// plot canvas with TH2D + TH1D(Mean and Sigma) from slice +TCanvas* plot2DwithMeanAndSigma(TH2F& h2D, TH1F& hMean, TH1F& hSigma, int color); + +/// prepare TPaveText with labels +void paveTextITS(TPaveText* pave, TString intRate); + +/// Convert TH1F in TGraphAsymmetricError +void ConvertTH1ToTGraphAsymmError(TH1F& hMean, TH1F& hSigma, TGraphAsymmErrors*& gr); + +} // namespace helpers +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSBeamBackgroundStudy.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSBeamBackgroundStudy.h new file mode 100644 index 0000000000000..cd96a9df8dee6 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSBeamBackgroundStudy.h @@ -0,0 +1,26 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS_BEAMBKG_STUDY_H +#define O2_ITS_BEAMBKG_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2::its::study +{ + +using mask_t = o2::dataformats::GlobalTrackID::mask_t; + +o2::framework::DataProcessorSpec getITSBeamBackgroundStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC); + +} // namespace o2::its::study +#endif diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h new file mode 100644 index 0000000000000..85e114e0fb739 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h @@ -0,0 +1,118 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ITS_STUDY_CONFIG_PARAM_H +#define ITS_STUDY_CONFIG_PARAM_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ +struct ITSCheckTracksParamConfig : public o2::conf::ConfigurableParamHelper { + std::string outFileName = "TrackCheckStudy.root"; + size_t effHistBins = 100; + unsigned short trackLengthMask = 0x7f; + float effPtCutLow = 0.01; + float effPtCutHigh = 10.; + + O2ParamDef(ITSCheckTracksParamConfig, "ITSCheckTracksParam"); +}; + +struct ITSAvgClusSizeParamConfig : public o2::conf::ConfigurableParamHelper { + // Data parameters + double b = 5; // Solenoid field in kG (+/-) + + // K0s ID cuts + std::string targetV0 = "K0"; // target V0; set as "K0" or "Lambda" + float tgV0window = 0.02; // half-width of mass window for target V0 mass hypothesis testing (GeV) + float bgV0window = 0.01; // half-width of mass window for background V0 mass hypothesis testing (GeV) + float Rmin = 0.; // lower limit on V0 decay length (cm?) + float Rmax = 5.4; // upper limit on V0 decay length (cm?) + float cosPAmin = 0.995; // lower limit on cosine of pointing angle + float prongDCAmax = 0.2; // upper limit on DCA between two daughter prongs (cm?) + float dauPVDCAmin = 0.2; // lower limit on DCA between prong and primary vertex (cm?) + float v0PVDCAmax = 0.2; // upper limit on DCA between V0 and primary vertex (cm?) + int dauNClusMin = 0; // lower limit on number of ITS clusters on daughter tracks TODO: not yet implemented + + // Kinematic cut disable flags, false="leave this cut on"; NOTE: may be a better way to implement this with std::bitset<8> + bool disableCosPA = false; + bool disableRmin = false; + bool disableRmax = false; + bool disableProngDCAmax = false; + bool disableDauPVDCAmin = false; + bool disableV0PVDCAmax = false; + bool disableDauNClusmin = false; // TODO: not yet implemented + bool disableMassHypoth = true; // applies to both target and background V0 cuts + + // Plotting options + bool generatePlots = true; // flag to generate plots + std::string outFileName = "o2standalone_cluster_size_study.root"; // filename for the ROOT output of this study + + // Average cluster size plot: eta binning parameters + float etaMin = -1.5; // lower edge of lowest bin for eta binning on average cluster size + float etaMax = 1.5; // upper edge for highest bin for eta binning on average cluster size + int etaNBins = 5; // number of eta bins + + // Average cluster size plot: cluster size binning parameters + float sizeMax = 15; // upper edge of highest bin for average cluster size + int sizeNBins = 20; // number of cluster size bins + + O2ParamDef(ITSAvgClusSizeParamConfig, "ITSAvgClusSizeParam"); +}; + +struct PIDStudyParamConfig : public o2::conf::ConfigurableParamHelper { + std::string outFileName = "its_PIDStudy.root"; + // default: average 2023 from C. Sonnabend, Nov 2023: ([0.217553 4.02762 0.00850178 2.33324 0.880904 ]) + // to-do: grab from CCDB when available + float mBBpars[5] = {0.217553, 4.02762, 0.00850178, 2.33324, 0.880904}; + float mBBres = 0.07; // default: 7% resolution + O2ParamDef(PIDStudyParamConfig, "PIDStudyParam"); +}; + +struct AnomalyStudyParamConfig : public o2::conf::ConfigurableParamHelper { + std::string outFileName = "its_AnomalyStudy.root"; + size_t nLayersToProcess = 3; + size_t nTimeFramesOffset = 500; + size_t nRofTimeFrames = 192; + float nPhiBinsMultiplier = 1.f; + float pValueCut = 0.05; + bool doROFAnalysis = false; + + O2ParamDef(AnomalyStudyParamConfig, "AnomalyStudyParam"); +}; + +struct ITSEfficiencyParamConfig : public o2::conf::ConfigurableParamHelper { + std::string outFileName = "ITS_efficiencyStudy.root"; + double b = 0; // Solenoid field in kG (+/-) + + O2ParamDef(ITSEfficiencyParamConfig, "ITSEfficiencyParam"); +}; + +struct ITSImpactParameterParamConfig : public o2::conf::ConfigurableParamHelper { + std::string outFileName = "its_ImpParameter.root"; + int minNumberOfContributors = 0; + bool applyTrackCuts = false; + bool useAllTracks = false; + bool generatePlots = false; + + O2ParamDef(ITSImpactParameterParamConfig, "ITSImpactParameterParam"); +}; + +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ImpactParameter.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ImpactParameter.h new file mode 100644 index 0000000000000..43ec3971e7ee3 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ImpactParameter.h @@ -0,0 +1,32 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_IMPACT_PARAMETER_STUDY_H +#define O2_IMPACT_PARAMETER_STUDY_H + +#include +#include +#include "ITSStudies/ITSStudiesConfigParam.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ +using mask_t = o2::dataformats::GlobalTrackID::mask_t; + +o2::framework::DataProcessorSpec getImpactParameterStudy(mask_t srcTracksMask, mask_t srcClusMask, bool useMC = false); +} // namespace study +} // namespace its +} // namespace o2 + +#endif // O2_IMPACT_PARAMETER_STUDY_H \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/PIDStudy.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/PIDStudy.h new file mode 100644 index 0000000000000..54c7e5f387e90 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/PIDStudy.h @@ -0,0 +1,36 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file PIDStudy.h +/// \author Francesco Mazzaschi, fmazzasc@cern.ch + +#ifndef O2_PID_STUDY_H +#define O2_PID_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ + +using mask_t = o2::dataformats::GlobalTrackID::mask_t; + +o2::framework::DataProcessorSpec getPIDStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader); +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCheck.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCheck.h new file mode 100644 index 0000000000000..406677796a9b3 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCheck.h @@ -0,0 +1,33 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACK_CHECK_STUDY_H +#define O2_TRACK_CHECK_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2 +{ +namespace steer +{ +class MCKinematicsReader; +} +namespace its +{ +namespace study +{ +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +o2::framework::DataProcessorSpec getTrackCheckStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader); +} // namespace study +} // namespace its +} // namespace o2 +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCuts.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCuts.h new file mode 100644 index 0000000000000..03f52aae380c5 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCuts.h @@ -0,0 +1,207 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TrackCuts.h +/// \brief Class to perform track cuts + +// This class is developed in order to be used for extensive track selections. The selections are done detector wise +//(or for detector combinations). +// sources: https://github.com/AliceO2Group/AliceO2/blob/e988b0c43346ccb24f3515d1a24f058313f14a0f/DataFormats/Reconstruction/include/ReconstructionDataFormats/GlobalTrackID.h#L40 +// +// !!! For further development: +// The main method is isSelected(o2::dataformats::GlobalTrackID, o2::globaltracking::RecoContainer&), which is a boolean +// that returns true only if all the checks are passed. First, based on the global track id, the track detector source is +// inquired(e.g. below for ITS and TPC tracks). The source-specific tracks is initialized in order to access the +// source-specific parameters than one wants to perform selections on. +// For each detector source, the inquiry should be done in such way that “false” should be returned if the checks are not passed, +// moving to the next detector source otherwise. +// (e.g below for TPC tracks, where the track selections used here: https://github.com/AliceO2Group/AliceO2/blob/dev/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx#L318 +// are reproduced; +// Moreover, an example of how this class should be used can be found here: https://github.com/AliceO2Group/AliceO2/blob/dev/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx#L184). + +#ifndef O2_TRACK_CUTS_STUDY_H +#define O2_TRACK_CUTS_STUDY_H + +#include "Framework/Logger.h" +#include "Framework/DataTypes.h" +#include "ReconstructionDataFormats/Track.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "ReconstructionDataFormats/MatchInfoTOF.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsTRD/TrackTRD.h" +#include "ITSStudies/TrackMethods.h" +#include "DetectorsBase/Propagator.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include +#include +#include "Rtypes.h" +#include +namespace o2 +{ +namespace its +{ +namespace study +{ +using GID = o2::dataformats::GlobalTrackID; +using GIndex = o2::dataformats::VtxTrackIndex; +class TrackCuts +{ + public: + void countTPCClusters(const o2::globaltracking::RecoContainer& data) + { + const auto& tpcTracks = data.getTPCTracks(); + const auto& tpcClusRefs = data.getTPCTracksClusterRefs(); + const auto& tpcClusShMap = data.clusterShMapTPC; + const auto& tpcClusAcc = data.getTPCClusters(); + constexpr int maxRows = 152; + constexpr int neighbour = 2; + int ntr = tpcTracks.size(); + mTPCCounters.clear(); + mTPCCounters.resize(ntr); + for (int itr = 0; itr < ntr; itr++) { + std::array clMap{}, shMap{}; + uint8_t sectorIndex, rowIndex; + uint32_t clusterIndex; + auto& counters = mTPCCounters[itr]; + const auto& track = tpcTracks[itr]; + for (int i = 0; i < track.getNClusterReferences(); i++) { + o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef()); + unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex; + clMap[rowIndex] = true; + if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (!shMap[rowIndex]) { + counters.shared++; + } + shMap[rowIndex] = true; + } + } + int last = -1; + for (int i = 0; i < maxRows; i++) { + if (clMap[i]) { + counters.crossed++; + counters.found++; + last = i; + } else if ((i - last) <= neighbour) { + counters.crossed++; + } else { + int lim = std::min(i + 1 + neighbour, maxRows); + for (int j = i + 1; j < lim; j++) { + if (clMap[j]) { + counters.crossed++; + break; + } + } + } + } + } + } + //////////////////////////////// O2 //////////////////////////////// + bool isSelected(GID trackIndex, o2::globaltracking::RecoContainer& data) + { + o2::track::TrackParCov trk; + auto contributorsGID = data.getSingleDetectorRefs(trackIndex); + auto src = trackIndex.getSource(); // make selections depending on track source + // ITS tracks + if (contributorsGID[GIndex::Source::ITS].isIndexSet()) { // ITS tracks selection + isBarrelTrack = true; + const auto& itsTrk = data.getITSTrack(contributorsGID[GID::ITS]); + int ITSnClusters = itsTrk.getNClusters(); + float ITSchi2 = itsTrk.getChi2(); + float itsChi2NCl = ITSnClusters != 0 ? ITSchi2 / (float)ITSnClusters : 0; + uint8_t itsClusterMap = itsTrk.getPattern(); + SetRequireHitsInITSLayers(1, {0, 1, 2}); + if (itsChi2NCl >= mMaxChi2PerClusterITS || + TrackMethods::FulfillsITSHitRequirements(itsClusterMap, mRequiredITSHits) == false) { + // LOGP(info,"FAILURE hits in ITS layers"); + return false; + } + } + // TPC tracks + /* if (contributorsGID[GIndex::Source::TPC].isIndexSet()) { + LOGP(info, "****** INSIDE TPC ********"); + //countTPCClusters(data); + isBarrelTrack = true; + const auto& tpcTrk = data.getTPCTrack(contributorsGID[GID::TPC]); + const auto& tpcClData = mTPCCounters[contributorsGID[GID::TPC]]; + math_utils::Point3D v{}; + std::array dca; + int tpcNClsFindable = tpcTrk.getNClusters(); + float tpcChi2NCl = tpcTrk.getNClusters() ? tpcTrk.getChi2() / tpcTrk.getNClusters() : 0; + double tpcNClsFindableMinusFound = tpcTrk.getNClusters() - tpcClData.found; + double tpcNClsFindableMinusCrossedRows = tpcTrk.getNClusters() - tpcClData.crossed; + float tpcNClsCrossedRows = (float)((int16_t)tpcNClsFindable - tpcNClsFindableMinusCrossedRows)/tpcNClsFindable; + float tpcCrossedRowsOverFindableCl = (float)tpcNClsCrossedRows / (float)tpcNClsFindable; + if(tpcCrossedRowsOverFindableCl < 0.8) { + LOGP(info,"FAILURE tpcCrossedRowsOverFindableCl"); + return false; + } + if(tpcClData.crossed < 70){ + LOGP(info,"FAILURE crossed"); + return false; + } + if(tpcChi2NCl >= mMaxChi2PerClusterTPC){ + LOGP(info,"FAILURE tpcChi2NCl"); + return false; + } + } */ + if (isBarrelTrack) { // track selection for barrel tracks + trk = data.getTrackParam(trackIndex); + if (trk.getPt() < mMinPt && trk.getPt() > mMaxPt && trk.getEta() < mMinEta && trk.getEta() > mMaxEta) { + return false; + } + } + return true; + } + + void SetRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) + { + // layer 0 corresponds to the the innermost ITS layer + mRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); + // LOG(info) << "Track selection, set require hits in ITS layers: " << static_cast(minNRequiredHits); + } + + private: + // counters for TPC clusters + struct TPCCounters { + uint8_t shared = 0; + uint8_t found = 0; + uint8_t crossed = 0; + }; + std::vector mTPCCounters; + + bool isBarrelTrack = true; // all barrel tracks must have either ITS or TPC contribution -> true if ITS || TPC track source condition is passed + // cut values + float mPtTPCCut = 0.1f; + float mEtaTPCCut = 1.4f; + int32_t mNTPCClustersCut = 60; + float mDCACut = 100.f; + float mDCACutY = 10.f; + // kinematic cuts + float mMinPt{0.1f}, mMaxPt{1e10f}; // range in pT + float mMinEta{0.8}, mMaxEta{0.8}; // range in eta + float mBz = o2::base::Propagator::Instance()->getNominalBz(); + + float mMaxChi2PerClusterITS{36.0}; // max its fit chi2 per ITS cluster + float mMaxChi2PerClusterTPC{4.0}; // max its fit chi2 per ITS cluster + int mMinNClustersITS{0}; // min number of ITS clusters + + // vector of ITS requirements (minNRequiredHits in specific requiredLayers) + int8_t minNRequiredHits = 1; + std::set requiredLayers{0, 1, 2}; // one hit in the first three layers + std::vector>> mRequiredITSHits{}; + + ClassDefNV(TrackCuts, 1); +}; +} // namespace study +} // namespace its +} // namespace o2 + +#endif diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackExtension.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackExtension.h new file mode 100644 index 0000000000000..fd5b93b0f9509 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackExtension.h @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACK_EXTENSION_STUDY_H +#define O2_TRACK_EXTENSION_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2 +{ +namespace steer +{ +class MCKinematicsReader; +} +namespace its::study +{ +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +o2::framework::DataProcessorSpec getTrackExtensionStudy(mask_t srcTracksMask, mask_t srcClustersMask, std::shared_ptr kineReader); +} // namespace its::study + +} // namespace o2 +#endif diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackMethods.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackMethods.h new file mode 100644 index 0000000000000..55f92843cd14d --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackMethods.h @@ -0,0 +1,106 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TrackCuts.h +/// \brief Class to store some methods used in TrackCuts +/// \author antonio.palasciano@ba.infn.it + +#ifndef O2_TRACK_METHODS_STUDY_H +#define O2_TRACK_METHODS_STUDY_H + +#include "DataFormatsTPC/TrackTPC.h" +#include "GPUTPCGMMergedTrackHit.h" +#include "DataFormatsITS/TrackITS.h" +#include "ITSStudies/TrackCuts.h" +#include "ITSStudies/TrackMethods.h" +#include +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ +class TrackMethods +{ + public: + static void countTPCClusters(const o2::tpc::TrackTPC& track, + const gsl::span& tpcClusRefs, + const gsl::span& tpcClusShMap, + const o2::tpc::ClusterNativeAccess& tpcClusAcc, + uint8_t& shared, uint8_t& found, uint8_t& crossed) + { + LOGP(info, "tpcClusRefs {}/{}", (void*)tpcClusRefs.data(), tpcClusRefs.size()); + LOGP(info, "tpcClusShMap {}/{}", (void*)tpcClusShMap.data(), tpcClusShMap.size()); + LOGP(info, " tpcClusAcc{}/{}", (void*)tpcClusAcc.clustersLinear, tpcClusAcc.nClustersTotal); + constexpr int maxRows = 152; + constexpr int neighbour = 2; + std::array clMap{}, shMap{}; + uint8_t sectorIndex; + uint8_t rowIndex; + uint32_t clusterIndex; + shared = 0; + for (int i = 0; i < track.getNClusterReferences(); i++) { + o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef()); + unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex; + clMap[rowIndex] = true; + if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (!shMap[rowIndex]) { + shared++; + } + shMap[rowIndex] = true; + } + } + + crossed = 0; + found = 0; + int last = -1; + for (int i = 0; i < maxRows; i++) { + if (clMap[i]) { + crossed++; + found++; + last = i; + } else if ((i - last) <= neighbour) { + crossed++; + } else { + int lim = std::min(i + 1 + neighbour, maxRows); + for (int j = i + 1; j < lim; j++) { + if (clMap[j]) { + crossed++; + } + } + } + } + } + static bool FulfillsITSHitRequirements(uint8_t itsClusterMap, std::vector>> mRequiredITSHits) + { + constexpr uint8_t bit = 1; + for (auto& itsRequirement : mRequiredITSHits) { + auto hits = std::count_if(itsRequirement.second.begin(), itsRequirement.second.end(), [&](auto&& requiredLayer) { return itsClusterMap & (bit << requiredLayer); }); + if ((itsRequirement.first == -1) && (hits > 0)) { + return false; // no hits were required in specified layers + } else if (hits < itsRequirement.first) { + return false; // not enough hits found in specified layers + } + } + return true; + } + + private: + std::vector>> mRequiredITSHits{}; + ClassDefNV(TrackMethods, 1); +}; +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/macros/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/CMakeLists.txt new file mode 100644 index 0000000000000..2d78e4077ec53 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +# o2_add_test_root_macro( +# PostTrackExtension.C +# PUBLIC_LINK_LIBRARIES ROOT::Hist ROOT::RIO ROOT::Core ROOT::Gpad +# LABELS its-study +# COMPILE_ONLY) diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest new file mode 100644 index 0000000000000..29f94086aae4c --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest @@ -0,0 +1,629 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "TStyle.h" +#include "TFile.h" +#include "TError.h" +#include "TColor.h" +#include "TCanvas.h" +#include "TH2D.h" +#include "TF1.h" +#include "TEfficiency.h" +#include "TMarker.h" +#include "TLegend.h" +#include "TTree.h" +#include "TLatex.h" + +#include +#include +#include +#endif + +static constexpr std::array bitPatternsBefore{15, 30, 31, 60, 62, 63, 120, 124, 126}; +static constexpr std::array bitPatternsAfter{31, 47, 61, 62, 63, 79, 94, 95, 111, 121, 122, 123, 124, 125, 126, 127}; +inline bool bitsCleared(uint8_t b, uint8_t a) { return (a == b) ? true : (b & ~(a & b)) != 0; } +static constexpr std::array patternColors = { + kRed, // Red + kBlue, // Blue + kGreen, // Green + kMagenta, // Magenta + kCyan, // Cyan + kOrange, // Orange + kViolet, // Violet + kYellow, // Yellow + kPink, // Pink + kAzure, // Azure + kSpring, // Spring Green + kTeal, // Teal + kBlack, // Black + kGray, // Gray + kOrange + 7, // Light Orange + kBlue - 9 // Light Blue +}; + +// Marker styles +static constexpr std::array patternMarkers = { + 20, // Full circle + 21, // Full square + 22, // Full triangle up + 23, // Full triangle down + 24, // Open circle + 25, // Open square + 26, // Open triangle up + 27, // Open cross + 28, // Star + 29, // Plus sign + 30, // Open diamond + 31, // Full diamond + 32, // Cross + 33, // Circle with cross + 34, // X sign + 35 // Double open cross +}; + +enum Labels : unsigned int { + eAll = 0, + eGood, + eFake, + eFakeBefore, + eFakeAfter, + eFakeMix, + eTopGood, + eBotGood, + eMixGood, + eTopFake, + eBotFake, + eMixFake, + eN, +}; +static const std::array names{ + "ALL #frac{ext trks}{all trks}", + "GOOD #frac{good ext trks}{all ext trks}", + "FAKE #frac{fake trks}{all ext trks}", + "FAKE BF #frac{fake bf trks}{fake ext trks}", + "FAKE AF #frac{fake af trks}{fake ext trks}", + "FAKE MIX #frac{fake mix trks}{fake ext trks}", + // Good Top/Bot/Mix + "TOP #frac{good top ext trks}{good ext trks}", + "BOT #frac{good bot ext trks}{good ext trks}", + "MIX #frac{good mix ext trks}{good ext trks}", + // Fake Top/Bot/Mix + "TOP #frac{fake top ext trks}{fake ext trks}", + "BOT #frac{fake bot ext trks}{fake ext trks}", + "MIX #frac{fake mix ext trks}{fake ext trks}", +}; +static const std::array colors{kBlack, kGreen, kRed, kCyan, kYellow, kAzure, + // Good Top/Bot/Mix + kBlue, kOrange, kPink, + // Fake Top/Bot/Mix + kBlue, kOrange, kPink}; +static const std::array markers{20, 21, 22, 23, 27, 28, + // Good Top/Bot/Mix + 29, 33, 39, + // Fake Top/Bot/Mix + 29, 33, 39}; +static const char* const texPtX = "#it{p}_{T} (GeV/#it{c})"; +static const char* const texEff = "Efficiency"; +static const char* const texPtRes = "#sigma(#Delta#it{p}_{T}/#it{p}_{T})"; +static const char* const texDCAxyRes = "#sigma(DCA_{#it{xy}}) (#mum)"; +static const char* const texDCAzRes = "#sigma(DCA_{#it{z}}) (#mum)"; +static const char* const fitOpt{"QWMER"}; + +void setStyle(); +TEfficiency* makeEff(TFile*, const char* num, const char* den); + +template +void style(T* t, Labels lab, TLegend* leg = nullptr) +{ + t->SetMarkerStyle(markers[lab]); + t->SetMarkerColor(colors[lab]); + t->SetLineColor(colors[lab]); + if (leg) { + leg->AddEntry(t, names[lab]); + } +} + +template +void stylePattern(T* t, int i, TLegend* leg = nullptr, const char* name = nullptr) +{ + t->SetMarkerStyle(patternMarkers[i]); + t->SetMarkerColor(patternColors[i]); + t->SetLineColor(patternColors[i]); + if (leg) { + leg->AddEntry(t, name); + } +} + +void PostTrackExtension(const char* fileName = "TrackExtensionStudy.root") +{ + setStyle(); + + std::unique_ptr fIn{TFile::Open(fileName, "READ")}; + if (!fIn || fIn->IsZombie()) { + Error("", "Cannot open file %s", fileName); + return; + } + + { // Purity & Fake-Rate + auto c = new TCanvas("cPFR", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto eff = fIn->Get("eExtension"); + style(eff, eAll, leg); + eff->Draw("same"); + auto effPurity = fIn->Get("eExtensionPurity"); + style(effPurity, eGood, leg); + effPurity->Draw("same"); + auto effFake = fIn->Get("eExtensionFake"); + style(effFake, eFake, leg); + effFake->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_purity_fake.pdf"); + } + + { // FAKE-Rate composition + auto c = new TCanvas("cFR", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto effFake = fIn->Get("eExtensionFake"); + style(effFake, eFake, leg); + effFake->Draw("same"); + auto effFakeBf = fIn->Get("eExtensionFakeBefore"); + style(effFakeBf, eFakeBefore, leg); + effFakeBf->Draw("same"); + auto effFakeAf = fIn->Get("eExtensionFakeAfter"); + style(effFakeAf, eFakeAfter, leg); + effFakeAf->Draw("same"); + auto effFakeMi = fIn->Get("eExtensionFakeMix"); + style(effFakeMi, eFakeMix, leg); + effFakeMi->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_fake.pdf"); + } + + { // GOOD Top/Bot/Mix Purity composition + auto c = new TCanvas("cGC", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto effTop = makeEff(fIn.get(), "eExtensionTopPurity", "eExtensionPurity"); + style(effTop, eTopGood, leg); + effTop->Draw("same"); + auto effBot = makeEff(fIn.get(), "eExtensionBotPurity", "eExtensionPurity"); + style(effBot, eBotGood, leg); + effBot->Draw("same"); + auto effMix = makeEff(fIn.get(), "eExtensionMixPurity", "eExtensionPurity"); + style(effMix, eMixGood, leg); + effMix->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_good_comp.pdf"); + } + + { // FAKE Top/Bot/Mix composition + auto c = new TCanvas("cFC", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto effTop = fIn->Get("eExtensionTopFake"); + style(effTop, eTopFake, leg); + effTop->Draw("same"); + auto effBot = fIn->Get("eExtensionBotFake"); + style(effBot, eBotFake, leg); + effBot->Draw("same"); + auto effMix = fIn->Get("eExtensionMixFake"); + style(effMix, eMixFake, leg); + effMix->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_fake_comp.pdf"); + } + + { // Good Patterns + auto c = new TCanvas("cPatGood", "", 3 * 800, 3 * 600); + c->Divide(3, 3); + for (int i{0}; i < (int)bitPatternsBefore.size(); ++i) { + auto p = c->cd(i + 1); + auto h = p->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.60, 0.7, 0.88); + leg->SetNColumns(4); + leg->SetHeader(std::format("BEFORE={:07b} GOOD Pattern AFTER/BEFORE", bitPatternsBefore[i]).c_str()); + for (int j{0}; j < (int)bitPatternsAfter.size(); ++j) { + if (bitsCleared(bitPatternsBefore[i], bitPatternsAfter[j])) { + continue; + } + auto eff = fIn->Get(std::format("eExtensionPatternGood_{:07b}_{:07b}", bitPatternsBefore[i], bitPatternsAfter[j]).c_str()); + stylePattern(eff, j, leg, std::format("{:07b}", bitPatternsAfter[j]).c_str()); + eff->Draw("same"); + } + leg->Draw(); + p->SetLogx(); + p->SetGrid(); + } + c->SaveAs("trkExt_good_pattern_comp.pdf"); + } + + { // Fake Patterns + auto c = new TCanvas("cPatFake", "", 3 * 800, 3 * 600); + c->Divide(3, 3); + for (int i{0}; i < (int)bitPatternsBefore.size(); ++i) { + auto p = c->cd(i + 1); + auto h = p->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.60, 0.7, 0.88); + leg->SetNColumns(4); + leg->SetHeader(std::format("BEFORE={:07b} FAKE Pattern AFTER/BEFORE", bitPatternsBefore[i]).c_str()); + for (int j{0}; j < (int)bitPatternsAfter.size(); ++j) { + if (bitsCleared(bitPatternsBefore[i], bitPatternsAfter[j])) { + continue; + } + auto eff = fIn->Get(std::format("eExtensionPatternFake_{:07b}_{:07b}", bitPatternsBefore[i], bitPatternsAfter[j]).c_str()); + stylePattern(eff, j, leg, std::format("{:07b}", bitPatternsAfter[j]).c_str()); + eff->Draw("same"); + } + leg->Draw(); + p->SetLogx(); + p->SetGrid(); + } + c->SaveAs("trkExt_fake_pattern_comp.pdf"); + } + + { // DCA + auto fGaus = new TF1("fGaus", "gaus", -200., 200.); + auto dcaXYVsPtNo = fIn->Get("hDCAxyVsPtResNormal"); + auto dcaXYVsPtYes = fIn->Get("hDCAxyVsPtResExtended"); + auto dcazVsPtNo = fIn->Get("hDCAzVsPtResNormal"); + auto dcazVsPtYes = fIn->Get("hDCAzVsPtResExtended"); + auto bins = dcazVsPtNo->GetXaxis()->GetXbins(); + auto dcaXYResNo = new TH1F("hDcaxyResNo", "NORMAL;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + auto dcaXYResYes = new TH1F("hDcaxyResYes", "EXTENDED;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + auto dcaZResNo = new TH1F("hDcazResNo", "NORMAL;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + auto dcaZResYes = new TH1F("hDcazResYes", "EXTENDED;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + TH1* proj; + for (int iPt{1}; iPt <= bins->GetSize(); ++iPt) { + auto ptMin = dcaXYResNo->GetXaxis()->GetBinLowEdge(iPt); + if (ptMin < 0.1) { + continue; + } + float minFit = (ptMin < 1.) ? -200. : -75.; + float maxFit = (ptMin < 1.) ? 200. : 75.; + + proj = dcaXYVsPtNo->ProjectionY(Form("hProjDCAxy_no_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaXYResNo->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaXYResNo->SetBinError(iPt, fGaus->GetParError(2)); + + proj = dcaXYVsPtYes->ProjectionY(Form("hProjDCAxy_yes_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaXYResYes->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaXYResYes->SetBinError(iPt, fGaus->GetParError(2)); + + proj = dcazVsPtNo->ProjectionY(Form("hProjDCAz_no_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaZResNo->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaZResNo->SetBinError(iPt, fGaus->GetParError(2)); + + proj = dcazVsPtYes->ProjectionY(Form("hProjDCAz_yes_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaZResYes->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaZResYes->SetBinError(iPt, fGaus->GetParError(2)); + } + + dcaXYResNo->SetLineColor(kRed); + dcaXYResNo->SetMarkerColor(kRed); + dcaXYResYes->SetLineColor(kBlue); + dcaXYResYes->SetMarkerColor(kBlue); + dcaZResNo->SetLineColor(kRed); + dcaZResNo->SetMarkerColor(kRed); + dcaZResYes->SetLineColor(kBlue); + dcaZResYes->SetMarkerColor(kBlue); + + auto c = new TCanvas("cDCA", "", 2 * 800, 600); + c->Divide(2, 1); + c->cd(1); + auto h = gPad->DrawFrame(0.1, 1, 10., 500); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texDCAxyRes); + dcaXYResNo->Draw("SAME"); + dcaXYResYes->Draw("SAME"); + gPad->SetLogx(); + gPad->SetLogy(); + gPad->SetGrid(); + auto leg = new TLegend(0.20, 0.20, 0.40, 0.40); + leg->AddEntry(dcaXYResNo, "Normal"); + leg->AddEntry(dcaXYResYes, "Extended"); + leg->Draw(); + + c->cd(2); + h = gPad->DrawFrame(0.1, 1, 10., 500); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texDCAzRes); + dcaZResNo->Draw("SAME"); + dcaZResYes->Draw("SAME"); + gPad->SetLogx(); + gPad->SetLogy(); + gPad->SetGrid(); + + c->SaveAs("trkExt_dca.pdf"); + } + + return; + { // Kinematic variables + auto t = fIn->Get("tree"); + auto c = new TCanvas("cKG", "", 800, 600); + c->Divide(3, 2); + { + auto p = c->cd(1); + p->SetGrid(); + auto h = p->DrawFrame(-.6, 0., .6, 9.); + h->GetXaxis()->SetTitle("#frac{Q^{2}}{p_{T,TRK}}-#frac{Q^{2}}{p_{T,MC}}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getQ2Pt()-mcTrk.getQ2Pt()>>hPtNo(100,-.6,.6)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hPtNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.04, 0.04); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-0.55, 8.2, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getQ2Pt()-mcTrk.getQ2Pt()>>hPtYes(100,-.6,.6)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hPtYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.04, 0.04); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-0.55, 7, Form("#mu = %.4f, #sigma = %.4f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(2); + p->SetGrid(); + auto h = p->DrawFrame(-3, 0., 3, 2.); + h->GetXaxis()->SetTitle("Y_{TRK}-Y_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getY()-mcTrk.getY()>>hYNo(100,-3,3)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hYNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.5, 0.5); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-2, 1.7, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getY()-mcTrk.getY()>>hYYes(100,-3,3)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hYYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.5, 0.5); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-2, 1.5, Form("#mu = %.4f, #sigma = %.4f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(3); + p->SetGrid(); + auto h = p->DrawFrame(-2, 0., 2, 4.2); + h->GetXaxis()->SetTitle("Z_{TRK}-Z_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getZ()-mcTrk.getZ()>>hZNo(100,-2,2)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hZNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.2, 0.2); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-1.7, 3.8, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getZ()-mcTrk.getZ()>>hZYes(100,-2,2)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hZYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.2, 0.2); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-1.7, 3.5, Form("#mu = %.4f, #sigma = %.4f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(4); + p->SetGrid(); + auto h = p->DrawFrame(-0.02, 0., 0.02, 370.); + h->GetXaxis()->SetTitle("TGL_{TRK}-TGL_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getTgl()-mcTrk.getTgl()>>hTglNo(100,-0.02,0.02)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hTglNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.003, 0.003); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-0.018, 330, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getTgl()-mcTrk.getTgl()>>hTglYes(100,-0.02,0.02)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hTglYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.003, 0.003); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-0.018, 310, Form("#mu = %.6f, #sigma = %.6f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(5); + p->SetGrid(); + auto h = p->DrawFrame(-0.08, 0., 0.08, 80.); + h->GetXaxis()->SetTitle("SNP_{TRK}-SNP_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getSnp()-mcTrk.getSnp()>>hSnpNo(100,-0.08,0.08)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hSnpNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.03, 0.03); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-0.07, 72, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getSnp()-mcTrk.getSnp()>>hSnpYes(100,-0.08,0.08)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hSnpYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.03, 0.03); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-0.07, 66, Form("#mu = %.6f, #sigma = %.6f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(6); + auto legend = new TLegend(0.2, 0.2, 0.8, 0.8); + legend->SetTextSize(0.06); + legend->SetLineWidth(3); + legend->SetHeader("GOOD tracks", "C"); + auto mBlue = new TMarker(); + mBlue->SetMarkerColor(kBlue); + mBlue->SetMarkerSize(4); + legend->AddEntry(mBlue, "extended", "p"); + auto mRed = new TMarker(); + mRed->SetMarkerColor(kRed); + mRed->SetMarkerSize(4); + legend->AddEntry(mRed, "normal", "p"); + legend->SetLineColor(kRed); + legend->Draw(); + } + c->SaveAs("trkExt_kinematics.pdf"); + } +} + +void setStyle() +{ + gStyle->Reset("Plain"); + gStyle->SetOptTitle(0); + gStyle->SetOptStat(0); + gStyle->SetPalette(kRainBow); + gStyle->SetCanvasColor(10); + gStyle->SetCanvasBorderMode(0); + gStyle->SetFrameLineWidth(1); + gStyle->SetFrameFillColor(kWhite); + gStyle->SetPadColor(10); + gStyle->SetPadTickX(1); + gStyle->SetPadTickY(1); + gStyle->SetPadBottomMargin(0.15); + gStyle->SetPadLeftMargin(0.15); + gStyle->SetHistLineWidth(1); + gStyle->SetHistLineColor(kRed); + gStyle->SetFuncWidth(2); + gStyle->SetFuncColor(kGreen); + gStyle->SetLineWidth(2); + gStyle->SetLabelSize(0.045, "xyz"); + gStyle->SetLabelOffset(0.01, "y"); + gStyle->SetLabelOffset(0.01, "x"); + gStyle->SetLabelColor(kBlack, "xyz"); + gStyle->SetTitleSize(0.05, "xyz"); + gStyle->SetTitleOffset(1.25, "y"); + gStyle->SetTitleOffset(1.2, "x"); + gStyle->SetTitleFillColor(kWhite); + gStyle->SetTextSizePixels(26); + gStyle->SetTextFont(42); + gStyle->SetTickLength(0.04, "X"); + gStyle->SetTickLength(0.04, "Y"); + gStyle->SetLegendBorderSize(0); + gStyle->SetLegendFillColor(kWhite); + gStyle->SetFillColor(kWhite); + gStyle->SetLegendFont(42); +} + +TEfficiency* makeEff(TFile* fIn, const char* num, const char* den) +{ + auto h1 = fIn->Get(num)->GetPassedHistogram(); + auto h2 = fIn->Get(den)->GetPassedHistogram(); + auto e = new TEfficiency(*h1, *h2); + return e; +} diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/AnomalyStudy.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/AnomalyStudy.cxx new file mode 100644 index 0000000000000..c56c2bff2e0d2 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/AnomalyStudy.cxx @@ -0,0 +1,288 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITSStudies/AnomalyStudy.h" +#include "ITSStudies/ITSStudiesConfigParam.h" +#include "ITSBase/GeometryTGeo.h" + +#include "Framework/Task.h" +#include "ITStracking/IOUtils.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" + +#include +#include +#include +#include + +namespace o2::its::study +{ +using namespace o2::framework; +using namespace o2::globaltracking; +using GTrackID = o2::dataformats::GlobalTrackID; +using ITSCluster = o2::BaseCluster; +class AnomalyStudy : public Task +{ + static constexpr int nChipStavesIB{9}; + + public: + AnomalyStudy(std::shared_ptr dr, + std::shared_ptr gr, + bool isMC) : mDataRequest{dr}, mGGCCDBRequest(gr), mUseMC(isMC) {} + void init(InitContext& ic) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + + // custom + void prepareOutput(); + void updateTimeDependentParams(ProcessingContext& pc); + void process(o2::globaltracking::RecoContainer& recoData); + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + + private: + bool mUseMC; + int mTFCount{0}; + TStopwatch mStopwatch; + const int mNumberOfStaves[7] = {12, 16, 20, 24, 30, 42, 48}; + std::shared_ptr mGGCCDBRequest; + std::shared_ptr mDataRequest; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + + // utils + void getClusterPatterns(gsl::span&, gsl::span&, const o2::itsmft::TopologyDictionary&); + std::vector mPatterns; + o2::its::GeometryTGeo* mGeom; + o2::itsmft::ChipMappingITS mChipMapping; + + // Histos + std::vector> mTFvsPhiHist; + std::vector> mTFvsPhiClusSizeHist; + std::vector> mROFvsPhiHist; + std::vector> mROFvsPhiClusSizeHist; +}; + +void AnomalyStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this param need to be queried only once + initOnceDone = true; + mGeom = o2::its::GeometryTGeo::Instance(); + mGeom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void AnomalyStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + prepareOutput(); + + auto nLayProc = o2::its::study::AnomalyStudyParamConfig::Instance().nLayersToProcess; + auto nTF = o2::its::study::AnomalyStudyParamConfig::Instance().nTimeFramesOffset; + auto nROF = o2::its::study::AnomalyStudyParamConfig::Instance().nRofTimeFrames; + auto doROFAnalysis = o2::its::study::AnomalyStudyParamConfig::Instance().doROFAnalysis; + + mTFvsPhiHist.resize(nLayProc); + mTFvsPhiClusSizeHist.resize(nLayProc); + if (doROFAnalysis) { + mROFvsPhiHist.resize(nLayProc); + mROFvsPhiClusSizeHist.resize(nLayProc); + } + for (unsigned int i = 0; i < nLayProc; i++) { + int phiBins = o2::its::study::AnomalyStudyParamConfig::Instance().nPhiBinsMultiplier * mNumberOfStaves[i]; + mTFvsPhiHist[i].reset(new TH2F(Form("tf_phi_occup_layer_%d", i), Form("Occupancy layer %d ; #phi ; # TF; Counts", i), phiBins, -TMath::Pi(), TMath::Pi(), nTF, 0.5, nTF + 0.5)); + mTFvsPhiClusSizeHist[i].reset(new TH2F(Form("tf_phi_clsize_layer_%d", i), Form("Cluster size layer %d ; #phi; # TF ; #lt Cluster Size #gt", i), phiBins, -TMath::Pi(), TMath::Pi(), nTF, 0.5, nTF + 0.5)); + if (doROFAnalysis) { + mROFvsPhiHist[i].reset(new TH2F(Form("rof_phi_occup_layer_%d", i), Form("Occupancy layer %d ; #phi; # ROF; Counts", i), phiBins, -TMath::Pi(), TMath::Pi(), nROF * nTF, 0.5, nROF * nTF + 0.5)); + mROFvsPhiClusSizeHist[i].reset(new TH2F(Form("rof_phi_clsize_layer_%d", i), Form("Cluster size layer %d; #phi; # ROF; #lt Cluster Size #gt", i), phiBins, -TMath::Pi(), TMath::Pi(), nROF * nTF, 0.5, nROF * nTF + 0.5)); + } + } +} + +void AnomalyStudy::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(recoData); +} + +void AnomalyStudy::endOfStream(EndOfStreamContext&) +{ + TFile* f = TFile::Open(o2::its::study::AnomalyStudyParamConfig::Instance().outFileName.c_str(), "recreate"); + auto nLayProc = o2::its::study::AnomalyStudyParamConfig::Instance().nLayersToProcess; + auto doROFAnalysis = o2::its::study::AnomalyStudyParamConfig::Instance().doROFAnalysis; + + // Iterate over all the histograms and compute the averages + for (unsigned int i = 0; i < nLayProc; i++) { + mTFvsPhiClusSizeHist[i]->Divide(mTFvsPhiHist[i].get()); + if (doROFAnalysis) { + mROFvsPhiClusSizeHist[i]->Divide(mROFvsPhiHist[i].get()); + } + } + + // Fit slices along x of the 2D histograms + for (unsigned int iLayer = 0; iLayer < nLayProc; ++iLayer) { + int phiBins = o2::its::study::AnomalyStudyParamConfig::Instance().nPhiBinsMultiplier * mNumberOfStaves[iLayer]; + TObjArray aSlices; + auto* f1 = new TF1(Form("f1_%d", iLayer), "pol0", -TMath::Pi(), TMath::Pi()); + auto* hPValue = new TH1F(Form("pValue_%d", iLayer), Form("pValue_%d", iLayer), mTFvsPhiClusSizeHist[iLayer]->GetNbinsY(), 0.5, mTFvsPhiClusSizeHist[iLayer]->GetNbinsY() + 0.5); + mTFvsPhiClusSizeHist[iLayer]->FitSlicesX(f1, 0, -1, 0, "QNR", &aSlices); + auto* hChi2 = (TH1D*)aSlices.At(1); + for (auto iTF{0}; iTF < hChi2->GetEntries(); ++iTF) { + auto pValue = TMath::Prob(hChi2->GetBinContent(iTF + 1) * (phiBins - 1), phiBins - 1); + // LOGP(info, "Layer: {} TF: {} Chi2: {} Pvalue: {}", iLayer, iTF, hChi2->GetBinContent(iTF + 1), pValue); + hPValue->SetBinContent(iTF + 1, pValue); + } + hPValue->Write(); + // Save slices to file + for (unsigned int j = 0; j < aSlices.GetEntries(); ++j) { + auto h = (TH1D*)aSlices.At(j); + h->SetMinimum(0); + h->Write(); + } + + // Do the same for ROFs + if (doROFAnalysis) { + TObjArray aSlicesROF; + auto f1ROF = new TF1(Form("f1ROF_%d", iLayer), "pol0", -TMath::Pi(), TMath::Pi()); + mROFvsPhiClusSizeHist[iLayer]->FitSlicesX(f1ROF, 0, -1, 0, "QNR", &aSlicesROF); + // Save slices to file + for (unsigned int j = 0; j < aSlicesROF.GetEntries(); ++j) { + auto h = (TH1D*)aSlicesROF.At(j); + h->SetMinimum(0); + h->Write(); + } + } + } + + for (unsigned int i = 0; i < nLayProc; i++) { + mTFvsPhiHist[i]->Write(); + mTFvsPhiClusSizeHist[i]->Write(); + if (doROFAnalysis) { + mROFvsPhiHist[i]->Write(); + mROFvsPhiClusSizeHist[i]->Write(); + } + } + f->Close(); +} + +void AnomalyStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); + return; + } +} + +// custom +void AnomalyStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + mStopwatch.Start(); + mTFCount++; + auto nROF = o2::its::study::AnomalyStudyParamConfig::Instance().nRofTimeFrames; + auto nLayProc = o2::its::study::AnomalyStudyParamConfig::Instance().nLayersToProcess; + auto doROFAnalysis = o2::its::study::AnomalyStudyParamConfig::Instance().doROFAnalysis; + int rofCount = 0; + auto clusRofRecords = recoData.getITSClustersROFRecords(); + auto compClus = recoData.getITSClusters(); + auto clusPatt = recoData.getITSClustersPatterns(); + + getClusterPatterns(compClus, clusPatt, *mDict); + + auto pattIt = clusPatt.begin(); + std::vector globalClusters; + o2::its::ioutils::convertCompactClusters(compClus, pattIt, globalClusters, mDict); + + int lay, sta, ssta, mod, chipInMod; + for (auto& rofRecord : clusRofRecords) { + auto clustersInRof = rofRecord.getROFData(compClus); + auto patternsInRof = rofRecord.getROFData(mPatterns); + auto locClustersInRof = rofRecord.getROFData(globalClusters); + for (unsigned int clusInd{0}; clusInd < clustersInRof.size(); clusInd++) { + const auto& compClus = clustersInRof[clusInd]; + auto& locClus = locClustersInRof[clusInd]; + auto& clusPattern = patternsInRof[clusInd]; + auto gloC = locClus.getXYZGlo(*mGeom); + mChipMapping.expandChipInfoHW(compClus.getChipID(), lay, sta, ssta, mod, chipInMod); + if (lay >= nLayProc) { + continue; + } + float phi = TMath::ATan2(gloC.Y(), gloC.X()); + mTFvsPhiHist[lay]->Fill(phi, mTFCount); + mTFvsPhiClusSizeHist[lay]->Fill(phi, mTFCount, clusPattern.getNPixels()); + if (doROFAnalysis) { + mROFvsPhiHist[lay]->Fill(phi, (mTFCount - 1) * nROF + rofCount); + mROFvsPhiClusSizeHist[lay]->Fill(phi, (mTFCount - 1) * nROF + rofCount, clusPattern.getNPixels()); + } + } + ++rofCount; + } + mStopwatch.Stop(); + LOGP(info, "Processed TF: {} in {} s", mTFCount, mStopwatch.RealTime()); +} + +void AnomalyStudy::prepareOutput() +{ +} + +void AnomalyStudy::getClusterPatterns(gsl::span& ITSclus, gsl::span& ITSpatt, const o2::itsmft::TopologyDictionary& mdict) +{ + mPatterns.clear(); + mPatterns.reserve(ITSclus.size()); + auto pattIt = ITSpatt.begin(); + + for (unsigned int iClus{0}; iClus < ITSclus.size(); ++iClus) { + auto& clus = ITSclus[iClus]; + + auto pattID = clus.getPatternID(); + o2::itsmft::ClusterPattern patt; + + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mdict.isGroup(pattID)) { + patt.acquirePattern(pattIt); + } else { + patt = mdict.getPattern(pattID); + } + + mPatterns.push_back(patt); + } +} + +// getter +DataProcessorSpec getAnomalyStudy(mask_t srcClustersMask, bool useMC) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestClusters(srcClustersMask, useMC); + dataRequest->requestTracks(GTrackID::getSourcesMask(""), useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + return DataProcessorSpec{ + "its-anomaly-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, + Options{}}; +} +} // namespace o2::its::study \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/AvgClusSize.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/AvgClusSize.cxx new file mode 100644 index 0000000000000..f7efae8677516 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/AvgClusSize.cxx @@ -0,0 +1,676 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AvgClusSize.cxx +/// \brief Study to calculate average cluster size per track in the ITS +/// \author Tucker Hwang mhwang@cern.ch + +#include "ITSStudies/AvgClusSize.h" +#include "ITSStudies/ITSStudiesConfigParam.h" + +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "Steer/MCKinematicsReader.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "ITStracking/IOUtils.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/PID.h" +#include "ReconstructionDataFormats/V0.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/DCA.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "DetectorsCommonDataFormats/DetID.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; +using PVertex = o2::dataformats::PrimaryVertex; +using V0 = o2::dataformats::V0; +using ITSCluster = o2::BaseCluster; +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +using Track = o2::track::TrackParCov; +using TrackITS = o2::its::TrackITS; +using DCA = o2::dataformats::DCA; +using PID = o2::track::PID; + +class AvgClusSizeStudy final : public Task +{ + public: + AvgClusSizeStudy(std::shared_ptr dr, + std::shared_ptr gr, + bool isMC, + std::shared_ptr kineReader) : mDataRequest{dr}, mGGCCDBRequest(gr), mUseMC(isMC), mKineReader(kineReader){}; + ~AvgClusSizeStudy() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + + private: + // Other functions + void process(o2::globaltracking::RecoContainer&); + void loadData(o2::globaltracking::RecoContainer&); + + // Helper functions + void prepareOutput(); + void setStyle(); + void updateTimeDependentParams(ProcessingContext& pc); + float getAverageClusterSize(o2::its::TrackITS*); + float calcV0HypoMass(const V0&, PID, PID); + void calcAPVars(const V0&, float*, float*); + void getClusterSizes(std::vector&, const gsl::span, gsl::span::iterator&, const o2::itsmft::TopologyDictionary*); + void saveHistograms(); + void plotHistograms(); + void fillEtaBin(float eta, float clusSize, int i); + + // Running options + bool mUseMC; + + // Data + std::shared_ptr mGGCCDBRequest; + std::shared_ptr mDataRequest; + std::vector mClusterSizes; + gsl::span mInputITSidxs; + std::vector mMCTracks; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + + // Output plots + std::unique_ptr mDBGOut; + std::unique_ptr mOutputNtupleAll; + std::unique_ptr mOutputNtupleCut; + + std::unique_ptr mMCR{}; + std::unique_ptr mMCCosPA{}; + std::unique_ptr mMCPosACS{}; + std::unique_ptr mMCNegACS{}; + std::unique_ptr mMCDauDCA{}; + std::unique_ptr mMCPosPVDCA{}; + std::unique_ptr mMCNegPVDCA{}; + std::unique_ptr mMCV0PVDCA{}; + + std::unique_ptr mMCRisTg{}; + std::unique_ptr mMCRisBg{}; + std::unique_ptr mMCCosPAisTg{}; + std::unique_ptr mMCCosPAisBg{}; + std::unique_ptr mMCPosACSisTg{}; + std::unique_ptr mMCPosACSisBg{}; + std::unique_ptr mMCNegACSisTg{}; + std::unique_ptr mMCNegACSisBg{}; + std::unique_ptr mMCDauDCAisTg{}; + std::unique_ptr mMCDauDCAisBg{}; + std::unique_ptr mMCPosPVDCAisTg{}; + std::unique_ptr mMCPosPVDCAisBg{}; + std::unique_ptr mMCNegPVDCAisTg{}; + std::unique_ptr mMCNegPVDCAisBg{}; + std::unique_ptr mMCV0PVDCAisTg{}; + std::unique_ptr mMCV0PVDCAisBg{}; + + std::unique_ptr mMCArmPodolisTg{}; + std::unique_ptr mMCArmPodolisBg{}; + + std::unique_ptr mAvgClusSizeCEta{}; + std::vector> mAvgClusSizeCEtaVec{}; + + std::vector mEtaBinUL; // upper edges for eta bins + + // Counters for target V0 identification + int nNotValid = 0; + int nNullptrs = 0; + int nMotherIDMismatch = 0; + int nEvIDMismatch = 0; + int nTargetV0 = 0; + int nNotTargetV0 = 0; + int nV0OutOfEtaRange = 0; + + std::string mOutName; + std::shared_ptr mKineReader; +}; + +void AvgClusSizeStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + LOGP(info, "Starting average cluster size study..."); + + prepareOutput(); + + if (mUseMC) { // for counting the missed K0shorts + mKineReader = std::make_unique("collisioncontext.root"); + for (int iEvent{0}; iEvent < mKineReader->getNEvents(0); iEvent++) { + auto mctrk = mKineReader->getTracks(0, iEvent); + mMCTracks.insert(mMCTracks.end(), mctrk.begin(), mctrk.end()); + } + } + + LOGP(info, "Cluster size study initialized."); +} + +void AvgClusSizeStudy::prepareOutput() +{ + auto& params = o2::its::study::ITSAvgClusSizeParamConfig::Instance(); + mOutName = params.outFileName; + mDBGOut = std::make_unique(mOutName.c_str(), "recreate"); + mOutputNtupleAll = std::make_unique("v0_data", "v0 data", "dPosACS:dNegACS:cosPA:V0R:eta:dauDCA:dPospvDCA:dNegpvDCA:v0pvDCA:alpha:pT:K0mass:lambdaMass:antilambdaMass:v0PDGcode"); + mOutputNtupleCut = std::make_unique("cut_v0_data", "v0 data (cut)", "dPosACS:dNegACS:cosPA:V0R:eta:dauDCA:dPospvDCA:dNegpvDCA:v0pvDCA:alpha:pT:K0mass:lambdaMass:antilambdaMass:v0PDGcode"); + + mMCR = std::make_unique("R", "V0 decay length R;R (cm?)"); + mMCCosPA = std::make_unique("cosPA", "cos(#theta_{p})"); + mMCPosACS = std::make_unique("acsPos", "Average cluster size per track;pixels / cluster / track"); + mMCNegACS = std::make_unique("acsNeg", "Average cluster size per track;pixels / cluster / track"); + mMCDauDCA = std::make_unique("dauDCA", "Prong-prong DCA;cm?"); + mMCPosPVDCA = std::make_unique("posPVDCA", "Positive prong-primary vertex DCA;cm?"); + mMCNegPVDCA = std::make_unique("negPVDCA", "Negative prong-primary vertex DCA;cm?"); + mMCV0PVDCA = std::make_unique("v0PVDCA", "V0 reconstructed track-primary vertex DCA;cm?"); + + mMCRisTg = std::make_unique("mcRTg", "target V0", 40, 0, 2); + mMCRisBg = std::make_unique("mcRBg", "background", 100, 0, 2); + mMCCosPAisTg = std::make_unique("mcCosPATg", "target V0", 40, 0, 1); + mMCCosPAisBg = std::make_unique("mcCosPABg", "background", 100, 0, 1); + mMCPosACSisTg = std::make_unique("mcPosACSTg", "target V0", 60, 0, 30); + mMCPosACSisBg = std::make_unique("mcPosACSBg", "background", 150, 0, 30); + mMCNegACSisTg = std::make_unique("mcNegACSTg", "target V0", 60, 0, 30); + mMCNegACSisBg = std::make_unique("mcNegACSBg", "background", 150, 0, 30); + mMCDauDCAisTg = std::make_unique("mcDauDCATg", "target V0", 40, 0, 2); + mMCDauDCAisBg = std::make_unique("mcDauDCABg", "background", 100, 0, 2); + mMCPosPVDCAisTg = std::make_unique("mcPosPVDCATg", "target V0", 40, 0, 15); + mMCPosPVDCAisBg = std::make_unique("mcPosPVDCABg", "background", 100, 0, 15); + mMCNegPVDCAisTg = std::make_unique("mcNegPVDCATg", "target V0", 40, 0, 15); + mMCNegPVDCAisBg = std::make_unique("mcNegPVDCABg", "background", 100, 0, 15); + mMCV0PVDCAisTg = std::make_unique("mcV0PVDCATg", "target V0", 40, 0, 15); + mMCV0PVDCAisBg = std::make_unique("mcV0PVDCABg", "background", 100, 0, 15); + + mMCArmPodolisTg = std::make_unique("apPlotTg", "Armenteros-Podolanski;#alpha_{Arm};p_{T}^{Arm} (GeV/c)", 150, -2, 2, 150, 0, 0.5); + mMCArmPodolisBg = std::make_unique("apPlotBg", "Armenteros-Podolanski;#alpha_{Arm};p_{T}^{Arm} (GeV/c)", 150, -2, 2, 150, 0, 0.5); + + mAvgClusSizeCEta = std::make_unique("avgclussizeeta", "Average cluster size per track;pixels / cluster / track"); // auto-set axis ranges + float binWidth = (params.etaMax - params.etaMin) / (float)params.etaNBins; + mEtaBinUL.reserve(params.etaNBins); + for (int i = 0; i < params.etaNBins; i++) { + mEtaBinUL.emplace_back(params.etaMin + (binWidth * (i + 1))); + mAvgClusSizeCEtaVec.push_back(std::make_unique(Form("avgclussize%i", i), Form("%.2f < #eta < %.2f", mEtaBinUL[i] - binWidth, mEtaBinUL[i]), params.sizeNBins, 0, params.sizeMax)); + mAvgClusSizeCEtaVec[i]->SetDirectory(nullptr); + mAvgClusSizeCEta->Add(mAvgClusSizeCEtaVec[i].get()); + } + + mMCRisTg->SetDirectory(nullptr); + mMCRisBg->SetDirectory(nullptr); + mMCCosPAisTg->SetDirectory(nullptr); + mMCCosPAisBg->SetDirectory(nullptr); + mMCDauDCAisTg->SetDirectory(nullptr); + mMCDauDCAisBg->SetDirectory(nullptr); + mMCPosACSisTg->SetDirectory(nullptr); + mMCPosACSisBg->SetDirectory(nullptr); + mMCNegACSisTg->SetDirectory(nullptr); + mMCNegACSisBg->SetDirectory(nullptr); + mMCPosPVDCAisTg->SetDirectory(nullptr); + mMCPosPVDCAisBg->SetDirectory(nullptr); + mMCNegPVDCAisTg->SetDirectory(nullptr); + mMCNegPVDCAisBg->SetDirectory(nullptr); + mMCV0PVDCAisTg->SetDirectory(nullptr); + mMCV0PVDCAisBg->SetDirectory(nullptr); + + mMCArmPodolisTg->SetDirectory(nullptr); + mMCArmPodolisBg->SetDirectory(nullptr); + + mOutputNtupleAll->SetDirectory(nullptr); + mOutputNtupleCut->SetDirectory(nullptr); + + mMCR->Add(mMCRisTg.get()); + mMCR->Add(mMCRisBg.get()); + mMCCosPA->Add(mMCCosPAisTg.get()); + mMCCosPA->Add(mMCCosPAisBg.get()); + mMCPosACS->Add(mMCPosACSisTg.get()); + mMCPosACS->Add(mMCPosACSisBg.get()); + mMCNegACS->Add(mMCNegACSisTg.get()); + mMCNegACS->Add(mMCNegACSisBg.get()); + mMCDauDCA->Add(mMCDauDCAisTg.get()); + mMCDauDCA->Add(mMCDauDCAisBg.get()); + mMCPosPVDCA->Add(mMCPosPVDCAisTg.get()); + mMCPosPVDCA->Add(mMCPosPVDCAisBg.get()); + mMCNegPVDCA->Add(mMCNegPVDCAisTg.get()); + mMCNegPVDCA->Add(mMCNegPVDCAisBg.get()); + mMCV0PVDCA->Add(mMCV0PVDCAisTg.get()); + mMCV0PVDCA->Add(mMCV0PVDCAisBg.get()); +} + +void AvgClusSizeStudy::setStyle() +{ + gStyle->SetPalette(kRainBow); + std::vector colors = {1, 2, 3, 4, 6, 7, 41, 47}; + std::vector markers = {2, 3, 4, 5, 25, 26, 27, 28, 32}; + for (int i = 0; i < mAvgClusSizeCEtaVec.size(); i++) { + mAvgClusSizeCEtaVec[i]->SetMarkerStyle(markers[i]); + mAvgClusSizeCEtaVec[i]->SetMarkerColor(colors[i]); + mAvgClusSizeCEtaVec[i]->SetLineColor(colors[i]); + } + + mMCRisTg->SetLineColor(kRed); + mMCCosPAisTg->SetLineColor(kRed); + mMCPosACSisTg->SetLineColor(kRed); + mMCNegACSisTg->SetLineColor(kRed); + mMCDauDCAisTg->SetLineColor(kRed); + mMCPosPVDCAisTg->SetLineColor(kRed); + mMCNegPVDCAisTg->SetLineColor(kRed); + mMCV0PVDCAisTg->SetLineColor(kRed); +} + +void AvgClusSizeStudy::run(ProcessingContext& pc) +{ + // auto geom = o2::its::GeometryTGeo::Instance(); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(recoData); +} + +void AvgClusSizeStudy::getClusterSizes(std::vector& clusSizeVec, const gsl::span ITSclus, gsl::span::iterator& pattIt, const o2::itsmft::TopologyDictionary* mdict) +{ + for (unsigned int iClus{0}; iClus < ITSclus.size(); ++iClus) { + auto& clus = ITSclus[iClus]; + auto pattID = clus.getPatternID(); + int npix; + o2::itsmft::ClusterPattern patt; + + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mdict->isGroup(pattID)) { + patt.acquirePattern(pattIt); + npix = patt.getNPixels(); + } else { + npix = mdict->getNpixels(pattID); + patt = mdict->getPattern(pattID); + } + clusSizeVec[iClus] = npix; + } +} + +void AvgClusSizeStudy::loadData(o2::globaltracking::RecoContainer& recoData) +{ + mInputITSidxs = recoData.getITSTracksClusterRefs(); + auto compClus = recoData.getITSClusters(); + auto clusPatt = recoData.getITSClustersPatterns(); + mClusterSizes.resize(compClus.size()); + auto pattIt = clusPatt.begin(); + getClusterSizes(mClusterSizes, compClus, pattIt, mDict); +} + +void AvgClusSizeStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + auto& params = o2::its::study::ITSAvgClusSizeParamConfig::Instance(); + float dPosACS, dNegACS, dauDCA, cosPA, v0R, eta, dPospvDCA, dNegpvDCA, v0pvDCA, tgV0HypoMass, bgV0HypoMass, alphaArm, pT; // ACS=average cluster size + bool isMCTarget = false; + int targetPDGCode = 310; + TrackITS dPosRecoTrk, dNegRecoTrk; // daughter ITS tracks + DCA dPosDCA, dNegDCA, v0DCA; // DCA object for prong to primary vertex (DCA = o2::dataformats::DCA) + PVertex pv; + + PID targetV0 = PID("K0"); + PID backgroundV0 = PID("Lambda"); + PID tgPos, tgNeg, bgPos, bgNeg; + if (params.targetV0 == "K0") { + bgPos = PID("Proton"); + LOGP(info, "V0 target set to K0-short."); + } else if (params.targetV0 == "Lambda") { + targetV0 = PID("Lambda"); + tgPos = PID("Proton"); + backgroundV0 = PID("K0"); + targetPDGCode = 3122; + LOGP(info, "V0 target set to Lambda."); + } else { + LOGP(warning, "Given V0 target not recognized, defaulting to K0-short."); + bgPos = PID("Proton"); + } + + // Variables for MC analysis + gsl::span mcLabels; + o2::MCCompLabel dPosLab, dNegLab; + const o2::MCTrack *V0mcTrk, *dPosMCTrk, *dNegMCTrk; + int V0PdgCode, mPosTrkId, mNegTrkId; + + loadData(recoData); + auto V0s = recoData.getV0s(); + auto V0sIdx = recoData.getV0sIdx(); + size_t nV0s = V0sIdx.size(); + if (nV0s && nV0s != V0s.size()) { + LOGP(fatal, "This data has not secondary vertices kinematics filled"); + } + + LOGP(info, "Found {} reconstructed V0s.", nV0s); + LOGP(info, "Found {} ITS tracks.", recoData.getITSTracks().size()); + LOGP(info, "Found {} ROFs.", recoData.getITSTracksROFRecords().size()); + if (mUseMC) { + mcLabels = recoData.getITSTracksMCLabels(); + LOGP(info, "Found {} labels.", mcLabels.size()); + } + + for (size_t iv = 0; iv < nV0s; iv++) { + auto v0 = V0s[iv]; + const auto& v0Idx = V0sIdx[iv]; + dPosRecoTrk = recoData.getITSTrack(v0Idx.getProngID(0)); // cannot use v0.getProng() since it returns TrackParCov not TrackITS + dNegRecoTrk = recoData.getITSTrack(v0Idx.getProngID(1)); + + pv = recoData.getPrimaryVertex(v0Idx.getVertexID()); // extract primary vertex + dPosRecoTrk.propagateToDCA(pv, params.b, &dPosDCA); // calculate and store DCA objects for both prongs + dNegRecoTrk.propagateToDCA(pv, params.b, &dNegDCA); + v0.propagateToDCA(pv, params.b, &v0DCA); + + dPospvDCA = std::sqrt(dPosDCA.getR2()); // extract DCA distance from DCA object + dNegpvDCA = std::sqrt(dNegDCA.getR2()); + v0pvDCA = std::sqrt(v0DCA.getR2()); + + eta = v0.getEta(); + dauDCA = v0.getDCA(); + cosPA = v0.getCosPA(); + v0R = std::sqrt(v0.calcR2()); // gives distance from pvertex to origin? in centimeters (?) NOTE: unsure if this is to the primary vertex or to origin + + dPosACS = getAverageClusterSize(&dPosRecoTrk); + dNegACS = getAverageClusterSize(&dNegRecoTrk); + + tgV0HypoMass = calcV0HypoMass(v0, tgPos, tgNeg); + bgV0HypoMass = calcV0HypoMass(v0, bgPos, bgNeg); + calcAPVars(v0, &alphaArm, &pT); + + if (mUseMC) { // check whether queried V0 is the target V0 in MC, and fill the cut validation plots + V0PdgCode = 0; + isMCTarget = false; + dPosLab = mcLabels[v0Idx.getProngID(0)]; // extract MC labels for the prongs + dNegLab = mcLabels[v0Idx.getProngID(1)]; + if (!dPosLab.isValid() || !dNegLab.isValid()) { + LOGP(debug, "Daughter MCCompLabel not valid: {}(+) and {}(-). Skipping.", dPosLab.isValid(), dNegLab.isValid()); + nNotValid++; + } else { + dPosMCTrk = mKineReader->getTrack(dPosLab); + dNegMCTrk = mKineReader->getTrack(dNegLab); + if (dPosMCTrk == nullptr || dNegMCTrk == nullptr) { + LOGP(debug, "Nullptr found: {}(+) and {}(-). Skipping.", (void*)dPosMCTrk, (void*)dNegMCTrk); + nNullptrs++; + } else { + mPosTrkId = dPosMCTrk->getMotherTrackId(); + mNegTrkId = dNegMCTrk->getMotherTrackId(); + LOGP(debug, "Daughter PDG codes: {}(+) and {}(-)", dPosMCTrk->GetPdgCode(), dNegMCTrk->GetPdgCode()); + if (mPosTrkId != mNegTrkId || mPosTrkId == -1 || mNegTrkId == -1) { + LOGP(debug, "Mother track ID mismatch or default -1: {}(+) and {}(-). Skipping.", mPosTrkId, mNegTrkId); + nMotherIDMismatch++; + } else { + if (dNegLab.getEventID() != dPosLab.getEventID()) { + LOGP(debug, "Daughter EvID mismatch: {}(+) and {}(-). Skipping.", dPosLab.getEventID(), dNegLab.getEventID()); + nEvIDMismatch++; + } else { + V0mcTrk = mKineReader->getTrack(dNegLab.getEventID(), mPosTrkId); // assume daughter MCTracks are in same event as V0 MCTrack + V0PdgCode = V0mcTrk->GetPdgCode(); + if (V0PdgCode == targetPDGCode) { + isMCTarget = true; + nTargetV0++; + } else { + nNotTargetV0++; + } + } + } + } + } + if (isMCTarget) { + mMCCosPAisTg->Fill(cosPA); + mMCDauDCAisTg->Fill(dauDCA); + mMCRisTg->Fill(v0R); + mMCPosPVDCAisTg->Fill(dPospvDCA); + mMCNegPVDCAisTg->Fill(dNegpvDCA); + mMCPosACSisTg->Fill(dPosACS); + mMCNegACSisTg->Fill(dNegACS); + mMCV0PVDCAisTg->Fill(v0pvDCA); + mMCArmPodolisTg->Fill(alphaArm, pT); + } else { + mMCCosPAisBg->Fill(cosPA); + mMCDauDCAisBg->Fill(dauDCA); + mMCRisBg->Fill(v0R); + mMCPosPVDCAisBg->Fill(dPospvDCA); + mMCNegPVDCAisBg->Fill(dNegpvDCA); + mMCPosACSisBg->Fill(dPosACS); + mMCNegACSisBg->Fill(dNegACS); + mMCV0PVDCAisBg->Fill(v0pvDCA); + mMCArmPodolisBg->Fill(alphaArm, pT); + } + } + + mOutputNtupleAll->Fill(dPosACS, dNegACS, cosPA, v0R, eta, dauDCA, dPospvDCA, dNegpvDCA, v0pvDCA, alphaArm, pT, calcV0HypoMass(v0, PID::Pion, PID::Pion), calcV0HypoMass(v0, PID::Proton, PID::Pion), calcV0HypoMass(v0, PID::Pion, PID::Proton), (float)V0PdgCode); + if ((cosPA > params.cosPAmin || params.disableCosPA) && (v0R < params.Rmax || params.disableRmax) && (v0R > params.Rmin || params.disableRmin) && (dauDCA < params.prongDCAmax || params.disableProngDCAmax) && (dPospvDCA > params.dauPVDCAmin || params.disableDauPVDCAmin) && (dNegpvDCA > params.dauPVDCAmin || params.disableDauPVDCAmin) && (v0pvDCA < params.v0PVDCAmax || params.disableV0PVDCAmax) && (abs(bgV0HypoMass - backgroundV0.getMass()) > params.bgV0window || params.disableMassHypoth) && (abs(tgV0HypoMass - targetV0.getMass()) < params.tgV0window || params.disableMassHypoth)) { + mOutputNtupleCut->Fill(dPosACS, dNegACS, cosPA, v0R, eta, dauDCA, dPospvDCA, dNegpvDCA, v0pvDCA, alphaArm, pT, calcV0HypoMass(v0, PID::Pion, PID::Pion), calcV0HypoMass(v0, PID::Proton, PID::Pion), calcV0HypoMass(v0, PID::Pion, PID::Proton), (float)V0PdgCode); + if (eta > params.etaMin && eta < params.etaMax) { + fillEtaBin(eta, dPosACS, 0); + fillEtaBin(eta, dNegACS, 0); + } else { + nV0OutOfEtaRange++; + } + } + } + + if (mUseMC) { + LOGP(info, "MONTE CARLO OVERALL STATISTICS: {} total V0s, {} nonvalid daughter labels, {} nullptrs, {} motherID mismatches, {} evID mismatches, {} matching target, {} not matching target", V0s.size(), nNotValid, nNullptrs, nMotherIDMismatch, nEvIDMismatch, nTargetV0, nNotTargetV0); + int nPrimaryTargetV0 = 0; + for (auto& mcTrk : mMCTracks) { // search through all MC tracks to find primary target V0s, whether reconstructed or not + if (mcTrk.GetPdgCode() == targetPDGCode && mcTrk.isPrimary()) { + nPrimaryTargetV0++; + } + } + LOGP(info, "MONTE CARLO OVERALL STATISTICS: {} MC target V0s (isPrimary) found in MC tracks out of {} total MC tracks", nPrimaryTargetV0, mMCTracks.size()); + } + LOGP(info, "{} V0s out of eta range ({}, {})", nV0OutOfEtaRange, params.etaMin, params.etaMax); +} + +float AvgClusSizeStudy::getAverageClusterSize(TrackITS* daughter) +{ + int totalSize{0}; + auto firstClus = daughter->getFirstClusterEntry(); + auto ncl = daughter->getNumberOfClusters(); + for (int icl = 0; icl < ncl; icl++) { + totalSize += mClusterSizes[mInputITSidxs[firstClus + icl]]; + } + return (float)totalSize / (float)ncl; +} + +float AvgClusSizeStudy::calcV0HypoMass(const V0& v0, PID hypothPIDPos, PID hypothPIDNeg) +{ + // Mass hypothesis calculation; taken from o2::strangeness_tracking::StrangenessTracker::calcMotherMass() + std::array pPos, pNeg, pV0; + v0.getProng(0).getPxPyPzGlo(pPos); + v0.getProng(1).getPxPyPzGlo(pNeg); + v0.getPxPyPzGlo(pV0); + double m2Pos = PID::getMass2(hypothPIDPos); + double m2Neg = PID::getMass2(hypothPIDNeg); + double p2Pos = (pPos[0] * pPos[0]) + (pPos[1] * pPos[1]) + (pPos[2] * pPos[2]); + double p2Neg = (pNeg[0] * pNeg[0]) + (pNeg[1] * pNeg[1]) + (pNeg[2] * pNeg[2]); + double ePos = std::sqrt(p2Pos + m2Pos), eNeg = std::sqrt(p2Neg + m2Neg); + double e2V0 = (ePos + eNeg) * (ePos + eNeg); + double pxV0 = (pPos[0] + pNeg[0]); + double pyV0 = (pPos[1] + pNeg[1]); + double pzV0 = (pPos[2] + pNeg[2]); + double p2V0 = (pxV0 * pxV0) + (pyV0 * pyV0) + (pzV0 * pzV0); + return (float)std::sqrt(e2V0 - p2V0); +} + +void AvgClusSizeStudy::calcAPVars(const V0& v0, float* alphaArm, float* pT) +{ + // Calculation of the Armenteros-Podolanski variables + std::array pV0, pPos, pNeg; + v0.getProng(0).getPxPyPzGlo(pPos); + v0.getProng(1).getPxPyPzGlo(pNeg); + v0.getPxPyPzGlo(pV0); + double p2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1] + pV0[2] * pV0[2]; + double qNeg = pNeg[0] * pV0[0] + pNeg[1] * pV0[1] + pNeg[2] * pV0[2]; + double qPos = pPos[0] * pV0[0] + pPos[1] * pV0[1] + pPos[2] * pV0[2]; + *alphaArm = (float)(qPos - qNeg) / (qPos + qNeg); + double p2Pos = pPos[0] * pPos[0] + pPos[1] * pPos[1] + pPos[2] * pPos[2]; + *pT = (float)std::sqrt(p2Pos - ((qPos * qPos) / p2V0)); +}; + +void AvgClusSizeStudy::fillEtaBin(float eta, float clusSize, int i) +{ + if (eta < mEtaBinUL[i]) { + mAvgClusSizeCEtaVec[i]->Fill(clusSize); + } else { + fillEtaBin(eta, clusSize, i + 1); + } +} + +void AvgClusSizeStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this param need to be queried only once + initOnceDone = true; + o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void AvgClusSizeStudy::saveHistograms() +{ + mDBGOut.reset(); + TFile fout(mOutName.c_str(), "RECREATE"); + + fout.WriteTObject(mOutputNtupleAll.get()); + fout.WriteTObject(mOutputNtupleCut.get()); + fout.WriteTObject(mMCR.get()); + fout.WriteTObject(mMCCosPA.get()); + fout.WriteTObject(mMCPosACS.get()); + fout.WriteTObject(mMCNegACS.get()); + fout.WriteTObject(mMCDauDCA.get()); + fout.WriteTObject(mMCPosPVDCA.get()); + fout.WriteTObject(mMCNegPVDCA.get()); + fout.WriteTObject(mMCV0PVDCA.get()); + fout.WriteTObject(mMCArmPodolisTg.get()); + fout.WriteTObject(mMCArmPodolisBg.get()); + + fout.WriteTObject(mAvgClusSizeCEta.get()); + fout.Close(); + + LOGP(info, "Stored histograms into {}", mOutName.c_str()); +} + +void AvgClusSizeStudy::plotHistograms() +{ + if (mUseMC) { + TCanvas* cMCR = new TCanvas(); + mMCR->Draw("nostack"); + cMCR->BuildLegend(); + cMCR->Print("mcR.png"); + TCanvas* cMCCosPA = new TCanvas(); + mMCCosPA->Draw("nostack"); + cMCCosPA->BuildLegend(); + cMCCosPA->Print("mcCosPA.png"); + TCanvas* cMCPosACS = new TCanvas(); + mMCPosACS->Draw("nostack"); + cMCPosACS->BuildLegend(); + cMCPosACS->Print("mcPosACS.png"); + TCanvas* cMCNegACS = new TCanvas(); + mMCNegACS->Draw("nostack"); + cMCNegACS->BuildLegend(); + cMCNegACS->Print("mcNegACS.png"); + TCanvas* cMCDauDCA = new TCanvas(); + mMCDauDCA->Draw("nostack"); + cMCDauDCA->BuildLegend(); + cMCDauDCA->Print("mcDauDCA.png"); + TCanvas* cMCPosPVDCA = new TCanvas(); + mMCPosPVDCA->Draw("nostack"); + cMCPosPVDCA->BuildLegend(); + cMCPosPVDCA->Print("mcPosPVDCA.png"); + TCanvas* cMCNegPVDCA = new TCanvas(); + mMCNegPVDCA->Draw("nostack"); + cMCNegPVDCA->BuildLegend(); + cMCNegPVDCA->Print("mcNegPVDCA.png"); + TCanvas* cMCV0PVDCA = new TCanvas(); + mMCV0PVDCA->Draw("nostack"); + cMCV0PVDCA->BuildLegend(); + cMCV0PVDCA->Print("mcV0PVDCA.png"); + TCanvas* cMCArmPodolTg = new TCanvas(); + mMCArmPodolisTg->Draw("COLZ"); + cMCArmPodolTg->Print("mcArmPodolTg.png"); + TCanvas* cMCArmPodolBg = new TCanvas(); + mMCArmPodolisBg->Draw("COLZ"); + cMCArmPodolBg->Print("mcArmPodolBg.png"); + } + + TCanvas* c10 = new TCanvas(); + mAvgClusSizeCEta->Draw("P NOSTACK"); + c10->BuildLegend(0.6, 0.6, 0.8, 0.8); + c10->Print("clusSizeEta.png"); +} + +void AvgClusSizeStudy::endOfStream(EndOfStreamContext& ec) +{ + auto& params = o2::its::study::ITSAvgClusSizeParamConfig::Instance(); + setStyle(); + saveHistograms(); + if (params.generatePlots) { + plotHistograms(); + } +} + +void AvgClusSizeStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + // o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); + return; + } +} + +DataProcessorSpec getAvgClusSizeStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestClusters(srcClustersMask, useMC); + dataRequest->requestSecondaryVertices(useMC); + dataRequest->requestPrimaryVertices(useMC); // NOTE: may be necessary to use requestPrimaryVerticesTMP()... + // dataRequest->requestPrimaryVerticesTMP(useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + return DataProcessorSpec{ + "its-study-AvgClusSize", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, kineReader)}, + Options{}}; +} +} // namespace study +} // namespace its +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/Efficiency.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/Efficiency.cxx new file mode 100644 index 0000000000000..f6ee014abdabd --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/Efficiency.cxx @@ -0,0 +1,2863 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include "TGeoGlobalMagField.h" +#include "ITSStudies/Efficiency.h" +#include "ITSStudies/ITSStudiesConfigParam.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DetectorsBase/Propagator.h" +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "ITStracking/IOUtils.h" +#include "ReconstructionDataFormats/DCA.h" +#include "SimulationDataFormat/MCTrack.h" +#include "Steer/MCKinematicsReader.h" +#include "ReconstructionDataFormats/TrackParametrization.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NLAYERS 3 + +namespace o2::its::study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; + +class EfficiencyStudy final : public Task +{ + public: + EfficiencyStudy(std::shared_ptr dr, + mask_t src, + bool useMC, + std::shared_ptr kineReader, + std::shared_ptr gr) : mDataRequest(dr), mTracksSrc(src), mUseMC(useMC), mKineReader(kineReader), mGGCCDBRequest(gr){}; + + ~EfficiencyStudy() final = default; + void init(InitContext&) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void initialiseRun(o2::globaltracking::RecoContainer&); + void stileEfficiencyGraph(std::unique_ptr& eff, const char* name, const char* title, bool bidimensional, const int markerStyle, const double markersize, const int markercolor, const int linercolor); + int getDCAClusterTrackMC(int countDuplicated); + void studyDCAcutsMC(); + void studyClusterSelectionMC(); + void countDuplicatedAfterCuts(); + void getEfficiency(bool isMC); + void process(o2::globaltracking::RecoContainer&); + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + + private: + void updateTimeDependentParams(ProcessingContext& pc); + bool mVerboseOutput = false; + bool mUseMC; + std::string mOutFileName; + double b; + std::shared_ptr mKineReader; + GeometryTGeo* mGeometry; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + float mrangesPt[NLAYERS][2] = {{0., 0.5}, {0.5, 2.}, {2., 7.5}}; + + // Spans + gsl::span mTracksROFRecords; + gsl::span mClustersROFRecords; + gsl::span mTracks; + gsl::span mTracksMCLabels; + gsl::span mClusters; + gsl::span mClusPatterns; + gsl::span mInputITSidxs; + const o2::dataformats::MCLabelContainer* mClustersMCLCont; + std::vector> mITSClustersArray; + + // Data + GTrackID::mask_t mTracksSrc{}; + std::shared_ptr mDataRequest; + + // Utils + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mOutFile; + int mDuplicated_layer[NLAYERS] = {0}; + + //// Histos + + // DCA betweeen track and original cluster + std::unique_ptr mDCAxyOriginal[NLAYERS]; + std::unique_ptr mDCAzOriginal[NLAYERS]; + // DCA betweeen track and duplicated cluster + std::unique_ptr mDCAxyDuplicated; + std::unique_ptr mDCAzDuplicated; + + // DCA betweeen track and duplicated cluster per layer + std::unique_ptr mDCAxyDuplicated_layer[NLAYERS]; + std::unique_ptr mDCAzDuplicated_layer[NLAYERS]; + + // phi, eta, pt of the cluster + std::unique_ptr mPhiOriginal[NLAYERS]; + std::unique_ptr mEtaOriginal[NLAYERS]; + std::unique_ptr mPtOriginal[NLAYERS]; + std::unique_ptr mPtDuplicated[NLAYERS]; + std::unique_ptr mEtaDuplicated[NLAYERS]; + std::unique_ptr mPhiDuplicated[NLAYERS]; + std::unique_ptr mPhiOriginalIfDuplicated[NLAYERS]; + + std::unique_ptr mZvsPhiDUplicated[NLAYERS]; + + // position of the clusters + std::unique_ptr m3DClusterPositions; + std::unique_ptr m3DDuplicatedClusterPositions; + std::unique_ptr m2DClusterOriginalPositions; + std::unique_ptr m2DClusterDuplicatedPositions; + + // Efficiency histos + std::unique_ptr mEfficiencyGoodMatch; + std::unique_ptr mEfficiencyFakeMatch; + std::unique_ptr mEfficiencyTotal; + std::unique_ptr mEfficiencyGoodMatch_layer[NLAYERS]; + std::unique_ptr mEfficiencyFakeMatch_layer[NLAYERS]; + std::unique_ptr mEfficiencyTotal_layer[NLAYERS]; + std::unique_ptr mEfficiencyGoodMatchPt_layer[NLAYERS]; + std::unique_ptr mEfficiencyFakeMatchPt_layer[NLAYERS]; + std::unique_ptr mEfficiencyGoodMatchEta_layer[NLAYERS]; + std::unique_ptr mEfficiencyFakeMatchEta_layer[NLAYERS]; + std::unique_ptr mEfficiencyGoodMatchPhi_layer[NLAYERS]; + std::unique_ptr mEfficiencyGoodMatchPhiOriginal_layer[NLAYERS]; + std::unique_ptr mEfficiencyFakeMatchPhi_layer[NLAYERS]; + + // std::unique_ptr mEfficiencyColEta[NLAYERS]; + std::unique_ptr mDenColEta[NLAYERS]; + std::unique_ptr mNumColEta[NLAYERS]; + std::unique_ptr mDenRowPhi[NLAYERS]; + std::unique_ptr mNumRowPhi[NLAYERS]; + std::unique_ptr mDenRowCol[NLAYERS]; + std::unique_ptr mNumRowCol[NLAYERS]; + + // phi, eta, pt of the duplicated cluster per layer + std::unique_ptr mPt_EtaDupl[NLAYERS]; + + // duplicated per layer and per cut + std::unique_ptr mDuplicatedEtaAllPt[NLAYERS]; + std::unique_ptr mDuplicatedEta[NLAYERS][3]; + std::unique_ptr mDuplicatedPhiAllPt[NLAYERS]; + std::unique_ptr mDuplicatedPhi[NLAYERS][3]; + std::unique_ptr mDuplicatedPt[NLAYERS]; + std::unique_ptr mDuplicatedRow[NLAYERS]; + std::unique_ptr mDuplicatedCol[NLAYERS]; + std::unique_ptr mDuplicatedZ[NLAYERS]; + std::unique_ptr mDuplicatedPtEta[NLAYERS]; + std::unique_ptr mDuplicatedPtPhi[NLAYERS]; + std::unique_ptr mDuplicatedEtaPhi[NLAYERS]; + + // matches per layer and per cut + std::unique_ptr mNGoodMatchesEtaAllPt[NLAYERS]; + std::unique_ptr mNGoodMatchesEta[NLAYERS][3]; + std::unique_ptr mNGoodMatchesPhiAllPt[NLAYERS]; + std::unique_ptr mNGoodMatchesPhi[NLAYERS][3]; + + std::unique_ptr mNFakeMatchesEtaAllPt[NLAYERS]; + std::unique_ptr mNFakeMatchesEta[NLAYERS][3]; + std::unique_ptr mNFakeMatchesPhiAllPt[NLAYERS]; + std::unique_ptr mNFakeMatchesPhi[NLAYERS][3]; + + std::unique_ptr mNGoodMatchesPt[NLAYERS]; + std::unique_ptr mNFakeMatchesPt[NLAYERS]; + + std::unique_ptr mNGoodMatchesRow[NLAYERS]; + std::unique_ptr mNFakeMatchesRow[NLAYERS]; + + std::unique_ptr mNGoodMatchesCol[NLAYERS]; + std::unique_ptr mNFakeMatchesCol[NLAYERS]; + + std::unique_ptr mNGoodMatchesZ[NLAYERS]; + std::unique_ptr mNFakeMatchesZ[NLAYERS]; + + std::unique_ptr mNGoodMatchesPtEta[NLAYERS]; + std::unique_ptr mNFakeMatchesPtEta[NLAYERS]; + + std::unique_ptr mNGoodMatchesPtPhi[NLAYERS]; + std::unique_ptr mNFakeMatchesPtPhi[NLAYERS]; + + std::unique_ptr mNGoodMatchesEtaPhi[NLAYERS]; + std::unique_ptr mNFakeMatchesEtaPhi[NLAYERS]; + + // calculating the efficiency with TEfficiency class + std::unique_ptr mEffPtGood[NLAYERS]; + std::unique_ptr mEffPtFake[NLAYERS]; + std::unique_ptr mEffRowGood[NLAYERS]; + std::unique_ptr mEffRowFake[NLAYERS]; + std::unique_ptr mEffColGood[NLAYERS]; + std::unique_ptr mEffColFake[NLAYERS]; + std::unique_ptr mEffZGood[NLAYERS]; + std::unique_ptr mEffZFake[NLAYERS]; + std::unique_ptr mEffPtEtaGood[NLAYERS]; + std::unique_ptr mEffPtEtaFake[NLAYERS]; + std::unique_ptr mEffPtPhiGood[NLAYERS]; + std::unique_ptr mEffPtPhiFake[NLAYERS]; + std::unique_ptr mEffEtaPhiGood[NLAYERS]; + std::unique_ptr mEffEtaPhiFake[NLAYERS]; + + std::unique_ptr mEffEtaGoodAllPt[NLAYERS]; + std::unique_ptr mEffEtaGood[NLAYERS][3]; + std::unique_ptr mEffEtaFakeAllPt[NLAYERS]; + std::unique_ptr mEffEtaFake[NLAYERS][3]; + + std::unique_ptr mEffPhiGoodAllPt[NLAYERS]; + std::unique_ptr mEffPhiGood[NLAYERS][3]; + std::unique_ptr mEffPhiFakeAllPt[NLAYERS]; + std::unique_ptr mEffPhiFake[NLAYERS][3]; + + std::unique_ptr mnGoodMatchesPt_layer[NLAYERS]; + std::unique_ptr mnFakeMatchesPt_layer[NLAYERS]; + + std::unique_ptr mnGoodMatchesEta_layer[NLAYERS]; + std::unique_ptr mnFakeMatchesEta_layer[NLAYERS]; + + std::unique_ptr mnGoodMatchesPhi_layer[NLAYERS]; + std::unique_ptr mnGoodMatchesPhiOriginal_layer[NLAYERS]; + std::unique_ptr mnFakeMatchesPhi_layer[NLAYERS]; + + std::unique_ptr DCAxyData[NLAYERS]; + std::unique_ptr DCAzData[NLAYERS]; + + std::unique_ptr DCAxyRejected[NLAYERS]; + std::unique_ptr DCAzRejected[NLAYERS]; + + std::unique_ptr denPt[NLAYERS]; + std::unique_ptr numPt[NLAYERS]; + std::unique_ptr numPtGood[NLAYERS]; + std::unique_ptr numPtFake[NLAYERS]; + + std::unique_ptr denPhi[NLAYERS]; + std::unique_ptr numPhi[NLAYERS]; + std::unique_ptr numPhiGood[NLAYERS]; + std::unique_ptr numPhiFake[NLAYERS]; + + std::unique_ptr denEta[NLAYERS]; + std::unique_ptr numEta[NLAYERS]; + std::unique_ptr numEtaGood[NLAYERS]; + std::unique_ptr numEtaFake[NLAYERS]; + + std::unique_ptr denRow[NLAYERS]; + std::unique_ptr numRow[NLAYERS]; + std::unique_ptr numRowGood[NLAYERS]; + std::unique_ptr numRowFake[NLAYERS]; + + std::unique_ptr denCol[NLAYERS]; + std::unique_ptr numCol[NLAYERS]; + std::unique_ptr numColGood[NLAYERS]; + std::unique_ptr numColFake[NLAYERS]; + std::unique_ptr denZ[NLAYERS]; + std::unique_ptr numZ[NLAYERS]; + std::unique_ptr numZGood[NLAYERS]; + std::unique_ptr numZFake[NLAYERS]; + + std::unique_ptr numLayers; + std::unique_ptr denLayers; + std::unique_ptr numGoodLayers; + std::unique_ptr numFakeLayers; + + int nDuplicatedClusters[NLAYERS] = {0}; + int nTracksSelected[NLAYERS] = {0}; // denominator fot the efficiency calculation + + std::unique_ptr IPOriginalxy[NLAYERS]; + std::unique_ptr IPOriginalz[NLAYERS]; + + std::unique_ptr chipRowDuplicated[NLAYERS]; + std::unique_ptr chipRowOriginalIfDuplicated[NLAYERS]; + + std::unique_ptr chi2trackAccepted; + + /// checking where the duplicated not found are (histograms filled with the orifinal cluster variables) + std::unique_ptr phiFound[NLAYERS]; + std::unique_ptr rowFound[NLAYERS]; + std::unique_ptr phiNotFound[NLAYERS]; + std::unique_ptr rowNotFound[NLAYERS]; + std::unique_ptr zFound[NLAYERS]; + std::unique_ptr zNotFound[NLAYERS]; + std::unique_ptr colFoundOriginalVsDuplicated[NLAYERS]; + std::unique_ptr colFoundOriginal[NLAYERS]; + std::unique_ptr colNotFound[NLAYERS]; + std::unique_ptr radiusFound[NLAYERS]; + std::unique_ptr radiusNotFound[NLAYERS]; + std::unique_ptr m2DClusterFoundPositions; + std::unique_ptr m2DClusterNotFoundPositions; + std::unique_ptr mChipNotFound; + std::unique_ptr mChipFound; + std::unique_ptr l0_00; + std::unique_ptr l1_15; + std::unique_ptr l2_19; + std::unique_ptr chipOrigVsOverlap; + std::unique_ptr chipmap; +}; + +void EfficiencyStudy::init(InitContext& ic) +{ + LOGP(info, "init"); + + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + + auto& pars = o2::its::study::ITSEfficiencyParamConfig::Instance(); + mOutFileName = pars.outFileName; + b = pars.b; + + int nbPt = 75; + double xbins[nbPt + 1], ptcutl = 0.05, ptcuth = 7.5; + double a = std::log(ptcuth / ptcutl) / nbPt; + for (int i = 0; i <= nbPt; i++) { + xbins[i] = ptcutl * std::exp(i * a); + } + + mOutFile = std::make_unique(mOutFileName.c_str(), "recreate"); + + mDCAxyDuplicated = std::make_unique("dcaXYDuplicated", "Distance between track and duplicated cluster ;DCA xy (cm); ", 200, -0.01, 0.01); + mDCAzDuplicated = std::make_unique("dcaZDuplicated", "Distance between track and duplicated cluster ;DCA z (cm); ", 200, -0.01, 0.01); + + m3DClusterPositions = std::make_unique("3DClusterPositions", ";x (cm);y (cm);z (cm)", 200, -10, 10, 200, -10, 10, 400, -20, 20); + m3DDuplicatedClusterPositions = std::make_unique("3DDuplicatedClusterPositions", ";x (cm);y (cm);z (cm)", 200, -10, 10, 200, -10, 10, 500, -30, 30); + m2DClusterOriginalPositions = std::make_unique("m2DClusterOriginalPositions", ";x (cm);y (cm)", 400, -10, 10, 400, -6, 6); + m2DClusterDuplicatedPositions = std::make_unique("m2DClusterDuplicatedPositions", ";x (cm);y (cm)", 400, -10, 10, 400, -6, 6); + + mEfficiencyGoodMatch = std::make_unique("mEfficiencyGoodMatch", ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + mEfficiencyFakeMatch = std::make_unique("mEfficiencyFakeMatch", ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + mEfficiencyTotal = std::make_unique("mEfficiencyTotal", ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + + chi2trackAccepted = std::make_unique("chi2trackAccepted", "; $#chi^{2}", 500, 0, 100); + + m2DClusterFoundPositions = std::make_unique("m2DClusterFoundPositions", ";x (cm);y (cm)", 250, -5, 5, 250, -5, 5); + m2DClusterNotFoundPositions = std::make_unique("m2DClusterNotFoundPositions", ";x (cm);y (cm)", 250, -5, 5, 250, -5, 5); + mChipNotFound = std::make_unique("mChipNotFound", ";chipID", 432, 0, 432); + mChipFound = std::make_unique("mChipFound", ";chipID", 432, 0, 432); + l0_00 = std::make_unique("l0_00", ";col; row", 2304, -0.5, 9215.5, 128, -0.5, 511.5); + l1_15 = std::make_unique("l1_15", ";col; row", 2304, -0.5, 9215.5, 512, -0.5, 511.5); + l2_19 = std::make_unique("l2_19", ";col; row", 2304, -0.5, 9215.5, 512, -0.5, 511.5); + chipOrigVsOverlap = std::make_unique("chipOrigVsOverlap", ";chipID Overlap;chipID Original", 9, 0, 9, 9, 0, 9); + chipmap = std::make_unique("chipmap", ";Column;Row", 1024, 0, 1023, 512, -0.5, 511.5); + + numLayers = std::make_unique("numLayers", "numLayers; ; Efficiency", 3, -0.5, 2.5); + numGoodLayers = std::make_unique("numGoodLayers", "numGoodLayers; ; Efficiency", 3, -0.5, 2.5); + numFakeLayers = std::make_unique("numFakeLayers", "numFakeLayers; ; Efficiency", 3, -0.5, 2.5); + denLayers = std::make_unique("denLayers", "denLayers; ; Efficiency", 3, -0.5, 2.5); + + for (int i = 0; i < NLAYERS; i++) { + + chipRowDuplicated[i] = std::make_unique(Form("chipPosDuplicated_L%d", i), Form("L%d; row", i), 512, -0.5, 511.5); + chipRowOriginalIfDuplicated[i] = std::make_unique(Form("chipPosOriginalIfDuplicated%d", i), Form("L%d; row", i), 512, -0.5, 511.5); + + DCAxyData[i] = std::make_unique(Form("dcaXYData_L%d", i), "Distance between track and original cluster ;DCA xy (cm); ", 4000, -0.2, 0.2); + DCAzData[i] = std::make_unique(Form("dcaZData_L%d", i), "Distance between track and original cluster ;DCA z (cm); ", 4000, -0.2, 0.2); + DCAxyRejected[i] = std::make_unique(Form("DCAxyRejected%d", i), "Distance between track and original cluster (rejected) ;DCA xy (cm); ", 30000, -30, 30); + DCAzRejected[i] = std::make_unique(Form("DCAzRejected%d", i), "Distance between track and original cluster (rejected) ;DCA z (cm); ", 30000, -30, 30); + + mDCAxyOriginal[i] = std::make_unique(Form("dcaXYOriginal_L%d", i), "Distance between track and original cluster ;DCA xy (cm); ", 200, -0.01, 0.01); + mDCAzOriginal[i] = std::make_unique(Form("dcaZOriginal_L%d", i), "Distance between track and original cluster ;DCA z (cm); ", 200, -0.01, 0.01); + + mPhiOriginal[i] = std::make_unique(Form("phiOriginal_L%d", i), ";phi (rad); ", 90, -3.2, 3.2); + mEtaOriginal[i] = std::make_unique(Form("etaOriginal_L%d", i), ";eta (rad); ", 100, -2, 2); + mPtOriginal[i] = std::make_unique(Form("ptOriginal_L%d", i), ";pt (GeV/c); ", 100, 0, 10); + + mZvsPhiDUplicated[i] = std::make_unique(Form("zvsphiDuplicated_L%d", i), ";z (cm);phi (rad)", 400, -20, 20, 90, -3.2, 3.2); + + mPtDuplicated[i] = std::make_unique(Form("ptDuplicated_L%d", i), ";pt (GeV/c); ", nbPt, 0, 7.5); // xbins); + mEtaDuplicated[i] = std::make_unique(Form("etaDuplicated_L%d", i), ";eta; ", 40, -2, 2); + mPhiDuplicated[i] = std::make_unique(Form("phiDuplicated_L%d", i), ";phi (rad); ", 90, -3.2, 3.2); + mPhiOriginalIfDuplicated[i] = std::make_unique(Form("phiOriginalIfDuplicated_L%d", i), ";phi (rad); ", 90, -3.2, 3.2); + mDCAxyDuplicated_layer[i] = std::make_unique(Form("dcaXYDuplicated_layer_L%d", i), "Distance between track and duplicated cluster ;DCA xy (cm); ", 100, -0.01, 0.01); + mDCAzDuplicated_layer[i] = std::make_unique(Form("dcaZDuplicated_layer_L%d", i), "Distance between track and duplicated cluster ;DCA z (cm); ", 100, -0.01, 0.01); + + mEfficiencyGoodMatch_layer[i] = std::make_unique(Form("mEfficiencyGoodMatch_layer_L%d", i), ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + mEfficiencyFakeMatch_layer[i] = std::make_unique(Form("mEfficiencyFakeMatch_layer_L%d", i), ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + mEfficiencyTotal_layer[i] = std::make_unique(Form("mEfficiencyTotal_layer_L%d", i), ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + + mEfficiencyGoodMatchPt_layer[i] = std::make_unique(Form("mEfficiencyGoodMatchPt_layer_L%d", i), ";#it{p}_{T} (GeV/c);#sigma(DCA) cut;Efficiency;", nbPt, 0, 7.5, /* xbins*/ 20, 0.5, 20.5); + mEfficiencyFakeMatchPt_layer[i] = std::make_unique(Form("mEfficiencyFakeMatchPt_layer_L%d", i), ";#it{p}_{T} (GeV/c);#sigma(DCA) cut;Efficiency;", nbPt, 0, 7.5, /* xbins*/ 20, 0.5, 20.5); + + mEfficiencyGoodMatchEta_layer[i] = std::make_unique(Form("mEfficiencyGoodMatchEta_layer_L%d", i), ";#eta;#sigma(DCA) cut;Efficiency;", 40, -2, 2, 20, 0.5, 20.5); + mEfficiencyFakeMatchEta_layer[i] = std::make_unique(Form("mEfficiencyFakeMatchEta_layer_L%d", i), ";#eta;#sigma(DCA) cut;Efficiency;", 40, -2, 2, 20, 0.5, 20.5); + + mEfficiencyGoodMatchPhi_layer[i] = std::make_unique(Form("mEfficiencyGoodMatchPhi_layer_L%d", i), ";#phi;#sigma(DCA) cut;Efficiency;", 90, -3.2, 3.2, 20, 0.5, 20.5); + mEfficiencyGoodMatchPhiOriginal_layer[i] = std::make_unique(Form("mEfficiencyGoodMatchPhiOriginal_layer_L%d", i), ";#phi Original;#sigma(DCA) cut;Efficiency;", 90, -3.2, 3.2, 20, 0.5, 20.5); + mEfficiencyFakeMatchPhi_layer[i] = std::make_unique(Form("mEfficiencyFakeMatchPhi_layer_L%d", i), ";#phi;#sigma(DCA) cut;Efficiency;", 90, -3.2, 3.2, 20, 0.5, 20.5); + + mPt_EtaDupl[i] = std::make_unique(Form("mPt_EtaDupl_L%d", i), ";#it{p}_{T} (GeV/c);#eta; ", 100, 0, 10, 100, -2, 2); + + mDuplicatedPt[i] = std::make_unique(Form("mDuplicatedPt_log_L%d", i), Form("; #it{p}_{T} (GeV/c); Number of duplciated clusters L%d", i), nbPt, 0, 7.5 /* xbins*/); + mDuplicatedPt[i]->Sumw2(); + mNGoodMatchesPt[i] = std::make_unique(Form("mNGoodMatchesPt_L%d", i), Form("; #it{p}_{T} (GeV/c); Number of good matches L%d", i), nbPt, 0, 7.5 /* xbins*/); + mNGoodMatchesPt[i]->Sumw2(); + mNFakeMatchesPt[i] = std::make_unique(Form("mNFakeMatchesPt_L%d", i), Form("; #it{p}_{T} (GeV/c); Number of fake matches L%d", i), nbPt, 0, 7.5 /* xbins*/); + mNFakeMatchesPt[i]->Sumw2(); + + mDuplicatedRow[i] = std::make_unique(Form("mDuplicatedRow_L%d", i), Form("; Row; Number of duplciated clusters L%d", i), 128, -0.5, 511.5); + mDuplicatedRow[i]->Sumw2(); + mNGoodMatchesRow[i] = std::make_unique(Form("mNGoodMatchesRow_L%d", i), Form("; Row; Number of good matches L%d", i), 128, -0.5, 511.5); + mNGoodMatchesRow[i]->Sumw2(); + mNFakeMatchesRow[i] = std::make_unique(Form("mNFakeMatchesRow_L%d", i), Form(";Row; Number of fake matches L%d", i), 128, -0.5, 511.5); + mNFakeMatchesRow[i]->Sumw2(); + + mDuplicatedCol[i] = std::make_unique(Form("mDuplicatedCol_L%d", i), Form("; Col; Number of duplciated clusters L%d", i), 128, -0.5, 1023.5); + mDuplicatedCol[i]->Sumw2(); + mNGoodMatchesCol[i] = std::make_unique(Form("mNGoodMatchesCol_L%d", i), Form("; Col; Number of good matches L%d", i), 128, -0.5, 1023.5); + mNGoodMatchesCol[i]->Sumw2(); + mNFakeMatchesCol[i] = std::make_unique(Form("mNFakeMatchesCol_L%d", i), Form(";Col; Number of fake matches L%d", i), 128, -0.5, 1023.5); + mNFakeMatchesCol[i]->Sumw2(); + + mDuplicatedZ[i] = std::make_unique(Form("mDuplicatedZ_L%d", i), Form("; Z (cm); Number of duplciated clusters L%d", i), 100, -15, 15); + mDuplicatedZ[i]->Sumw2(); + mNGoodMatchesZ[i] = std::make_unique(Form("mNGoodMatchesZ_L%d", i), Form("; Z (cm); Number of good matches L%d", i), 100, -15, 15); + mNGoodMatchesZ[i]->Sumw2(); + mNFakeMatchesZ[i] = std::make_unique(Form("mNFakeMatchesZ_L%d", i), Form(";Z (cm); Number of fake matches L%d", i), 100, -15, 15); + mNFakeMatchesZ[i]->Sumw2(); + + mDuplicatedPtEta[i] = std::make_unique(Form("mDuplicatedPtEta_log_L%d", i), Form("; #it{p}_{T} (GeV/c);#eta; Number of duplciated clusters L%d", i), nbPt, 0, 7.5 /* xbins*/, 40, -2, 2); + mDuplicatedPtEta[i]->Sumw2(); + mNGoodMatchesPtEta[i] = std::make_unique(Form("mNGoodMatchesPtEta_L%d", i), Form("; #it{p}_{T} (GeV/c);#eta; Number of good matches L%d", i), nbPt, 0, 7.5 /* xbins*/, 40, -2, 2); + mNGoodMatchesPtEta[i]->Sumw2(); + mNFakeMatchesPtEta[i] = std::make_unique(Form("mNFakeMatchesPtEta_L%d", i), Form("; #it{p}_{T} (GeV/c);#eta; Number of good matches L%d", i), nbPt, 0, 7.5 /* xbins*/, 40, -2, 2); + mNFakeMatchesPtEta[i]->Sumw2(); + + mDuplicatedPtPhi[i] = std::make_unique(Form("mDuplicatedPtPhi_log_L%d", i), Form("; #it{p}_{T} (GeV/c);#phi (rad); Number of duplciated clusters L%d", i), nbPt, 0, 7.5 /* xbins*/, 90, -3.2, 3.2); + mDuplicatedPtPhi[i]->Sumw2(); + mNGoodMatchesPtPhi[i] = std::make_unique(Form("mNGoodMatchesPtPhi_L%d", i), Form("; #it{p}_{T} (GeV/c);#phi (rad); Number of good matches L%d", i), nbPt, 0, 7.5 /* xbins*/, 90, -3.2, 3.2); + mNGoodMatchesPtPhi[i]->Sumw2(); + mNFakeMatchesPtPhi[i] = std::make_unique(Form("mNFakeMatchesPtPhi_L%d", i), Form("; #it{p}_{T} (GeV/c);#phi (rad); Number of good matches L%d", i), nbPt, 0, 7.5 /* xbins*/, 90, -3.2, 3.2); + mNFakeMatchesPtPhi[i]->Sumw2(); + + mDuplicatedEtaPhi[i] = std::make_unique(Form("mDuplicatedEtaPhi_L%d", i), Form("; #eta;#phi (rad); Number of duplciated clusters L%d", i), 40, -2, 2, 90, -3.2, 3.2); + mDuplicatedEtaPhi[i]->Sumw2(); + mNGoodMatchesEtaPhi[i] = std::make_unique(Form("mNGoodMatchesEtaPhi_L%d", i), Form("; #eta;#phi (rad); Number of good matches L%d", i), 40, -2, 2, 90, -3.2, 3.2); + mNGoodMatchesEtaPhi[i]->Sumw2(); + mNFakeMatchesEtaPhi[i] = std::make_unique(Form("mNFakeMatchesEtaPhi_L%d", i), Form("; #eta;#phi (rad); Number of good matches L%d", i), 40, -2, 2, 90, -3.2, 3.2); + mNFakeMatchesEtaPhi[i]->Sumw2(); + + mDuplicatedEtaAllPt[i] = std::make_unique(Form("mDuplicatedEtaAllPt_L%d", i), Form("; #eta; Number of duplicated clusters L%d", i), 40, -2, 2); + mNGoodMatchesEtaAllPt[i] = std::make_unique(Form("mNGoodMatchesEtaAllPt_L%d", i), Form("; #eta; Number of good matches L%d", i), 40, -2, 2); + mNFakeMatchesEtaAllPt[i] = std::make_unique(Form("mNFakeMatchesEtaAllPt_L%d", i), Form("; #eta; Number of fake matches L%d", i), 40, -2, 2); + + mDuplicatedPhiAllPt[i] = std::make_unique(Form("mDuplicatedPhiAllPt_L%d", i), Form("; #phi (rad); Number of duplicated clusters L%d", i), 90, -3.2, 3.2); + mNGoodMatchesPhiAllPt[i] = std::make_unique(Form("mNGoodMatchesPhiAllPt_L%d", i), Form("; #phi (rad); Number of good matches L%d", i), 90, -3.2, 3.2); + mNFakeMatchesPhiAllPt[i] = std::make_unique(Form("mNFakeMatchesPhiAllPt_L%d", i), Form("; #phi (rad); Number of fake matches L%d", i), 90, -3.2, 3.2); + + mnGoodMatchesPt_layer[i] = std::make_unique(Form("mnGoodMatchesPt_layer_L%d", i), ";pt; nGoodMatches", nbPt, 0, 7.5 /* xbins*/, 20, 0.5, 20.5); + mnFakeMatchesPt_layer[i] = std::make_unique(Form("mnFakeMatchesPt_layer_L%d", i), ";pt; nFakeMatches", nbPt, 0, 7.5 /* xbins*/, 20, 0.5, 20.5); + mnGoodMatchesEta_layer[i] = std::make_unique(Form("mnGoodMatchesEta_layer_L%d", i), ";#eta; nGoodMatches", 40, -2, 2, 20, 0.5, 20.5); + mnFakeMatchesEta_layer[i] = std::make_unique(Form("mnFakeMatchesEta_layer_L%d", i), ";#eta; nFakeMatches", 40, -2, 2, 20, 0.5, 20.5); + mnGoodMatchesPhi_layer[i] = std::make_unique(Form("mnGoodMatchesPhi_layer_L%d", i), ";#Phi; nGoodMatches", 90, -3.2, 3.2, 20, 0.5, 20.5); + mnGoodMatchesPhiOriginal_layer[i] = std::make_unique(Form("mnGoodMatchesPhiOriginal_layer_L%d", i), ";#Phi of the original Cluster; nGoodMatches", 90, -3.2, 3.2, 20, 0.5, 20.5); + mnFakeMatchesPhi_layer[i] = std::make_unique(Form("mnFakeMatchesPhi_layer_L%d", i), ";#Phi; nFakeMatches", 90, -3.2, 3.2, 20, 0.5, 20.5); + + denPt[i] = std::make_unique(Form("denPt_L%d", i), Form("denPt_L%d", i), nbPt, 0, 7.5 /* xbins*/); + numPt[i] = std::make_unique(Form("numPt_L%d", i), Form("numPt_L%d", i), nbPt, 0, 7.5 /* xbins*/); + numPtGood[i] = std::make_unique(Form("numPtGood_L%d", i), Form("numPtGood_L%d", i), nbPt, 0, 7.5 /* xbins*/); + numPtFake[i] = std::make_unique(Form("numPtFake_L%d", i), Form("numPtFake_L%d", i), nbPt, 0, 7.5 /* xbins*/); + + denPhi[i] = std::make_unique(Form("denPhi_L%d", i), Form("denPhi_L%d", i), 90, -3.2, 3.2); + numPhi[i] = std::make_unique(Form("numPhi_L%d", i), Form("numPhi_L%d", i), 90, -3.2, 3.2); + numPhiGood[i] = std::make_unique(Form("numPhiGood_L%d", i), Form("numPhiGood_L%d", i), 90, -3.2, 3.2); + numPhiFake[i] = std::make_unique(Form("numPhiFake_L%d", i), Form("numPhiFake_L%d", i), 90, -3.2, 3.2); + + denEta[i] = std::make_unique(Form("denEta_L%d", i), Form("denEta_L%d", i), 200, -2, 2); + numEta[i] = std::make_unique(Form("numEta_L%d", i), Form("numEta_L%d", i), 200, -2, 2); + numEtaGood[i] = std::make_unique(Form("numEtaGood_L%d", i), Form("numEtaGood_L%d", i), 200, -2, 2); + numEtaFake[i] = std::make_unique(Form("numEtaFake_L%d", i), Form("numEtaFake_L%d", i), 200, -2, 2); + + denRow[i] = std::make_unique(Form("denRow_L%d", i), Form("denRow_L%d", i), 128, -0.5, 511.5); + numRow[i] = std::make_unique(Form("numRow_L%d", i), Form("numRow_L%d", i), 128, -0.5, 511.5); + numRowGood[i] = std::make_unique(Form("numRowGood_L%d", i), Form("numRowGood_L%d", i), 128, -0.5, 511.5); + numRowFake[i] = std::make_unique(Form("numRowFake_L%d", i), Form("numRowFake_L%d", i), 128, -0.5, 511.5); + + denCol[i] = std::make_unique(Form("denCol_L%d", i), Form("denCol_L%d", i), 128, -0.5, 1023.5); + numCol[i] = std::make_unique(Form("numCol_L%d", i), Form("numCol_L%d", i), 128, -0.5, 1023.5); + numColGood[i] = std::make_unique(Form("numColGood_L%d", i), Form("numColGood_L%d", i), 128, -0.5, 1023.5); + numColFake[i] = std::make_unique(Form("numColFake_L%d", i), Form("numColFake_L%d", i), 128, -0.5, 1023.5); + + denZ[i] = std::make_unique(Form("denZ_L%d", i), Form("denZ_L%d", i), 100, -15, 15); + numZ[i] = std::make_unique(Form("numZ_L%d", i), Form("numZ_L%d", i), 100, -15, 15); + numZGood[i] = std::make_unique(Form("numZGood_L%d", i), Form("numZGood_L%d", i), 100, -15, 15); + numZFake[i] = std::make_unique(Form("numZFake_L%d", i), Form("numZFake_L%d", i), 100, -15, 15); + + mDenColEta[i] = std::make_unique(Form("mDenColEta_L%d", i), Form("mDenColEta_L%d", i), 128, -0.5, 1023.5, 50, -1, 1); + mNumColEta[i] = std::make_unique(Form("mNumColEta_L%d", i), Form("mNumColEta_L%d", i), 128, -0.5, 1023.5, 50, -1, 1); + + mDenRowPhi[i] = std::make_unique(Form("mDenRowPhi_L%d", i), Form("mDenRowPhi_L%d", i), 128, -0.5, 511.5, 90, -3.2, 3.2); + mNumRowPhi[i] = std::make_unique(Form("mNumRowPhi_L%d", i), Form("mNumRowPhi_L%d", i), 128, -0.5, 511.5, 90, -3.2, 3.2); + + mDenRowCol[i] = std::make_unique(Form("mDenRowCol_L%d", i), Form("mDenRowCol_L%d", i), 128, -0.5, 511.5, 128, -0.5, 1023.5); + mNumRowCol[i] = std::make_unique(Form("mNumRowCol_L%d", i), Form("mNumRowCol_L%d", i), 128, -0.5, 511.5, 128, -0.5, 1023.5); + + IPOriginalxy[i] = std::make_unique(Form("IPOriginalxy_L%d", i), Form("IPOriginalxy_L%d", i), 500, -0.002, 0.002); + IPOriginalz[i] = std::make_unique(Form("IPOriginalz_L%d", i), Form("IPOriginalz_L%d", i), 200, -10, 10); + + phiFound[i] = std::make_unique(Form("phiFound_L%d", i), Form("phiFound_L%d", i), 190, -3.2, 3.2); + rowFound[i] = std::make_unique(Form("rowFound_L%d", i), Form("rowFound_L%d", i), 128, -0.5, 511.5); + phiNotFound[i] = std::make_unique(Form("phiNotFound_L%d", i), Form("phiNotFound_L%d", i), 90, -3.2, 3.2); + rowNotFound[i] = std::make_unique(Form("rowNotFound_L%d", i), Form("rowNotFound_L%d", i), 128, -0.5, 511.5); + zFound[i] = std::make_unique(Form("zFound_L%d", i), Form("zFound_L%d", i), 100, -15, 15); + zNotFound[i] = std::make_unique(Form("zNotFound%d", i), Form("zNotFound%d", i), 100, -15, 15); + colFoundOriginalVsDuplicated[i] = std::make_unique(Form("colFoundOriginalVsDuplicated_L%d", i), Form("colFoundOriginalVsDuplicated_L%d; Col Original cluster; Col Overlap cluster", i), 9216, -0.5, 9215.5, 9216, -0.5, 9215.5); + colFoundOriginal[i] = std::make_unique(Form("colFoundOriginal_L%d", i), Form("colFoundOriginal_L%d; Col Original cluster;", i), 9216, -0.5, 9215.5); + colNotFound[i] = std::make_unique(Form("colNotFound_L%d", i), Form("colNotFound_L%d", i), 9216, -0.5, 9215.5); + radiusFound[i] = std::make_unique(Form("radiusFound_L%d", i), Form("radiusFound_L%d", i), 80, 0, 6); + radiusNotFound[i] = std::make_unique(Form("radiusNotFound_L%d", i), Form("radiusNotFound_L%d", i), 80, 0, 4); + + for (int j = 0; j < 3; j++) { + mDuplicatedEta[i][j] = std::make_unique(Form("mDuplicatedEta_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #eta; Number of duplicated clusters L%d", mrangesPt[j][0], mrangesPt[j][1], i), 40, -2, 2); + mNGoodMatchesEta[i][j] = std::make_unique(Form("mNGoodMatchesEta_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #eta; Number of good matches L%d", mrangesPt[j][0], mrangesPt[j][1], i), 40, -2, 2); + mNFakeMatchesEta[i][j] = std::make_unique(Form("mNFakeMatchesEta_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #eta; Number of fake matches L%d", mrangesPt[j][0], mrangesPt[j][1], i), 40, -2, 2); + + mDuplicatedPhi[i][j] = std::make_unique(Form("mDuplicatedPhi_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #phi; Number of duplicated clusters L%d", mrangesPt[j][0], mrangesPt[j][1], i), 90, -3.2, 3.2); + mNGoodMatchesPhi[i][j] = std::make_unique(Form("mNGoodMatchesPhi_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #phi; Number of good matches L%d", mrangesPt[j][0], mrangesPt[j][1], i), 90, -3.2, 3.2); + mNFakeMatchesPhi[i][j] = std::make_unique(Form("mNFakeMatchesPhi_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #phi; Number of fake matches L%d", mrangesPt[j][0], mrangesPt[j][1], i), 90, -3.2, 3.2); + } + } + gStyle->SetPalette(55); +} + +void EfficiencyStudy::run(ProcessingContext& pc) +{ + LOGP(info, "--------------- run"); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + initialiseRun(recoData); + process(recoData); +} + +void EfficiencyStudy::initialiseRun(o2::globaltracking::RecoContainer& recoData) +{ + LOGP(info, "--------------- initialiseRun"); + if (mUseMC) { + mTracksMCLabels = recoData.getITSTracksMCLabels(); + mClustersMCLCont = recoData.getITSClustersMCLabels(); + } + mITSClustersArray.clear(); + mTracksROFRecords = recoData.getITSTracksROFRecords(); + mTracks = recoData.getITSTracks(); + mClusters = recoData.getITSClusters(); + mClustersROFRecords = recoData.getITSClustersROFRecords(); + mClusPatterns = recoData.getITSClustersPatterns(); + mInputITSidxs = recoData.getITSTracksClusterRefs(); + mITSClustersArray.reserve(mClusters.size()); + auto pattIt = mClusPatterns.begin(); + o2::its::ioutils::convertCompactClusters(mClusters, pattIt, mITSClustersArray, mDict); // clusters converted to 3D spacepoints +} + +void EfficiencyStudy::stileEfficiencyGraph(std::unique_ptr& eff, const char* name, const char* title, bool bidimensional = false, const int markerStyle = kFullCircle, const double markersize = 1, const int markercolor = kBlack, const int linecolor = kBlack) +{ + eff->SetName(name); + eff->SetTitle(title); + if (!bidimensional) { + eff->SetMarkerStyle(markerStyle); + eff->SetMarkerSize(markersize); + eff->SetMarkerColor(markercolor); + eff->SetLineColor(linecolor); + } +} + +int EfficiencyStudy::getDCAClusterTrackMC(int countDuplicated = 0) +{ + // get the DCA between the clusters and the track from MC and fill histograms: distance between original and duplicated cluster, DCA, phi, clusters + // used to study the DCA cut to be applied + LOGP(info, "--------------- getDCAClusterTrackMC"); + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::array clusOriginalDCA, clusDuplicatedDCA; + auto propagator = o2::base::Propagator::Instance(); + + auto bz = o2::base::Propagator::Instance()->getNominalBz(); + LOG(info) << ">>>>>>>>>>>> Magnetic field: " << bz; + + unsigned int rofIndexTrack = 0; + unsigned int rofNEntriesTrack = 0; + unsigned int rofIndexClus = 0; + unsigned int rofNEntriesClus = 0; + int nLabels = 0; + unsigned int totClus = 0; + + int duplicated = 0; + + std::unordered_map> label_vecClus[mClustersROFRecords.size()][NLAYERS]; // array of maps nRofs x Nlayers -> {label, vec(iClus)} where vec(iClus) are the clusters that share the same label + + for (unsigned int iROF = 0; iROF < mTracksROFRecords.size(); iROF++) { // loop on ROFRecords array + rofIndexTrack = mTracksROFRecords[iROF].getFirstEntry(); + rofNEntriesTrack = mTracksROFRecords[iROF].getNEntries(); + + rofIndexClus = mClustersROFRecords[iROF].getFirstEntry(); + rofNEntriesClus = mClustersROFRecords[iROF].getNEntries(); + + for (unsigned int iTrack = rofIndexTrack; iTrack < rofIndexTrack + rofNEntriesTrack; iTrack++) { // loop on tracks per ROF + auto track = mTracks[iTrack]; + o2::track::TrackParCov trackParCov = mTracks[iTrack]; + int firstClus = track.getFirstClusterEntry(); // get the first cluster of the track + int ncl = track.getNumberOfClusters(); // get the number of clusters of the track + + if (ncl < 7) { + continue; + } + + float ip[2]; // IP from 0,0,0 and the track should be the deplacement of the primary vertex + track.getImpactParams(0, 0, 0, 0, ip); + + // if (abs(ip[0])>0.001 ) continue; ///pv not in (0,0,0) + + auto& tracklab = mTracksMCLabels[iTrack]; + if (tracklab.isFake()) { + continue; + } + + auto pt = trackParCov.getPt(); + auto eta = trackParCov.getEta(); + + // if (pt < mPtCuts[0] || pt > mPtCuts[1]) { + // continue; + // } + // if (eta < mEtaCuts[0] || eta > mEtaCuts[1]) { + // continue; + // } + + float phioriginal = 0; + float phiduplicated = 0; + + for (int iclTrack = firstClus; iclTrack < firstClus + ncl; iclTrack++) { // loop on clusters associated to the track + auto& clusOriginal = mClusters[mInputITSidxs[iclTrack]]; + auto clusOriginalPoint = mITSClustersArray[mInputITSidxs[iclTrack]]; // cluster spacepoint in the tracking system + auto staveOriginal = mGeometry->getStave(clusOriginal.getSensorID()); + auto chipOriginal = mGeometry->getChipIdInStave(clusOriginal.getSensorID()); + + UShort_t rowOriginal = clusOriginal.getRow(); + UShort_t colOriginal = clusOriginal.getCol(); + + auto layer = mGeometry->getLayer(clusOriginal.getSensorID()); + if (layer >= NLAYERS) { + continue; // checking only selected layers + } + auto labsTrack = mClustersMCLCont->getLabels(mInputITSidxs[iclTrack]); // get labels of the cluster associated to the track + + o2::math_utils::Point3D clusOriginalPointTrack = {clusOriginalPoint.getX(), clusOriginalPoint.getY(), clusOriginalPoint.getZ()}; + o2::math_utils::Point3D clusOriginalPointGlob = mGeometry->getMatrixT2G(clusOriginal.getSensorID()) * clusOriginalPointTrack; + + phioriginal = clusOriginalPointGlob.phi(); // * 180 / M_PI; + + mPhiOriginal[layer]->Fill(phioriginal); + mPtOriginal[layer]->Fill(pt); + mEtaOriginal[layer]->Fill(eta); + m3DClusterPositions->Fill(clusOriginalPointGlob.x(), clusOriginalPointGlob.y(), clusOriginalPointGlob.z()); + m2DClusterOriginalPositions->Fill(clusOriginalPointGlob.x(), clusOriginalPointGlob.y()); + + for (auto& labT : labsTrack) { // for each valid label iterate over ALL the clusters in the ROF to see if there are duplicates + if (labT != tracklab) { + continue; + } + nLabels++; + if (labT.isValid()) { + for (unsigned int iClus = rofIndexClus; iClus < rofIndexClus + rofNEntriesClus; iClus++) { // iteration over ALL the clusters in the ROF + + auto clusDuplicated = mClusters[iClus]; + auto clusDuplicatedPoint = mITSClustersArray[iClus]; + + auto layerClus = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerClus != layer) { + continue; + } + + o2::math_utils::Point3D clusDuplicatedPointTrack = {clusDuplicatedPoint.getX(), clusDuplicatedPoint.getY(), clusDuplicatedPoint.getZ()}; + o2::math_utils::Point3D clusDuplicatedPointGlob = mGeometry->getMatrixT2G(clusDuplicated.getSensorID()) * clusDuplicatedPointTrack; + // phiduplicated = std::atan2(clusDuplicatedPointGlob.y(), clusDuplicatedPointGlob.x()) * 180 / M_PI + 180; + phiduplicated = clusDuplicatedPointGlob.phi(); // * 180 / M_PI; + + auto labsClus = mClustersMCLCont->getLabels(iClus); // ideally I can have more than one label per cluster + for (auto labC : labsClus) { + if (labC == labT) { + label_vecClus[iROF][layerClus][labT].push_back(iClus); // same cluster: label from the track = label from the cluster + // if a duplicate cluster is found, propagate the track to the duplicate cluster and compute the distance from the original cluster + // if (clusOriginalPointGlob != clusDuplicatedPointGlob) { /// check that the duplicated cluster is not the original one just counted twice + // if (clusDuplicated.getSensorID() != clusOriginal.getSensorID()) { /// check that the duplicated cluster is not the original one just counted twice + + // applying constraints: the cluster should be on the same layer, should be on an adjacent stave and on the same or adjacent chip position + if (clusDuplicated.getSensorID() == clusOriginal.getSensorID()) { + continue; + } + auto layerDuplicated = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerDuplicated != layerClus) { + continue; + } + auto staveDuplicated = mGeometry->getStave(clusDuplicated.getSensorID()); + if (abs(staveDuplicated - staveOriginal) != 1) { + continue; + } + auto chipDuplicated = mGeometry->getChipIdInStave(clusDuplicated.getSensorID()); + if (abs(chipDuplicated - chipOriginal) > 1) { + continue; + } + + duplicated++; + + if (countDuplicated == 0) { + UShort_t rowDuplicated = clusDuplicated.getRow(); + UShort_t colDuplicated = clusDuplicated.getCol(); + + chipRowDuplicated[layerDuplicated]->Fill(rowDuplicated); + chipRowOriginalIfDuplicated[layerDuplicated]->Fill(rowOriginal); + + mDuplicated_layer[layerDuplicated]++; // This has to be incremented at the first call + mPtDuplicated[layerClus]->Fill(pt); + mEtaDuplicated[layerClus]->Fill(eta); + mPhiDuplicated[layerClus]->Fill(phiduplicated); + mZvsPhiDUplicated[layerClus]->Fill(clusDuplicatedPointGlob.Z(), phiduplicated); + mPhiOriginalIfDuplicated[layerClus]->Fill(phioriginal); + } + + if (countDuplicated == 1) { + for (int ipt = 0; ipt < 3; ipt++) { + if (pt >= mrangesPt[ipt][0] && pt < mrangesPt[ipt][1]) { + mDuplicatedEta[layerDuplicated][ipt]->Fill(eta); + mDuplicatedPhi[layerDuplicated][ipt]->Fill(phiduplicated); + } + } + UShort_t rowDuplicated = clusDuplicated.getRow(); + mDuplicatedRow[layerDuplicated]->Fill(rowOriginal); + mDuplicatedCol[layerDuplicated]->Fill(clusOriginal.getCol()); + mDuplicatedZ[layerDuplicated]->Fill(clusOriginalPointGlob.Z()); + mDuplicatedPt[layerDuplicated]->Fill(pt); + mDuplicatedPtEta[layerDuplicated]->Fill(pt, eta); + mDuplicatedPtPhi[layerDuplicated]->Fill(pt, phiduplicated); + mDuplicatedEtaPhi[layerDuplicated]->Fill(eta, phiduplicated); + + mDuplicatedEtaAllPt[layerDuplicated]->Fill(eta); + mDuplicatedPhiAllPt[layerDuplicated]->Fill(phiduplicated); + mPt_EtaDupl[layerClus]->Fill(pt, eta); + } + + m3DClusterPositions->Fill(clusDuplicatedPointGlob.x(), clusDuplicatedPointGlob.y(), clusDuplicatedPointGlob.z()); + m2DClusterDuplicatedPositions->Fill(clusDuplicatedPointGlob.x(), clusDuplicatedPointGlob.y()); + + /// Compute the DCA between the cluster location and the track + + /// first propagate to the original cluster + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusOriginal.getSensorID())); + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusOriginal.getSensorID())); + if (propagator->propagateToDCA(clusOriginalPointGlob, trackParCov, b, 2.f, matCorr, &clusOriginalDCA)) { + mDCAxyOriginal[layerClus]->Fill(clusOriginalDCA[0]); + mDCAzOriginal[layerClus]->Fill(clusOriginalDCA[1]); + } + /// then propagate to the duplicated cluster + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusDuplicated.getSensorID())); + if (propagator->propagateToDCA(clusDuplicatedPointGlob, trackParCov, b, 2.f, matCorr, &clusDuplicatedDCA)) { + mDCAxyDuplicated->Fill(clusDuplicatedDCA[0]); + mDCAzDuplicated->Fill(clusDuplicatedDCA[1]); + mDCAxyDuplicated_layer[layerDuplicated]->Fill(clusDuplicatedDCA[0]); + mDCAzDuplicated_layer[layerDuplicated]->Fill(clusDuplicatedDCA[1]); + } + /////////////////////////////////////////////////////// + } + } + } + } + } + } // end loop on clusters + totClus += NLAYERS; // summing only the number of clusters in the considered layers. Since the imposition of 7-clusters tracks, if the track is valid should release as clusters as the number of considered layers + } // end loop on tracks per ROF + } // end loop on ROFRecords array + LOGP(info, "Total number of clusters: {} ", totClus); + LOGP(info, "total nLabels: {}", nLabels); + LOGP(info, "Number of duplicated clusters: {}", duplicated); + + if (mVerboseOutput && mUseMC) { + // printing the duplicates + for (unsigned int iROF = 0; iROF < mClustersROFRecords.size(); iROF++) { + LOGP(info, "°°°°°°°°°°°°°°°°°°°°°°°° ROF {} °°°°°°°°°°°°°°°°°°°°°°°°", iROF); + for (unsigned int lay = 0; lay < NLAYERS; lay++) { + LOGP(info, "°°°°°°°°°°°°°°°°°°°°°°°° LAYER {} °°°°°°°°°°°°°°°°°°°°°°°°", lay); + for (auto& it : label_vecClus[iROF][lay]) { + if (it.second.size() <= 1) { + continue; // printing only duplicates + } + std::cout << " \n++++++++++++ Label: "; + auto label = it.first; + it.first.print(); + for (auto iClus : it.second) { + auto name = mGeometry->getSymbolicName(mClusters[iClus].getSensorID()); + auto chipid = mClusters[iClus].getChipID(); + auto clus = mClusters[iClus]; + auto clusPoint = mITSClustersArray[iClus]; + + o2::math_utils::Point3D clusPointTrack = {clusPoint.getX(), clusPoint.getY(), clusPoint.getZ()}; + o2::math_utils::Point3D clusPointGlob = mGeometry->getMatrixT2G(clus.getSensorID()) * clusPointTrack; + std::cout << "ROF: " << iROF << ", iClus: " << iClus << " -> chip: " << chipid << " = " << name << std::endl; + LOGP(info, "LOCtrack: {} {} {}", clusPointTrack.x(), clusPointTrack.y(), clusPointTrack.z()); + LOGP(info, "LOCglob {} {} {}", clusPointGlob.x(), clusPointGlob.y(), clusPointGlob.z()); + } + } + } + } + } + return duplicated; +} + +void EfficiencyStudy::countDuplicatedAfterCuts() +{ + // count the effective number of duplicated cluster good matches after applying the pt eta and phi cuts on the track + // to check the applied cuts + LOGP(info, "--------------- countDuplicatedAfterCuts"); + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::array clusOriginalDCA, clusDuplicatedDCA; + auto propagator = o2::base::Propagator::Instance(); + + unsigned int rofIndexTrack = 0; + unsigned int rofNEntriesTrack = 0; + unsigned int rofIndexClus = 0; + unsigned int rofNEntriesClus = 0; + int nLabels = 0; + unsigned int totClus = 0; + + int duplicated[3] = {0}; + int possibleduplicated[3] = {0}; + + std::cout << "Track candidates: " << std::endl; + + std::unordered_map> label_vecClus[mClustersROFRecords.size()][NLAYERS]; // array of maps nRofs x Nlayers -> {label, vec(iClus)} where vec(iClus) are the clusters that share the same label + + for (unsigned int iROF = 0; iROF < mTracksROFRecords.size(); iROF++) { // loop on ROFRecords array + std::cout << "ROF number: " << iROF << std::endl; + rofIndexTrack = mTracksROFRecords[iROF].getFirstEntry(); + rofNEntriesTrack = mTracksROFRecords[iROF].getNEntries(); + + rofIndexClus = mClustersROFRecords[iROF].getFirstEntry(); + rofNEntriesClus = mClustersROFRecords[iROF].getNEntries(); + + for (unsigned int iTrack = rofIndexTrack; iTrack < rofIndexTrack + rofNEntriesTrack; iTrack++) { // loop on tracks per ROF + + auto track = mTracks[iTrack]; + o2::track::TrackParCov trackParCov = mTracks[iTrack]; + int firstClus = track.getFirstClusterEntry(); // get the first cluster of the track + int ncl = track.getNumberOfClusters(); // get the number of clusters of the track + + if (ncl < 7) { + continue; + } + + auto& tracklab = mTracksMCLabels[iTrack]; + if (tracklab.isFake()) { + continue; + } + + auto eta = trackParCov.getEta(); + + // applying the cuts on the track - only eta + + if (eta < mEtaCuts[0] || eta >= mEtaCuts[1]) { + continue; + } + + float phi = -999.; + float phiOriginal = -999.; + + for (int iclTrack = firstClus; iclTrack < firstClus + ncl; iclTrack++) { // loop on clusters associated to the track + auto& clusOriginal = mClusters[mInputITSidxs[iclTrack]]; + auto clusOriginalPoint = mITSClustersArray[mInputITSidxs[iclTrack]]; // cluster spacepoint in the tracking system + auto layerOriginal = mGeometry->getLayer(clusOriginal.getSensorID()); + auto staveOriginal = mGeometry->getStave(clusOriginal.getSensorID()); + auto chipOriginal = mGeometry->getChipIdInStave(clusOriginal.getSensorID()); + + auto layer = mGeometry->getLayer(clusOriginal.getSensorID()); + if (layer >= NLAYERS) { + continue; // checking only selected layers + } + auto labsTrack = mClustersMCLCont->getLabels(mInputITSidxs[iclTrack]); // get labels of the cluster associated to the track + + o2::math_utils::Point3D clusOriginalPointTrack = {clusOriginalPoint.getX(), clusOriginalPoint.getY(), clusOriginalPoint.getZ()}; + o2::math_utils::Point3D clusOriginalPointGlob = mGeometry->getMatrixT2G(clusOriginal.getSensorID()) * clusOriginalPointTrack; + phiOriginal = clusOriginalPointGlob.phi(); // * 180 / M_PI; + + if (abs(clusOriginalPointGlob.y()) < 0.5) { ///// excluding gap between bottom and top barrels + continue; + } + + if (abs(clusOriginalPointGlob.z()) >= 10) { /// excluding external z + continue; + } + + if (clusOriginal.getRow() < 2 || (clusOriginal.getRow() > 15 && clusOriginal.getRow() < 496) || clusOriginal.getRow() > 509) { //// cutting on the row + continue; + } + + if (clusOriginal.getCol() < 160 || clusOriginal.getCol() > 870) { /// excluding the gap between two chips in the same stave (comment to obtain the plot efficiency col vs eta) + continue; + } + + for (auto& labT : labsTrack) { // for each valid label iterate over ALL the clusters in the ROF to see if there are duplicates + if (labT != tracklab) { + continue; + } + + if (labT.isValid()) { + for (unsigned int iClus = rofIndexClus; iClus < rofIndexClus + rofNEntriesClus; iClus++) { // iteration over ALL the clusters in the ROF + + auto clusDuplicated = mClusters[iClus]; + auto clusDuplicatedPoint = mITSClustersArray[iClus]; + + auto layerClus = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerClus != layer) { + continue; + } + + o2::math_utils::Point3D clusDuplicatedPointTrack = {clusDuplicatedPoint.getX(), clusDuplicatedPoint.getY(), clusDuplicatedPoint.getZ()}; + o2::math_utils::Point3D clusDuplicatedPointGlob = mGeometry->getMatrixT2G(clusDuplicated.getSensorID()) * clusDuplicatedPointTrack; + phi = clusDuplicatedPointGlob.phi(); // * 180 / M_PI; + + auto labsClus = mClustersMCLCont->getLabels(iClus); // ideally I can have more than one label per cluster + for (auto labC : labsClus) { + if (labC == labT) { + label_vecClus[iROF][layerClus][labT].push_back(iClus); // same cluster: label from the track = label from the cluster + // if a duplicate cluster is found, propagate the track to the duplicate cluster and compute the distance from the original cluster + // if (clusOriginalPointGlob != clusDuplicatedPointGlob) { /// check that the duplicated cluster is not the original one just counted twice + // if (clusDuplicated.getSensorID() != clusOriginal.getSensorID()) { /// check that the duplicated cluster is not the original one just counted twice + + // applying constraints: the cluster should be on the same layer, should be on an adjacent stave and on the same or adjacent chip position + if (clusDuplicated.getSensorID() == clusOriginal.getSensorID()) { + continue; + } + auto layerDuplicated = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerDuplicated != layerClus) { + continue; + } + auto staveDuplicated = mGeometry->getStave(clusDuplicated.getSensorID()); + if (abs(staveDuplicated - staveOriginal) != 1) { + continue; + } + auto chipDuplicated = mGeometry->getChipIdInStave(clusDuplicated.getSensorID()); + if (abs(chipDuplicated - chipOriginal) > 1) { + continue; + } + + duplicated[layer]++; + std::cout << "Taken L" << layer << " # " << duplicated[layer] << " : eta, phi = " << eta << " , " << phiOriginal << " Label: " << std::endl; + labC.print(); + } + } + } + } + } + } // end loop on clusters + totClus += ncl; + } // end loop on tracks per ROF + } // end loop on ROFRecords array + + LOGP(info, "Total number of possible cluster duplicated in L0: {} ", possibleduplicated[0]); + LOGP(info, "Total number of possible cluster duplicated in L1: {} ", possibleduplicated[1]); + LOGP(info, "Total number of possible cluster duplicated in L2: {} ", possibleduplicated[2]); + + LOGP(info, "Total number of cluster duplicated in L0: {} ", duplicated[0]); + LOGP(info, "Total number of cluster duplicated in L1: {} ", duplicated[1]); + LOGP(info, "Total number of cluster duplicated in L2: {} ", duplicated[2]); +} + +void EfficiencyStudy::studyDCAcutsMC() +{ + //// Study the DCA cuts to be applied + + LOGP(info, "--------------- studyDCAcutsMC"); + + int duplicated = getDCAClusterTrackMC(0); + + double meanDCAxyDuplicated[NLAYERS] = {0}; + double meanDCAzDuplicated[NLAYERS] = {0}; + double sigmaDCAxyDuplicated[NLAYERS] = {0}; + double sigmaDCAzDuplicated[NLAYERS] = {0}; + + std::ofstream ofs("dcaValues.csv", std::ofstream::out); + ofs << "layer,dcaXY,dcaZ,sigmaDcaXY,sigmaDcaZ" << std::endl; + + for (int l = 0; l < NLAYERS; l++) { + meanDCAxyDuplicated[l] = mDCAxyDuplicated_layer[l]->GetMean(); + meanDCAzDuplicated[l] = mDCAzDuplicated_layer[l]->GetMean(); + sigmaDCAxyDuplicated[l] = mDCAxyDuplicated_layer[l]->GetRMS(); + sigmaDCAzDuplicated[l] = mDCAzDuplicated_layer[l]->GetRMS(); + ofs << l << "," << meanDCAxyDuplicated[l] << "," << meanDCAzDuplicated[l] << "," << sigmaDCAxyDuplicated[l] << "," << sigmaDCAzDuplicated[l] << std::endl; + } + + for (int l = 0; l < NLAYERS; l++) { + LOGP(info, "meanDCAxyDuplicated L{}: {}, meanDCAzDuplicated: {}, sigmaDCAxyDuplicated: {}, sigmaDCAzDuplicated: {}", l, meanDCAxyDuplicated[l], meanDCAzDuplicated[l], sigmaDCAxyDuplicated[l], sigmaDCAzDuplicated[l]); + } + // now we have the DCA distribution: + // ->iterate again over tracks and over duplicated clusters and find the matching ones basing on DCA cuts (1 sigma, 2 sigma,...) + // then control if the matching ones according to the DCA matches have the same label + // if yes, then we have a good match -> increase the good match counter + // if not, keep it as a fake match -> increase the fake match counter + // the efficiency of each one will be match counter / total of the duplicated clusters + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::array clusOriginalDCA, clusDuplicatedDCA; + auto propagator = o2::base::Propagator::Instance(); + + unsigned int rofIndexTrack = 0; + unsigned int rofNEntriesTrack = 0; + unsigned int rofIndexClus = 0; + unsigned int rofNEntriesClus = 0; + int nLabels = 0; + unsigned int totClus = 0; + + unsigned int nDCAMatches[20] = {0}; + unsigned int nGoodMatches[20] = {0}; + unsigned int nFakeMatches[20] = {0}; + + unsigned int nGoodMatches_layer[NLAYERS][20] = {0}; + unsigned int nFakeMatches_layer[NLAYERS][20] = {0}; + + int nbPt = 75; + double xbins[nbPt + 1], ptcutl = 0.05, ptcuth = 7.5; + double a = std::log(ptcuth / ptcutl) / nbPt; + for (int i = 0; i <= nbPt; i++) { + xbins[i] = ptcutl * std::exp(i * a); + } + + for (unsigned int iROF = 0; iROF < mTracksROFRecords.size(); iROF++) { // loop on ROFRecords array + rofIndexTrack = mTracksROFRecords[iROF].getFirstEntry(); + rofNEntriesTrack = mTracksROFRecords[iROF].getNEntries(); + + rofIndexClus = mClustersROFRecords[iROF].getFirstEntry(); + rofNEntriesClus = mClustersROFRecords[iROF].getNEntries(); + + for (unsigned int iTrack = rofIndexTrack; iTrack < rofIndexTrack + rofNEntriesTrack; iTrack++) { // loop on tracks per ROF + auto track = mTracks[iTrack]; + o2::track::TrackParCov trackParCov = mTracks[iTrack]; + auto pt = trackParCov.getPt(); + auto eta = trackParCov.getEta(); + + float ip[2]; + track.getImpactParams(0, 0, 0, 0, ip); + + float phi = -999.; + float phiOriginal = -999.; + int firstClus = track.getFirstClusterEntry(); // get the first cluster of the track + int ncl = track.getNumberOfClusters(); // get the number of clusters of the track + + if (ncl < 7) { + continue; + } + + auto& tracklab = mTracksMCLabels[iTrack]; + if (tracklab.isFake()) { + continue; + } + + if (mVerboseOutput) { + LOGP(info, "--------- track Label: "); + tracklab.print(); + } + + for (int iclTrack = firstClus; iclTrack < firstClus + ncl; iclTrack++) { // loop on clusters associated to the track to extract layer, stave and chip to restrict the possible matches to be searched with the DCA cut + auto& clusOriginal = mClusters[mInputITSidxs[iclTrack]]; + auto clusOriginalPoint = mITSClustersArray[mInputITSidxs[iclTrack]]; // cluster spacepoint in the tracking system + auto layerOriginal = mGeometry->getLayer(clusOriginal.getSensorID()); + if (layerOriginal >= NLAYERS) { + continue; + } + auto labsOriginal = mClustersMCLCont->getLabels(mInputITSidxs[iclTrack]); // get labels of the cluster associated to the track (original) + auto staveOriginal = mGeometry->getStave(clusOriginal.getSensorID()); + auto chipOriginal = mGeometry->getChipIdInStave(clusOriginal.getSensorID()); + + o2::math_utils::Point3D clusOriginalPointTrack = {clusOriginalPoint.getX(), clusOriginalPoint.getY(), clusOriginalPoint.getZ()}; + o2::math_utils::Point3D clusOriginalPointGlob = mGeometry->getMatrixT2G(clusOriginal.getSensorID()) * clusOriginalPointTrack; + + phiOriginal = clusOriginalPointGlob.phi(); // * 180 / M_PI; + + for (auto& labT : labsOriginal) { // for each valid label iterate over ALL the clusters in the ROF to see if there are duplicates + if (labT != tracklab) { + continue; + } + if (!labT.isValid()) { + continue; + } + + /// for each oroginal cluster iterate over all the possible "adjacent" clusters (stave +-1, chip =,+-1) and calculate the DCA with the track. Then compare the cluster label with the track label to see if it is a true or fake match + for (unsigned int iClus = rofIndexClus; iClus < rofIndexClus + rofNEntriesClus; iClus++) { // iteration over ALL the clusters in the ROF + auto clusDuplicated = mClusters[iClus]; + //// applying constraints: the cluster should be on the same layer, should be on an adjacent stave and on the same or adjacent chip position + if (clusDuplicated.getSensorID() == clusOriginal.getSensorID()) { + continue; + } + auto layerDuplicated = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerDuplicated != layerOriginal) { + continue; + } + auto staveDuplicated = mGeometry->getStave(clusDuplicated.getSensorID()); + if (abs(staveDuplicated - staveOriginal) != 1) { + continue; + } + auto chipDuplicated = mGeometry->getChipIdInStave(clusDuplicated.getSensorID()); + if (abs(chipDuplicated - chipOriginal) > 1) { + continue; + } + + auto labsDuplicated = mClustersMCLCont->getLabels(iClus); + + /// if the cheks are passed, then calculate the DCA + auto clusDuplicatedPoint = mITSClustersArray[iClus]; + + o2::math_utils::Point3D clusDuplicatedPointTrack = {clusDuplicatedPoint.getX(), clusDuplicatedPoint.getY(), clusDuplicatedPoint.getZ()}; + o2::math_utils::Point3D clusDuplicatedPointGlob = mGeometry->getMatrixT2G(clusDuplicated.getSensorID()) * clusDuplicatedPointTrack; + phi = clusDuplicatedPointGlob.phi(); // * 180 / M_PI; + + /// Compute the DCA between the duplicated cluster location and the track + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusDuplicated.getSensorID())); + if (propagator->propagateToDCA(clusDuplicatedPointGlob, trackParCov, b, 2.f, matCorr, &clusDuplicatedDCA)) { // check if the propagation fails + if (mVerboseOutput) { + LOGP(info, "Propagation ok"); + } + /// checking the DCA for 20 different sigma ranges + for (int i = 0; i < 20; i++) { + if (abs(dcaXY[layerDuplicated] - clusDuplicatedDCA[0]) < (i + 1) * sigmaDcaXY[layerDuplicated] && abs(dcaZ[layerDuplicated] - clusDuplicatedDCA[1]) < (i + 1) * sigmaDcaZ[layerDuplicated]) { // check if the DCA is within the cut i*sigma + + if (mVerboseOutput) { + LOGP(info, "Check DCA ok: {} < {}; {} < {}", abs(meanDCAxyDuplicated[layerDuplicated] - clusDuplicatedDCA[0]), (i + 1) * sigmaDCAxyDuplicated[layerDuplicated], abs(meanDCAzDuplicated[layerDuplicated] - clusDuplicatedDCA[1]), (i + 1) * sigmaDCAzDuplicated[layerDuplicated]); + } + nDCAMatches[i]++; + bool isGoodMatch = false; + + for (auto labD : labsDuplicated) { // at this point the track has been matched with a duplicated cluster based on the DCA cut. Now we check if the matching is good ore not based on the label + if (mVerboseOutput) { + LOGP(info, "tracklab, labD:"); + tracklab.print(); + labD.print(); + } + if (labD == tracklab) { //// check if the label of the origial cluster is equal to the label of the duplicated cluster among all the labels for a cluster + isGoodMatch = true; + continue; + } + } + + if (isGoodMatch) { + nGoodMatches[i]++; + nGoodMatches_layer[layerDuplicated][i]++; + mnGoodMatchesPt_layer[layerDuplicated]->Fill(pt, i); + mnGoodMatchesEta_layer[layerDuplicated]->Fill(eta, i); + mnGoodMatchesPhi_layer[layerDuplicated]->Fill(phi, i); + mnGoodMatchesPhiOriginal_layer[layerDuplicated]->Fill(phiOriginal, i); + } else { + + nFakeMatches[i]++; + nFakeMatches_layer[layerDuplicated][i]++; + mnFakeMatchesPt_layer[layerDuplicated]->Fill(pt, i); + mnFakeMatchesEta_layer[layerDuplicated]->Fill(eta, i); + mnFakeMatchesPhi_layer[layerDuplicated]->Fill(phi, i); + } + } else if (mVerboseOutput) { + LOGP(info, "Check DCA failed"); + } + } + } else if (mVerboseOutput) { + LOGP(info, "Propagation failed"); + } + } // end loop on all the clusters in the rof + } + } // end loop on clusters associated to the track + } // end loop on tracks per ROF + } // end loop on ROFRecords array + + for (int i = 0; i < 20; i++) { + LOGP(info, "Cut: {} sigma -> number of duplicated clusters: {} nDCAMatches: {} nGoodMatches: {} nFakeMatches: {}", i + 1, duplicated, nDCAMatches[i], nGoodMatches[i], nFakeMatches[i]); + mEfficiencyGoodMatch->SetBinContent(i + 1, nGoodMatches[i]); + mEfficiencyFakeMatch->SetBinContent(i + 1, nFakeMatches[i]); + mEfficiencyTotal->SetBinContent(i + 1, double(nGoodMatches[i] + nFakeMatches[i])); + + for (int l = 0; l < NLAYERS; l++) { + mEfficiencyGoodMatch_layer[l]->SetBinContent(i + 1, nGoodMatches_layer[l][i]); + mEfficiencyFakeMatch_layer[l]->SetBinContent(i + 1, nFakeMatches_layer[l][i]); + mEfficiencyTotal_layer[l]->SetBinContent(i + 1, double(nGoodMatches_layer[l][i] + nFakeMatches_layer[l][i])); + + for (int ipt = 0; ipt < mPtDuplicated[l]->GetNbinsX(); ipt++) { + if (mPtDuplicated[l]->GetBinContent(ipt + 1) != 0) { + mEfficiencyGoodMatchPt_layer[l]->SetBinContent(ipt + 1, i + 1, mnGoodMatchesPt_layer[l]->GetBinContent(ipt + 1, i + 1) / mPtDuplicated[l]->GetBinContent(ipt + 1)); + } + mEfficiencyFakeMatchPt_layer[l]->SetBinContent(ipt + 1, i + 1, mnFakeMatchesPt_layer[l]->GetBinContent(ipt + 1, i + 1) / mPtDuplicated[l]->GetBinContent(ipt + 1)); + } + + for (int ieta = 0; ieta < mEtaDuplicated[l]->GetNbinsX(); ieta++) { + if (mEtaDuplicated[l]->GetBinContent(ieta + 1) != 0) { + mEfficiencyGoodMatchEta_layer[l]->SetBinContent(ieta + 1, i + 1, mnGoodMatchesEta_layer[l]->GetBinContent(ieta + 1, i + 1) / mEtaDuplicated[l]->GetBinContent(ieta + 1)); + } + mEfficiencyFakeMatchEta_layer[l]->SetBinContent(ieta + 1, i + 1, mnFakeMatchesEta_layer[l]->GetBinContent(ieta + 1, i + 1) / mEtaDuplicated[l]->GetBinContent(ieta + 1)); + } + + for (int iphi = 0; iphi < mPhiDuplicated[l]->GetNbinsX(); iphi++) { + if (mPhiDuplicated[l]->GetBinContent(iphi + 1) != 0) { + mEfficiencyGoodMatchPhi_layer[l]->SetBinContent(iphi + 1, i + 1, mnGoodMatchesPhi_layer[l]->GetBinContent(iphi + 1, i + 1) / mPhiDuplicated[l]->GetBinContent(iphi + 1)); + } + mEfficiencyFakeMatchPhi_layer[l]->SetBinContent(iphi + 1, i + 1, mnFakeMatchesPhi_layer[l]->GetBinContent(iphi + 1, i + 1) / mPhiDuplicated[l]->GetBinContent(iphi + 1)); + } + + for (int iphi = 0; iphi < mPhiOriginalIfDuplicated[l]->GetNbinsX(); iphi++) { + if (mPhiOriginalIfDuplicated[l]->GetBinContent(iphi + 1) != 0) { + mEfficiencyGoodMatchPhiOriginal_layer[l]->SetBinContent(iphi + 1, i + 1, mnGoodMatchesPhiOriginal_layer[l]->GetBinContent(iphi + 1, i + 1) / mPhiOriginalIfDuplicated[l]->GetBinContent(iphi + 1)); + } + } + } + } + for (int i = 0; i < NLAYERS; i++) { + std::cout << "+++++++++ Duplicated in layer L" << i << ": " << mDuplicated_layer[i] << std::endl; + } + + for (int l = 0; l < NLAYERS; l++) { + mEfficiencyGoodMatch_layer[l]->Scale(1. / double(mDuplicated_layer[l]), "b"); + mEfficiencyFakeMatch_layer[l]->Scale(1. / double(mDuplicated_layer[l]), "b"); + mEfficiencyTotal_layer[l]->Scale(1. / double(mDuplicated_layer[l]), "b"); + } + + mEfficiencyGoodMatch->Scale(1. / double(duplicated), "b"); + mEfficiencyFakeMatch->Scale(1. / double(duplicated), "b"); + mEfficiencyTotal->Scale(1. / double(duplicated), "b"); + + mOutFile->mkdir("EffVsDCA2D/"); + mOutFile->cd("EffVsDCA2D/"); + for (int l = 0; l < NLAYERS; l++) { + mEfficiencyGoodMatchPt_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyGoodMatchPt_layer[l]->Write(); + mEfficiencyGoodMatchEta_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyGoodMatchEta_layer[l]->Write(); + mEfficiencyGoodMatchPhi_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyGoodMatchPhi_layer[l]->Write(); + mEfficiencyGoodMatchPhiOriginal_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyGoodMatchPhiOriginal_layer[l]->Write(); + mEfficiencyFakeMatchPt_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyFakeMatchPt_layer[l]->Write(); + mEfficiencyFakeMatchEta_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyFakeMatchEta_layer[l]->Write(); + mEfficiencyFakeMatchPhi_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyFakeMatchPhi_layer[l]->Write(); + } + + mOutFile->mkdir("Efficiency/"); + mOutFile->cd("Efficiency/"); + mEfficiencyGoodMatch->Write(); + mEfficiencyFakeMatch->Write(); + mEfficiencyTotal->Write(); + for (int l = 0; l < NLAYERS; l++) { + + mEfficiencyGoodMatch_layer[l]->Write(); + mEfficiencyFakeMatch_layer[l]->Write(); + mEfficiencyTotal_layer[l]->Write(); + + mEfficiencyGoodMatch_layer[l]->GetYaxis()->SetRangeUser(-0.1, 1.1); + mEfficiencyFakeMatch_layer[l]->GetYaxis()->SetRangeUser(-0.1, 1.1); + mEfficiencyTotal_layer[l]->GetYaxis()->SetRangeUser(-0.1, 1.1); + } + + mEfficiencyGoodMatch->GetYaxis()->SetRangeUser(-0.1, 1.1); + mEfficiencyFakeMatch->GetYaxis()->SetRangeUser(-0.1, 1.1); + mEfficiencyTotal->GetYaxis()->SetRangeUser(-0.1, 1.1); + + TCanvas c; + c.SetName("EffVsDCA_allLayers"); + auto leg = std::make_unique(0.75, 0.45, 0.89, 0.65); + leg->AddEntry(mEfficiencyGoodMatch.get(), "#frac{# good matches}{# tot duplicated clusters}", "p"); + leg->AddEntry(mEfficiencyFakeMatch.get(), "#frac{# fake matches}{# tot duplicated clusters}", "p"); + leg->AddEntry(mEfficiencyTotal.get(), "#frac{# tot matches}{# tot duplicated clusters}", "p"); + + mEfficiencyGoodMatch->Draw("P l E1_NOSTAT PLC PMC "); + mEfficiencyFakeMatch->Draw("same P l E1_NOSTAT PLC PMC"); + mEfficiencyTotal->Draw("same P l E1_NOSTAT PLC PMC"); + leg->Draw("same"); + c.Write(); + + TCanvas cc[NLAYERS]; + for (int l = 0; l < NLAYERS; l++) { + cc[l].cd(); + cc[l].SetName(Form("EffVsDCA_layer_L%d", l)); + + auto leg = std::make_unique(0.75, 0.45, 0.89, 0.65); + leg->AddEntry(mEfficiencyGoodMatch_layer[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "p"); + leg->AddEntry(mEfficiencyFakeMatch_layer[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "p"); + leg->AddEntry(mEfficiencyTotal_layer[l].get(), "#frac{# tot matches}{# tot duplicated clusters}", "p"); + + mEfficiencyGoodMatch_layer[l]->SetLineColor(kBlue + 3); + mEfficiencyGoodMatch_layer[l]->SetMarkerColor(kBlue + 3); + mEfficiencyGoodMatch_layer[l]->Draw("P l E1_NOSTAT"); + mEfficiencyFakeMatch_layer[l]->SetLineColor(kAzure + 7); + mEfficiencyFakeMatch_layer[l]->SetMarkerColor(kAzure + 7); + mEfficiencyFakeMatch_layer[l]->Draw("same P l E1_NOSTAT"); + mEfficiencyTotal_layer[l]->SetLineColor(kGreen + 1); + mEfficiencyTotal_layer[l]->SetMarkerColor(kGreen + 1); + mEfficiencyTotal_layer[l]->Draw("same P l E1_NOSTAT"); + leg->Draw("same"); + cc[l].Write(); + } +} + +void EfficiencyStudy::studyClusterSelectionMC() +{ + //// to be used only with MC + // study to find a good selection method for the duplicated cluster, to be used for non-MC data + // iterate over tracks an associated clusters, and find the closer cluster that is not the original one applying cuts on staveID and chipID + // fix the DCA < 10 sigma, then compute the efficiency for each bin of pt, eta and phi and also in the rows + + LOGP(info, "--------------- studyClusterSelection"); + + int duplicated = getDCAClusterTrackMC(1); + + std::cout << "duplicated: " << duplicated << std::endl; + + double meanDCAxyDuplicated[NLAYERS] = {0}; + double meanDCAzDuplicated[NLAYERS] = {0}; + double sigmaDCAxyDuplicated[NLAYERS] = {0}; + double sigmaDCAzDuplicated[NLAYERS] = {0}; + + for (int l = 0; l < NLAYERS; l++) { + meanDCAxyDuplicated[l] = mDCAxyDuplicated_layer[l]->GetMean(); + meanDCAzDuplicated[l] = mDCAzDuplicated_layer[l]->GetMean(); + sigmaDCAxyDuplicated[l] = mDCAxyDuplicated_layer[l]->GetRMS(); + sigmaDCAzDuplicated[l] = mDCAzDuplicated_layer[l]->GetRMS(); + } + + for (int l = 0; l < NLAYERS; l++) { + LOGP(info, "meanDCAxyDuplicated L{}: {}, meanDCAzDuplicated: {}, sigmaDCAxyDuplicated: {}, sigmaDCAzDuplicated: {}", l, meanDCAxyDuplicated[l], meanDCAzDuplicated[l], sigmaDCAxyDuplicated[l], sigmaDCAzDuplicated[l]); + } + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::array clusOriginalDCA, clusDuplicatedDCA; + auto propagator = o2::base::Propagator::Instance(); + + unsigned int rofIndexTrack = 0; + unsigned int rofNEntriesTrack = 0; + unsigned int rofIndexClus = 0; + unsigned int rofNEntriesClus = 0; + int nLabels = 0; + unsigned int totClus = 0; + + unsigned int nDCAMatches[15] = {0}; + unsigned int nGoodMatches[15] = {0}; + unsigned int nFakeMatches[15] = {0}; + + std::map, bool> clusterMatchesPtEta[100][100] = {}; + + for (unsigned int iROF = 0; iROF < mTracksROFRecords.size(); iROF++) { // loop on ROFRecords array + rofIndexTrack = mTracksROFRecords[iROF].getFirstEntry(); + rofNEntriesTrack = mTracksROFRecords[iROF].getNEntries(); + + rofIndexClus = mClustersROFRecords[iROF].getFirstEntry(); + rofNEntriesClus = mClustersROFRecords[iROF].getNEntries(); + + //////calculcating efficiency vs pt, eta, phi + for (unsigned int iTrack = rofIndexTrack; iTrack < rofIndexTrack + rofNEntriesTrack; iTrack++) { // loop on tracks per ROF + auto track = mTracks[iTrack]; + o2::track::TrackParCov trackParCov = mTracks[iTrack]; + + /// cut on primary vertex position (?) + float ip[2]; + track.getImpactParams(0, 0, 0, 0, ip); + + int firstClus = track.getFirstClusterEntry(); // get the first cluster of the track + int ncl = track.getNumberOfClusters(); // get the number of clusters of the track + + if (ncl < 7) { + continue; + } + + auto& tracklab = mTracksMCLabels[iTrack]; + if (tracklab.isFake()) { + continue; + } + + auto pt = trackParCov.getPt(); + auto eta = trackParCov.getEta(); + + float phi = -999.; + float phiOriginal = -999.; + float phiDuplicated = -999.; + UShort_t row = -999; + + if (mVerboseOutput) { + LOGP(info, "--------- track Label: "); + tracklab.print(); + } + for (int iclTrack = firstClus; iclTrack < firstClus + ncl; iclTrack++) { // loop on clusters associated to the track to extract layer, stave and chip to restrict the possible matches to be searched with the DCA cut + auto& clusOriginal = mClusters[mInputITSidxs[iclTrack]]; + auto layerOriginal = mGeometry->getLayer(clusOriginal.getSensorID()); + if (layerOriginal >= NLAYERS) { + continue; + } + + IPOriginalxy[layerOriginal]->Fill(ip[0]); + IPOriginalz[layerOriginal]->Fill(ip[1]); + + UShort_t rowOriginal = clusOriginal.getRow(); + + auto clusOriginalPoint = mITSClustersArray[mInputITSidxs[iclTrack]]; + o2::math_utils::Point3D clusOriginalPointTrack = {clusOriginalPoint.getX(), clusOriginalPoint.getY(), clusOriginalPoint.getZ()}; + o2::math_utils::Point3D clusOriginalPointGlob = mGeometry->getMatrixT2G(clusOriginal.getSensorID()) * clusOriginalPointTrack; + + auto phiOriginal = clusOriginalPointGlob.phi(); // * 180 / M_PI; + + auto labsOriginal = mClustersMCLCont->getLabels(mInputITSidxs[iclTrack]); // get labels of the cluster associated to the track (original) + auto staveOriginal = mGeometry->getStave(clusOriginal.getSensorID()); + auto chipOriginal = mGeometry->getChipIdInStave(clusOriginal.getSensorID()); + + std::tuple> clusID_rDCA_label = {0, 999., gsl::span()}; // inizializing tuple with dummy values + + bool adjacentFound = 0; + /// for each oroginal cluster iterate over all the possible "adjacten" clusters (stave +-1, chip =,+-1) and calculate the DCA with the track. Then choose the closest one. + for (unsigned int iClus = rofIndexClus; iClus < rofIndexClus + rofNEntriesClus; iClus++) { // iteration over ALL the clusters in the ROF + auto clusDuplicated = mClusters[iClus]; + + //// applying constraints: the cluster should be on the same layer, should be on an adjacent stave and on the same or adjacent chip position + if (clusDuplicated.getSensorID() == clusOriginal.getSensorID()) { + continue; + } + auto layerDuplicated = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerDuplicated != layerOriginal) { + continue; + } + auto staveDuplicated = mGeometry->getStave(clusDuplicated.getSensorID()); + if (abs(staveDuplicated - staveOriginal) != 1) { + continue; + } + auto chipDuplicated = mGeometry->getChipIdInStave(clusDuplicated.getSensorID()); + if (abs(chipDuplicated - chipOriginal) > 1) { + continue; + } + + auto labsDuplicated = mClustersMCLCont->getLabels(iClus); + + /// if the cheks are passed, then calculate the DCA + auto clusDuplicatedPoint = mITSClustersArray[iClus]; + + o2::math_utils::Point3D clusDuplicatedPointTrack = {clusDuplicatedPoint.getX(), clusDuplicatedPoint.getY(), clusDuplicatedPoint.getZ()}; + o2::math_utils::Point3D clusDuplicatedPointGlob = mGeometry->getMatrixT2G(clusDuplicated.getSensorID()) * clusDuplicatedPointTrack; + + auto phiDuplicated = clusDuplicatedPointGlob.phi(); // * 180 / M_PI; + + /// Compute the DCA between the duplicated cluster location and the track + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusDuplicated.getSensorID())); + if (!propagator->propagateToDCA(clusDuplicatedPointGlob, trackParCov, b, 2.f, matCorr, &clusDuplicatedDCA)) { // check if the propagation fails + continue; + } + + // Imposing that the distance between the original cluster and the duplicated one is less than x sigma + if (!(clusDuplicatedDCA[0] > mDCACutsXY[layerDuplicated][0] && clusDuplicatedDCA[0] < mDCACutsXY[layerDuplicated][1] && clusDuplicatedDCA[1] > mDCACutsZ[layerDuplicated][0] && clusDuplicatedDCA[1] < mDCACutsZ[layerDuplicated][1])) { + continue; + } + + if (mVerboseOutput) { + LOGP(info, "Propagation ok"); + } + double rDCA = std::hypot(clusDuplicatedDCA[0], clusDuplicatedDCA[1]); + + // taking the closest cluster within x sigma + if (rDCA < std::get<1>(clusID_rDCA_label)) { // updating the closest cluster + clusID_rDCA_label = {iClus, rDCA, labsDuplicated}; + phi = phiDuplicated; + row = rowOriginal; + } + adjacentFound = 1; + + } // end loop on all the clusters in the rof + + // here clusID_rDCA_label is updated with the closest cluster to the track other than the original one + // checking if it is a good or fake match looking at the labels + + if (!adjacentFound) { + continue; + } + + bool isGood = false; + for (auto lab : std::get<2>(clusID_rDCA_label)) { + if (lab == tracklab) { + isGood = true; + + mNGoodMatchesPt[layerOriginal]->Fill(pt); + mNGoodMatchesRow[layerOriginal]->Fill(row); + mNGoodMatchesCol[layerOriginal]->Fill(clusOriginal.getCol()); + mNGoodMatchesZ[layerOriginal]->Fill(clusOriginalPointGlob.Z()); + mNGoodMatchesPtEta[layerOriginal]->Fill(pt, eta); + mNGoodMatchesPtPhi[layerOriginal]->Fill(pt, phi); + mNGoodMatchesEtaPhi[layerOriginal]->Fill(eta, phi); + + mNGoodMatchesEtaAllPt[layerOriginal]->Fill(eta); + mNGoodMatchesPhiAllPt[layerOriginal]->Fill(phi); + for (int ipt = 0; ipt < 3; ipt++) { + if (pt >= mrangesPt[ipt][0] && pt < mrangesPt[ipt][1]) { + mNGoodMatchesEta[layerOriginal][ipt]->Fill(eta); + mNGoodMatchesPhi[layerOriginal][ipt]->Fill(phi); + } + } + + break; + } + } + if (!isGood) { + + mNFakeMatchesPt[layerOriginal]->Fill(pt); + mNFakeMatchesRow[layerOriginal]->Fill(row); + mNFakeMatchesCol[layerOriginal]->Fill(clusOriginal.getCol()); + mNFakeMatchesZ[layerOriginal]->Fill(clusOriginalPointGlob.Z()); + mNFakeMatchesPtEta[layerOriginal]->Fill(pt, eta); + mNFakeMatchesPtPhi[layerOriginal]->Fill(pt, phi); + mNFakeMatchesEtaPhi[layerOriginal]->Fill(eta, phi); + mNFakeMatchesEtaAllPt[layerOriginal]->Fill(eta); + mNFakeMatchesPhiAllPt[layerOriginal]->Fill(phi); + + for (int ipt = 0; ipt < 3; ipt++) { + if (pt >= mrangesPt[ipt][0] && pt < mrangesPt[ipt][1]) { + mNFakeMatchesEta[layerOriginal][ipt]->Fill(eta); + mNFakeMatchesPhi[layerOriginal][ipt]->Fill(phi); + } + } + } + } // end loop on clusters associated to the track + } // end loop on tracks per ROF + } // end loop on ROFRecords array + + mOutFile->mkdir("EfficiencyCuts/"); + mOutFile->cd("EfficiencyCuts/"); + + std::cout << "Calculating efficiency..." << std::endl; + std::unique_ptr axpt = std::make_unique("axpt", "", 1, 0.05, 7.5); + std::unique_ptr axRow = std::make_unique("axRow", "", 1, -0.5, 511.5); + std::unique_ptr axCol = std::make_unique("axRow", "", 1, -0.5, 1023.5); + std::unique_ptr axZ = std::make_unique("axZ", "", 1, -15, 15); + std::unique_ptr axptetaGood = std::make_unique("axptetaGood", "", 1, 0.05, 7.5, 1, -2, 2); + std::unique_ptr axptetaFake = std::make_unique("axptetaFake", "", 1, 0.05, 7.5, 1, -2, 2); + std::unique_ptr axptphiGood = std::make_unique("axptphiGood", "", 1, 0.05, 7.5, 1, -3.2, 3.2); + std::unique_ptr axptphiFake = std::make_unique("axptphiFake", "", 1, 0.05, 7.5, 1, -3.2, 3.2); + std::unique_ptr axetaphiGood = std::make_unique("axetaphiGood", "", 1, -2, 2, 1, -3.2, 3.2); + std::unique_ptr axetaphiFake = std::make_unique("axetaphiFake", "", 1, -2, 2, 1, -3.2, 3.2); + std::unique_ptr axetaAllPt = std::make_unique("axetaAllPt", "", 1, -2, 2); + std::unique_ptr axeta[NLAYERS]; + std::unique_ptr axphi[NLAYERS]; + for (int ipt = 0; ipt < 3; ipt++) { + axeta[ipt] = std::make_unique(Form("axeta%d", ipt), Form("axeta%d", ipt), 1, -2, 2); + axphi[ipt] = std::make_unique(Form("axphi%d", ipt), Form("axphi%d", ipt), 1, -3.2, 3.2); + } + std::unique_ptr axphiAllPt = std::make_unique("axphi", "", 1, -3.2, 3.2); + + std::unique_ptr effPt[NLAYERS]; + std::unique_ptr effRow[NLAYERS]; + std::unique_ptr effCol[NLAYERS]; + std::unique_ptr effZ[NLAYERS]; + std::unique_ptr effPtEta[NLAYERS][2]; + std::unique_ptr effPtPhi[NLAYERS][2]; + std::unique_ptr effEtaPhi[NLAYERS][2]; + std::unique_ptr effEtaAllPt[NLAYERS]; + std::unique_ptr effEta[NLAYERS][3]; + std::unique_ptr effPhiAllPt[NLAYERS]; + std::unique_ptr effPhi[NLAYERS][3]; + + ///////////////// plotting results + for (int l = 0; l < 3; l++) { + if (mVerboseOutput) { + std::cout << "Pt L" << l << "\n\n"; + } + + // Pt + effPt[l] = std::make_unique(Form("effPt_L%d", l)); + + mEffPtGood[l] = std::make_unique(*mNGoodMatchesPt[l], *mDuplicatedPt[l]); + stileEfficiencyGraph(mEffPtGood[l], Form("mEffPtGood_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesPt[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesPt[l]->GetBinContent(ibin) > mDuplicatedPt[l]->GetBinContent(ibin)) { + std::cout << "--- Pt: Npass = " << mNFakeMatchesPt[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedPt[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + mNFakeMatchesPt[l]->SetBinContent(ibin, mDuplicatedPt[l]->GetBinContent(ibin)); + } + } + mEffPtFake[l] = std::make_unique(*mNFakeMatchesPt[l], *mDuplicatedPt[l]); + stileEfficiencyGraph(mEffPtFake[l], Form("mEffPtFake_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axpt->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});Efficiency", l)); + axpt->GetYaxis()->SetRangeUser(-0.1, 1.1); + axpt->GetXaxis()->SetRangeUser(0.05, 7.5); + axpt->Draw(); + mEffPtGood[l]->Draw("same p"); + mEffPtFake[l]->Draw("same p"); + + auto legpt = std::make_unique(0.70, 0.15, 0.89, 0.35); + legpt->AddEntry(mEffPtGood[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legpt->AddEntry(mEffPtFake[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legpt->Draw("same"); + effPt[l]->Write(); + + // PtEtaGood + effPtEta[l][0] = std::make_unique(Form("effPtEtaGood_L%d", l)); + + mEffPtEtaGood[l] = std::make_unique(*mNGoodMatchesPtEta[l], *mDuplicatedPtEta[l]); + stileEfficiencyGraph(mEffPtEtaGood[l], Form("mEffPtEtaGood_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l), true); + + axptetaGood->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l)); + axptetaGood->GetZaxis()->SetRangeUser(-0.1, 1.1); + axptetaGood->GetYaxis()->SetRangeUser(-2., 2.); + axptetaGood->GetXaxis()->SetRangeUser(0.05, 7.5); + axptetaGood->Draw(); + mEffPtEtaGood[l]->Draw("same colz"); + effPtEta[l][0]->Update(); + effPtEta[l][0]->Write(); + + if (mVerboseOutput) { + std::cout << "Underflow (bin 0,0): " << mNFakeMatchesPtEta[l]->GetBinContent(0, 0) << " " << mDuplicatedPtEta[l]->GetBinContent(0, 0) << std::endl; + std::cout << "Overflow (bin nbinsx,nbinsy): " << mNFakeMatchesPtEta[l]->GetNbinsX() << " " << mNFakeMatchesPtEta[l]->GetNbinsY() << " -> " << mNFakeMatchesPtEta[l]->GetBinContent(mNFakeMatchesPtEta[l]->GetNbinsX(), mNFakeMatchesPtEta[l]->GetNbinsY()) << " " << mDuplicatedPtEta[l]->GetBinContent(mNFakeMatchesPtEta[l]->GetNbinsX(), mNFakeMatchesPtEta[l]->GetNbinsY()) << std::endl; + } + + for (int ibin = 1; ibin <= mNFakeMatchesPtEta[l]->GetNbinsX(); ibin++) { + for (int jbin = 1; jbin <= mNFakeMatchesPtEta[l]->GetNbinsY(); jbin++) { + if (mNFakeMatchesPtEta[l]->GetBinContent(ibin, jbin) > mDuplicatedPtEta[l]->GetBinContent(ibin, jbin)) { + if (mVerboseOutput) { + std::cout << "--- PtEta fakematches : Npass = " << mNFakeMatchesPtEta[l]->GetBinContent(ibin, jbin) << ", Nall = " << mDuplicatedPtEta[l]->GetBinContent(ibin, jbin) << " for ibin = " << ibin << ", jbin = " << jbin << std::endl; + } + mNFakeMatchesPtEta[l]->SetBinContent(ibin, jbin, mDuplicatedPtEta[l]->GetBinContent(ibin, jbin)); + } + } + } + + // Row + effRow[l] = std::make_unique(Form("effRow_L%d", l)); + + for (int ibin = 1; ibin <= mNGoodMatchesRow[l]->GetNbinsX(); ibin++) { + std::cout << "--- Good Row: Npass = " << mNGoodMatchesRow[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedRow[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + + mEffRowGood[l] = std::make_unique(*mNGoodMatchesRow[l], *mDuplicatedRow[l]); + stileEfficiencyGraph(mEffRowGood[l], Form("mEffRowGood_L%d", l), Form("L%d;Row;Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesRow[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesRow[l]->GetBinContent(ibin) > mDuplicatedRow[l]->GetBinContent(ibin)) { + std::cout << "--- Row: Npass = " << mNFakeMatchesRow[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedRow[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + mNFakeMatchesRow[l]->SetBinContent(ibin, mDuplicatedRow[l]->GetBinContent(ibin)); + } + } + mEffRowFake[l] = std::make_unique(*mNFakeMatchesRow[l], *mDuplicatedRow[l]); + stileEfficiencyGraph(mEffRowFake[l], Form("mEffRowFake_L%d", l), Form("L%d;Row;Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axRow->SetTitle(Form("L%d;Row;Efficiency", l)); + axRow->GetYaxis()->SetRangeUser(-0.1, 1.1); + axRow->GetXaxis()->SetRangeUser(0, 512); + axRow->Draw(); + mEffRowGood[l]->Draw("same p"); + mEffRowFake[l]->Draw("same p"); + + auto legRow = std::make_unique(0.70, 0.15, 0.89, 0.35); + legRow->AddEntry(mEffRowGood[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legRow->AddEntry(mEffRowFake[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legRow->Draw("same"); + effRow[l]->Write(); + + // Col + effCol[l] = std::make_unique(Form("effCol_L%d", l)); + + for (int ibin = 1; ibin <= mNGoodMatchesCol[l]->GetNbinsX(); ibin++) { + std::cout << "--- Good Col: Npass = " << mNGoodMatchesCol[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedCol[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + + mEffColGood[l] = std::make_unique(*mNGoodMatchesCol[l], *mDuplicatedCol[l]); + stileEfficiencyGraph(mEffColGood[l], Form("mEffColGood_L%d", l), Form("L%d;Col;Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesCol[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesCol[l]->GetBinContent(ibin) > mDuplicatedCol[l]->GetBinContent(ibin)) { + std::cout << "--- Col: Npass = " << mNFakeMatchesCol[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedCol[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + mNFakeMatchesCol[l]->SetBinContent(ibin, mDuplicatedCol[l]->GetBinContent(ibin)); + } + } + mEffColFake[l] = std::make_unique(*mNFakeMatchesCol[l], *mDuplicatedCol[l]); + stileEfficiencyGraph(mEffColFake[l], Form("mEffColFake_L%d", l), Form("L%d;Col;Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axCol->SetTitle(Form("L%d;Col;Efficiency", l)); + axCol->GetYaxis()->SetRangeUser(-0.1, 1.1); + axCol->GetXaxis()->SetRangeUser(0, 1024); + axCol->Draw(); + mEffColGood[l]->Draw("same p"); + mEffColFake[l]->Draw("same p"); + + auto legCol = std::make_unique(0.70, 0.15, 0.89, 0.35); + legCol->AddEntry(mEffColGood[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legCol->AddEntry(mEffColFake[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legCol->Draw("same"); + effCol[l]->Write(); + + // Z + effZ[l] = std::make_unique(Form("effZ_L%d", l)); + + for (int ibin = 1; ibin <= mNGoodMatchesZ[l]->GetNbinsX(); ibin++) { + std::cout << "--- Good Z: Npass = " << mNGoodMatchesZ[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedZ[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + + mEffZGood[l] = std::make_unique(*mNGoodMatchesZ[l], *mDuplicatedZ[l]); + stileEfficiencyGraph(mEffZGood[l], Form("mEffZGood_L%d", l), Form("L%d;Z;Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesZ[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesZ[l]->GetBinContent(ibin) > mDuplicatedZ[l]->GetBinContent(ibin)) { + std::cout << "--- Z: Npass = " << mNFakeMatchesZ[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedZ[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + mNFakeMatchesZ[l]->SetBinContent(ibin, mDuplicatedZ[l]->GetBinContent(ibin)); + } + } + mEffZFake[l] = std::make_unique(*mNFakeMatchesZ[l], *mDuplicatedZ[l]); + stileEfficiencyGraph(mEffZFake[l], Form("mEffZFake_L%d", l), Form("L%d;Z;Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axZ->SetTitle(Form("L%d;Z;Efficiency", l)); + axZ->GetYaxis()->SetRangeUser(-0.1, 1.1); + axZ->GetXaxis()->SetRangeUser(0, 512); + axZ->Draw(); + mEffZGood[l]->Draw("same p"); + mEffZFake[l]->Draw("same p"); + + auto legZ = std::make_unique(0.70, 0.15, 0.89, 0.35); + legZ->AddEntry(mEffZGood[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legZ->AddEntry(mEffZFake[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legZ->Draw("same"); + effZ[l]->Write(); + + // PtEtaGood + effPtEta[l][0] = std::make_unique(Form("effPtEtaGood_L%d", l)); + + mEffPtEtaGood[l] = std::make_unique(*mNGoodMatchesPtEta[l], *mDuplicatedPtEta[l]); + stileEfficiencyGraph(mEffPtEtaGood[l], Form("mEffPtEtaGood_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l), true); + + axptetaGood->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l)); + axptetaGood->GetZaxis()->SetRangeUser(-0.1, 1.1); + axptetaGood->GetYaxis()->SetRangeUser(-2., 2.); + axptetaGood->GetXaxis()->SetRangeUser(0.05, 7.5); + axptetaGood->Draw(); + mEffPtEtaGood[l]->Draw("same colz"); + effPtEta[l][0]->Update(); + effPtEta[l][0]->Write(); + + if (mVerboseOutput) { + std::cout << "Underflow (bin 0,0): " << mNFakeMatchesPtEta[l]->GetBinContent(0, 0) << " " << mDuplicatedPtEta[l]->GetBinContent(0, 0) << std::endl; + std::cout << "Overflow (bin nbinsx,nbinsy): " << mNFakeMatchesPtEta[l]->GetNbinsX() << " " << mNFakeMatchesPtEta[l]->GetNbinsY() << " -> " << mNFakeMatchesPtEta[l]->GetBinContent(mNFakeMatchesPtEta[l]->GetNbinsX(), mNFakeMatchesPtEta[l]->GetNbinsY()) << " " << mDuplicatedPtEta[l]->GetBinContent(mNFakeMatchesPtEta[l]->GetNbinsX(), mNFakeMatchesPtEta[l]->GetNbinsY()) << std::endl; + } + + for (int ibin = 1; ibin <= mNFakeMatchesPtEta[l]->GetNbinsX(); ibin++) { + for (int jbin = 1; jbin <= mNFakeMatchesPtEta[l]->GetNbinsY(); jbin++) { + if (mNFakeMatchesPtEta[l]->GetBinContent(ibin, jbin) > mDuplicatedPtEta[l]->GetBinContent(ibin, jbin)) { + if (mVerboseOutput) { + std::cout << "--- PtEta fakematches : Npass = " << mNFakeMatchesPtEta[l]->GetBinContent(ibin, jbin) << ", Nall = " << mDuplicatedPtEta[l]->GetBinContent(ibin, jbin) << " for ibin = " << ibin << ", jbin = " << jbin << std::endl; + } + mNFakeMatchesPtEta[l]->SetBinContent(ibin, jbin, mDuplicatedPtEta[l]->GetBinContent(ibin, jbin)); + } + } + } + + // PtEtaFake + effPtEta[l][1] = std::make_unique(Form("effPtEtaFake_L%d", l)); + + mEffPtEtaFake[l] = std::make_unique(*mNFakeMatchesPtEta[l], *mDuplicatedPtEta[l]); + stileEfficiencyGraph(mEffPtEtaFake[l], Form("mEffPtEtaFake_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l), true); + axptetaFake->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l)); + axptetaFake->GetZaxis()->SetRangeUser(-0.1, 1.1); + axptetaFake->GetYaxis()->SetRangeUser(-2., 2.); + axptetaFake->GetXaxis()->SetRangeUser(0.05, 7.5); + axptetaFake->Draw(); + mEffPtEtaFake[l]->Draw("same colz"); + effPtEta[l][1]->Update(); + effPtEta[l][1]->Write(); + + // PtPhiGood + effPtPhi[l][0] = std::make_unique(Form("effPtPhiGood_L%d", l)); + + mEffPtPhiGood[l] = std::make_unique(*mNGoodMatchesPtPhi[l], *mDuplicatedPtPhi[l]); + stileEfficiencyGraph(mEffPtPhiGood[l], Form("mEffPtPhiGood_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});#phi (rad);Efficiency", l), true); + + axptphiGood->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});#phi (rad);Efficiency", l)); + axptphiGood->GetZaxis()->SetRangeUser(-0.1, 1.1); + axptphiGood->GetYaxis()->SetRangeUser(-3.2, 3.2); + axptphiGood->GetXaxis()->SetRangeUser(0.05, 7.5); + axptphiGood->Draw(); + mEffPtPhiGood[l]->Draw("same colz"); + effPtPhi[l][0]->Update(); + effPtPhi[l][0]->Write(); + + for (int ibin = 1; ibin <= mNFakeMatchesPtPhi[l]->GetNbinsX(); ibin++) { + for (int jbin = 1; jbin <= mNFakeMatchesPtPhi[l]->GetNbinsY(); jbin++) { + if (mNFakeMatchesPtPhi[l]->GetBinContent(ibin, jbin) > mDuplicatedPtPhi[l]->GetBinContent(ibin, jbin)) { + if (mVerboseOutput) { + std::cout << "--- Pt: Npass = " << mNFakeMatchesPtPhi[l]->GetBinContent(ibin, jbin) << ", Nall = " << mDuplicatedPtPhi[l]->GetBinContent(ibin, jbin) << " for ibin = " << ibin << ", jbin = " << jbin << std::endl; + } + mNFakeMatchesPtPhi[l]->SetBinContent(ibin, jbin, mDuplicatedPtPhi[l]->GetBinContent(ibin, jbin)); + } + } + } + + // PtPhiFake + effPtPhi[l][1] = std::make_unique(Form("effPtPhiFake_L%d", l)); + + mEffPtPhiFake[l] = std::make_unique(*mNFakeMatchesPtPhi[l], *mDuplicatedPtPhi[l]); + stileEfficiencyGraph(mEffPtPhiFake[l], Form("mEffPtPhiFake_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});#phi (rad);Efficiency", l), true); + axptphiFake->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});#phi (rad);Efficiency", l)); + axptphiFake->GetZaxis()->SetRangeUser(-0.1, 1.1); + axptphiFake->GetYaxis()->SetRangeUser(-3.2, 3.2); + axptphiFake->GetXaxis()->SetRangeUser(0.05, 7.5); + axptphiFake->Draw(); + mEffPtPhiFake[l]->Draw("same colz"); + effPtPhi[l][1]->Update(); + effPtPhi[l][1]->Write(); + + // EtaPhiGood + effEtaPhi[l][0] = std::make_unique(Form("effEtaPhiGood_L%d", l)); + + mEffEtaPhiGood[l] = std::make_unique(*mNGoodMatchesEtaPhi[l], *mDuplicatedEtaPhi[l]); + stileEfficiencyGraph(mEffEtaPhiGood[l], Form("mEffEtaPhiGood_L%d", l), Form("L%d;#eta;#phi (rad);Efficiency", l), true); + + axetaphiGood->SetTitle(Form("L%d;#eta;#phi (rad);Efficiency", l)); + axetaphiGood->GetZaxis()->SetRangeUser(-0.1, 1.1); + axetaphiGood->GetYaxis()->SetRangeUser(-3.2, 3.2); + axetaphiGood->GetXaxis()->SetRangeUser(-2, 2); + axetaphiGood->Draw(); + mEffEtaPhiGood[l]->Draw("same colz"); + effEtaPhi[l][0]->Update(); + effEtaPhi[l][0]->Write(); + + for (int ibin = 1; ibin <= mNFakeMatchesEtaPhi[l]->GetNbinsX(); ibin++) { + for (int jbin = 1; jbin <= mNFakeMatchesEtaPhi[l]->GetNbinsY(); jbin++) { + if (mNFakeMatchesEtaPhi[l]->GetBinContent(ibin, jbin) > mDuplicatedEtaPhi[l]->GetBinContent(ibin, jbin)) { + if (mVerboseOutput) { + std::cout << "--- Eta: Npass = " << mNFakeMatchesEtaPhi[l]->GetBinContent(ibin, jbin) << ", Nall = " << mDuplicatedEtaPhi[l]->GetBinContent(ibin, jbin) << " for ibin = " << ibin << ", jbin = " << jbin << std::endl; + } + mNFakeMatchesEtaPhi[l]->SetBinContent(ibin, jbin, mDuplicatedEtaPhi[l]->GetBinContent(ibin, jbin)); + } + } + } + + // EtaPhiFake + effEtaPhi[l][1] = std::make_unique(Form("effEtaPhiFake_L%d", l)); + + mEffEtaPhiFake[l] = std::make_unique(*mNFakeMatchesEtaPhi[l], *mDuplicatedEtaPhi[l]); + stileEfficiencyGraph(mEffEtaPhiFake[l], Form("mEffEtaPhiFake_L%d", l), Form("L%d;#eta;#phi (rad);Efficiency", l), true); + axetaphiFake->SetTitle(Form("L%d;#eta;#phi (rad);Efficiency", l)); + axetaphiFake->GetZaxis()->SetRangeUser(-0.1, 1.1); + axetaphiFake->GetYaxis()->SetRangeUser(-3.2, 3.2); + axetaphiFake->GetXaxis()->SetRangeUser(-2, 2); + axetaphiFake->Draw(); + mEffEtaPhiFake[l]->Draw("same colz"); + effEtaPhi[l][1]->Update(); + effEtaPhi[l][1]->Write(); + + // EtaAllPt + if (mVerboseOutput) { + std::cout << "Eta L" << l << "\n\n"; + } + + effEtaAllPt[l] = std::make_unique(Form("effEtaAllPt_L%d", l)); + + mEffEtaGoodAllPt[l] = std::make_unique(*mNGoodMatchesEtaAllPt[l], *mDuplicatedEtaAllPt[l]); + stileEfficiencyGraph(mEffEtaGoodAllPt[l], Form("mEffEtaGoodAllPt_L%d", l), Form("L%d;#eta;Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesEtaAllPt[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesEtaAllPt[l]->GetBinContent(ibin) > mDuplicatedEtaAllPt[l]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- EtaAllPt: Npass = " << mNFakeMatchesEtaAllPt[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedEtaAllPt[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNFakeMatchesEtaAllPt[l]->SetBinContent(ibin, mDuplicatedEtaAllPt[l]->GetBinContent(ibin)); + } + } + mEffEtaFakeAllPt[l] = std::make_unique(*mNFakeMatchesEtaAllPt[l], *mDuplicatedEtaAllPt[l]); + stileEfficiencyGraph(mEffEtaFakeAllPt[l], Form("mEffEtaFakeAllPt_L%d", l), Form("L%d;#eta;Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axetaAllPt->SetTitle(Form("L%d;#eta;Efficiency", l)); + axetaAllPt->GetYaxis()->SetRangeUser(-0.1, 1.1); + + axetaAllPt->Draw(); + mEffEtaGoodAllPt[l]->Draw("same p"); + mEffEtaFakeAllPt[l]->Draw("same p"); + + auto legEta = std::make_unique(0.70, 0.15, 0.89, 0.35); + legEta->AddEntry(mEffEtaGoodAllPt[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legEta->AddEntry(mEffEtaFakeAllPt[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legEta->Draw("same"); + effEtaAllPt[l]->Write(); + + /// eta and phi in different pt ranges + for (int ipt = 0; ipt < 3; ipt++) { + // eta + effEta[l][ipt] = std::make_unique(Form("effEta_L%d_pt%d", l, ipt)); + + mEffEtaGood[l][ipt] = std::make_unique(*mNGoodMatchesEta[l][ipt], *mDuplicatedEta[l][ipt]); + stileEfficiencyGraph(mEffEtaGood[l][ipt], Form("mEffEtaGood_L%d_pt%d", l, ipt), Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#eta;Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1]), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesEta[l][ipt]->GetNbinsX(); ibin++) { + if (mNFakeMatchesEta[l][ipt]->GetBinContent(ibin) > mDuplicatedEta[l][ipt]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- Eta : Npass = " << mNFakeMatchesEta[l][ipt]->GetBinContent(ibin) << ", Nall = " << mDuplicatedEta[l][ipt]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNFakeMatchesEta[l][ipt]->SetBinContent(ibin, mDuplicatedEta[l][ipt]->GetBinContent(ibin)); + } + } + + mEffEtaFake[l][ipt] = std::make_unique(*mNFakeMatchesEta[l][ipt], *mDuplicatedEta[l][ipt]); + stileEfficiencyGraph(mEffEtaFake[l][ipt], Form("mEffEtaFake_L%d_pt%d", l, ipt), Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#eta;Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1]), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axeta[ipt]->SetTitle(Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#eta;Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1])); + axeta[ipt]->GetYaxis()->SetRangeUser(-0.1, 1.1); + + axeta[ipt]->Draw(); + mEffEtaGood[l][ipt]->Draw("same p"); + mEffEtaFake[l][ipt]->Draw("same p"); + + auto legEta = std::make_unique(0.70, 0.15, 0.89, 0.35); + legEta->AddEntry(mEffEtaGood[l][ipt].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legEta->AddEntry(mEffEtaFake[l][ipt].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legEta->Draw("same"); + effEta[l][ipt]->Write(); + + // phi + effPhi[l][ipt] = std::make_unique(Form("effPhi_L%d_pt%d", l, ipt)); + + for (int ibin = 1; ibin <= mNGoodMatchesPhi[l][ipt]->GetNbinsX(); ibin++) { + if (mNGoodMatchesPhi[l][ipt]->GetBinContent(ibin) > mDuplicatedPhi[l][ipt]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- Phi L: Npass = " << mNGoodMatchesPhi[l][ipt]->GetBinContent(ibin) << ", Nall = " << mDuplicatedPhi[l][ipt]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNGoodMatchesPhi[l][ipt]->SetBinContent(ibin, 0); + } + } + + mEffPhiGood[l][ipt] = std::make_unique(*mNGoodMatchesPhi[l][ipt], *mDuplicatedPhi[l][ipt]); + stileEfficiencyGraph(mEffPhiGood[l][ipt], Form("mEffPhiGood_L%d_pt%d", l, ipt), Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#phi (rad);Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1]), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesPhi[l][ipt]->GetNbinsX(); ibin++) { + if (mNFakeMatchesPhi[l][ipt]->GetBinContent(ibin) > mDuplicatedPhi[l][ipt]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- Phi L: Npass = " << mNFakeMatchesPhi[l][ipt]->GetBinContent(ibin) << ", Nall = " << mDuplicatedPhi[l][ipt]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNFakeMatchesPhi[l][ipt]->SetBinContent(ibin, mDuplicatedPhi[l][ipt]->GetBinContent(ibin)); + } + } + + mEffPhiFake[l][ipt] = std::make_unique(*mNFakeMatchesPhi[l][ipt], *mDuplicatedPhi[l][ipt]); + stileEfficiencyGraph(mEffPhiFake[l][ipt], Form("mEffPhiFake_L%d_pt%d", l, ipt), Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#phi (rad);Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1]), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axphi[ipt]->SetTitle(Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#phi (rad);Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1])); + axphi[ipt]->GetYaxis()->SetRangeUser(-0.1, 1.1); + + axphi[ipt]->Draw(); + mEffPhiGood[l][ipt]->Draw("same p"); + mEffPhiFake[l][ipt]->Draw("same p"); + + auto legPhi = std::make_unique(0.70, 0.15, 0.89, 0.35); + legPhi->AddEntry(mEffPhiGood[l][ipt].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legPhi->AddEntry(mEffPhiFake[l][ipt].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legPhi->Draw("same"); + effPhi[l][ipt]->Write(); + } + + // PhiAllPt + if (mVerboseOutput) { + std::cout << "Phi L" << l << "\n\n"; + } + + effPhiAllPt[l] = std::make_unique(Form("effPhiAllPt_L%d", l)); + + for (int ibin = 1; ibin <= mNGoodMatchesPhiAllPt[l]->GetNbinsX(); ibin++) { + if (mNGoodMatchesPhiAllPt[l]->GetBinContent(ibin) > mDuplicatedPhiAllPt[l]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- phi all good Npass = " << mNGoodMatchesPhiAllPt[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedPhiAllPt[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNGoodMatchesPhiAllPt[l]->SetBinContent(ibin, 0); + } + } + + mEffPhiGoodAllPt[l] = std::make_unique(*mNGoodMatchesPhiAllPt[l], *mDuplicatedPhiAllPt[l]); + stileEfficiencyGraph(mEffPhiGoodAllPt[l], Form("mEffPhiGoodAllPt_L%d", l), Form("L%d;#phi;Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesPhiAllPt[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesPhiAllPt[l]->GetBinContent(ibin) > mDuplicatedPhiAllPt[l]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- phi all fake Npass = " << mNFakeMatchesPhiAllPt[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedPhiAllPt[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNFakeMatchesPhiAllPt[l]->SetBinContent(ibin, mDuplicatedPhiAllPt[l]->GetBinContent(ibin)); + } + } + mEffPhiFakeAllPt[l] = std::make_unique(*mNFakeMatchesPhiAllPt[l], *mDuplicatedPhiAllPt[l]); + stileEfficiencyGraph(mEffPhiFakeAllPt[l], Form("mEffPhiFakeAllPt_L%d", l), Form("L%d;#phi;Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axphiAllPt->SetTitle(Form("L%d;#phi;Efficiency", l)); + axphiAllPt->GetYaxis()->SetRangeUser(-0.1, 1.1); + axphiAllPt->Draw(); + mEffPhiGoodAllPt[l]->Draw("same p"); + mEffPhiFakeAllPt[l]->Draw("same p"); + + auto legPhi = std::make_unique(0.70, 0.15, 0.89, 0.35); + legPhi->AddEntry(mEffPhiGoodAllPt[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legPhi->AddEntry(mEffPhiFakeAllPt[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legPhi->Draw("same"); + effPhiAllPt[l]->Write(); + } + + /// all Row + std::unique_ptr effRowAll = std::make_unique("effRowAll"); + auto numRowGoodAll = std::unique_ptr((TH1D*)mNGoodMatchesRow[0]->Clone("numRowGoodAll")); + numRowGoodAll->Add(mNGoodMatchesRow[1].get()); + numRowGoodAll->Add(mNGoodMatchesRow[2].get()); + numRowGoodAll->Write(); + auto numRowFakeAll = std::unique_ptr((TH1D*)mNFakeMatchesRow[0]->Clone("numRowFakeAll")); + numRowFakeAll->Add(mNFakeMatchesRow[1].get()); + numRowFakeAll->Add(mNFakeMatchesRow[2].get()); + numRowFakeAll->Write(); + auto denRowAll = std::unique_ptr((TH1D*)mDuplicatedRow[0]->Clone("denRowAll")); + denRowAll->Add(mDuplicatedRow[1].get()); + denRowAll->Add(mDuplicatedRow[2].get()); + denRowAll->Write(); + + std::unique_ptr mEffRowGoodAll = std::make_unique(*numRowGoodAll, *denRowAll); + stileEfficiencyGraph(mEffRowGoodAll, "mEffRowGoodAll", "L0 + L1 + L2;Row;Efficiency", false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + std::unique_ptr mEffRowFakeAll = std::make_unique(*numRowFakeAll, *denRowAll); + stileEfficiencyGraph(mEffRowFakeAll, "mEffRowFakeAll", "L0 + L1 + L2;Row;Efficiency", false, kFullDiamond, 1, kRed + 1, kRed + 1); + axRow->SetTitle("L0 + L1 + L2;Row;Efficiency"); + axRow->GetYaxis()->SetRangeUser(-0.1, 1.1); + axRow->Draw(); + mEffRowGoodAll->Draw("same p"); + mEffRowFakeAll->Draw("same p"); + + auto legRow = std::make_unique(0.70, 0.15, 0.89, 0.35); + legRow->AddEntry(mEffRowGoodAll.get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legRow->AddEntry(mEffRowFakeAll.get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legRow->Draw("same"); + effRowAll->Write(); + + /// all Col + std::unique_ptr effColAll = std::make_unique("effColAll"); + auto numColGoodAll = std::unique_ptr((TH1D*)mNGoodMatchesCol[0]->Clone("numColGoodAll")); + numColGoodAll->Add(mNGoodMatchesCol[1].get()); + numColGoodAll->Add(mNGoodMatchesCol[2].get()); + numColGoodAll->Write(); + auto numColFakeAll = std::unique_ptr((TH1D*)mNFakeMatchesCol[0]->Clone("numColFakeAll")); + numColFakeAll->Add(mNFakeMatchesCol[1].get()); + numColFakeAll->Add(mNFakeMatchesCol[2].get()); + numColFakeAll->Write(); + auto denColAll = std::unique_ptr((TH1D*)mDuplicatedCol[0]->Clone("denColAll")); + denColAll->Add(mDuplicatedCol[1].get()); + denColAll->Add(mDuplicatedCol[2].get()); + denColAll->Write(); + + std::unique_ptr mEffColGoodAll = std::make_unique(*numColGoodAll, *denColAll); + stileEfficiencyGraph(mEffColGoodAll, "mEffColGoodAll", "L0 + L1 + L2;Column;Efficiency", false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + std::unique_ptr mEffColFakeAll = std::make_unique(*numColFakeAll, *denColAll); + stileEfficiencyGraph(mEffColFakeAll, "mEffColFakeAll", "L0 + L1 + L2;Column;Efficiency", false, kFullDiamond, 1, kRed + 1, kRed + 1); + axCol->SetTitle("L0 + L1 + L2;Col;Efficiency"); + axCol->GetYaxis()->SetRangeUser(-0.1, 1.1); + axCol->Draw(); + mEffColGoodAll->Draw("same p"); + mEffColFakeAll->Draw("same p"); + + auto legCol = std::make_unique(0.70, 0.15, 0.89, 0.35); + legCol->AddEntry(mEffColGoodAll.get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legCol->AddEntry(mEffColFakeAll.get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legCol->Draw("same"); + effColAll->Write(); + + /// all Z + std::unique_ptr effZAll = std::make_unique("effZAll"); + auto numZGoodAll = std::unique_ptr((TH1D*)mNGoodMatchesZ[0]->Clone("numZGoodAll")); + numZGoodAll->Add(mNGoodMatchesZ[1].get()); + numZGoodAll->Add(mNGoodMatchesZ[2].get()); + numZGoodAll->Write(); + auto numZFakeAll = std::unique_ptr((TH1D*)mNFakeMatchesZ[0]->Clone("numZFakeAll")); + numZFakeAll->Add(mNFakeMatchesZ[1].get()); + numZFakeAll->Add(mNFakeMatchesZ[2].get()); + numZFakeAll->Write(); + auto denZAll = std::unique_ptr((TH1D*)mDuplicatedZ[0]->Clone("denZAll")); + denZAll->Add(mDuplicatedZ[1].get()); + denZAll->Add(mDuplicatedZ[2].get()); + denZAll->Write(); + + std::unique_ptr mEffZGoodAll = std::make_unique(*numZGoodAll, *denZAll); + stileEfficiencyGraph(mEffZGoodAll, "mEffZGoodAll", "L0 + L1 + L2;Z;Efficiency", false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + std::unique_ptr mEffZFakeAll = std::make_unique(*numZFakeAll, *denZAll); + stileEfficiencyGraph(mEffZFakeAll, "mEffZFakeAll", "L0 + L1 + L2;Z;Efficiency", false, kFullDiamond, 1, kRed + 1, kRed + 1); + axZ->SetTitle("L0 + L1 + L2;Z;Efficiency"); + axZ->GetYaxis()->SetRangeUser(-0.1, 1.1); + axZ->Draw(); + mEffZGoodAll->Draw("same p"); + mEffZFakeAll->Draw("same p"); + + auto legZ = std::make_unique(0.70, 0.15, 0.89, 0.35); + legZ->AddEntry(mEffZGoodAll.get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legZ->AddEntry(mEffZFakeAll.get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legZ->Draw("same"); + effZAll->Write(); + + /// all Eta + std::unique_ptr effEtaAll = std::make_unique("effEtaAll"); + auto numEtaGoodAll = std::unique_ptr((TH1D*)mNGoodMatchesEtaAllPt[0]->Clone("numEtaGoodAll")); + numEtaGoodAll->Add(mNGoodMatchesEtaAllPt[1].get()); + numEtaGoodAll->Add(mNGoodMatchesEtaAllPt[2].get()); + numEtaGoodAll->Write(); + auto numEtaFakeAll = std::unique_ptr((TH1D*)mNFakeMatchesEtaAllPt[0]->Clone("numEtaFakeAll")); + numEtaFakeAll->Add(mNFakeMatchesEtaAllPt[1].get()); + numEtaFakeAll->Add(mNFakeMatchesEtaAllPt[2].get()); + numEtaFakeAll->Write(); + auto denEtaAll = std::unique_ptr((TH1D*)mDuplicatedEtaAllPt[0]->Clone("denEtaAll")); + denEtaAll->Add(mDuplicatedEtaAllPt[1].get()); + denEtaAll->Add(mDuplicatedEtaAllPt[2].get()); + denEtaAll->Write(); + + std::unique_ptr mEffEtaGoodAll = std::make_unique(*numEtaGoodAll, *denEtaAll); + stileEfficiencyGraph(mEffEtaGoodAll, "mEffEtaGoodAll", "L0 + L1 + L2;#Eta;Efficiency", false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + std::unique_ptr mEffEtaFakeAll = std::make_unique(*numEtaFakeAll, *denEtaAll); + stileEfficiencyGraph(mEffEtaFakeAll, "mEffEtaFakeAll", "L0 + L1 + L2;#Eta;Efficiency", false, kFullDiamond, 1, kRed + 1, kRed + 1); + axetaAllPt->SetTitle("L0 + L1 + L2;Eta;Efficiency"); + axetaAllPt->GetYaxis()->SetRangeUser(-0.1, 1.1); + axetaAllPt->Draw(); + mEffEtaGoodAll->Draw("same p"); + mEffEtaFakeAll->Draw("same p"); + + auto legEta = std::make_unique(0.70, 0.15, 0.89, 0.35); + legEta->AddEntry(mEffEtaGoodAll.get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legEta->AddEntry(mEffEtaFakeAll.get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legEta->Draw("same"); + effEtaAll->Write(); + + /// all Phi + std::unique_ptr effPhiAll = std::make_unique("effPhiAll"); + auto numPhiGoodAll = std::unique_ptr((TH1D*)mNGoodMatchesPhiAllPt[0]->Clone("numPhiGoodAll")); + numPhiGoodAll->Add(mNGoodMatchesPhiAllPt[1].get()); + numPhiGoodAll->Add(mNGoodMatchesPhiAllPt[2].get()); + numPhiGoodAll->Write(); + auto numPhiFakeAll = std::unique_ptr((TH1D*)mNFakeMatchesPhiAllPt[0]->Clone("numPhiFakeAll")); + numPhiFakeAll->Add(mNFakeMatchesPhiAllPt[1].get()); + numPhiFakeAll->Add(mNFakeMatchesPhiAllPt[2].get()); + numPhiFakeAll->Write(); + auto denPhiAll = std::unique_ptr((TH1D*)mDuplicatedPhiAllPt[0]->Clone("denPhiAll")); + denPhiAll->Add(mDuplicatedPhiAllPt[1].get()); + denPhiAll->Add(mDuplicatedPhiAllPt[2].get()); + denPhiAll->Write(); + + std::unique_ptr mEffPhiGoodAll = std::make_unique(*numPhiGoodAll, *denPhiAll); + stileEfficiencyGraph(mEffPhiGoodAll, "mEffPhiGoodAll", "L0 + L1 + L2;#Phi (rad);Efficiency", false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + std::unique_ptr mEffPhiFakeAll = std::make_unique(*numPhiFakeAll, *denPhiAll); + stileEfficiencyGraph(mEffPhiFakeAll, "mEffPhiFakeAll", "L0 + L1 + L2;#Phi (rad);Efficiency", false, kFullDiamond, 1, kRed + 1, kRed + 1); + axphiAllPt->SetTitle("L0 + L1 + L2;Phi;Efficiency"); + axphiAllPt->GetYaxis()->SetRangeUser(-0.1, 1.1); + axphiAllPt->Draw(); + mEffPhiGoodAll->Draw("same p"); + mEffPhiFakeAll->Draw("same p"); + + auto legPhi = std::make_unique(0.70, 0.15, 0.89, 0.35); + legPhi->AddEntry(mEffPhiGoodAll.get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legPhi->AddEntry(mEffPhiFakeAll.get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legPhi->Draw("same"); + effPhiAll->Write(); +} + +void EfficiencyStudy::getEfficiency(bool isMC) +{ + // Extract the efficiency for the IB, exploiting the staves overlaps and the duplicated clusters for the tracks passing through the overlaps + // The denominator for the efficiency calculation will be the number of tracks per layer fulfilling some cuts (eta, z, row, col) + // The numerator will be the number of duplicated clusters for the tracks passing through the overlaps + + LOGP(info, "getEfficiency()"); + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::array clusOriginalDCA, clusDuplicatedDCA; + auto propagator = o2::base::Propagator::Instance(); + + unsigned int rofIndexTrack = 0; + unsigned int rofNEntriesTrack = 0; + unsigned int rofIndexClus = 0; + unsigned int rofNEntriesClus = 0; + + int nbPt = 75; + double xbins[nbPt + 1], ptcutl = 0.05, ptcuth = 7.5; + double a = std::log(ptcuth / ptcutl) / nbPt; + for (int i = 0; i <= nbPt; i++) { + xbins[i] = ptcutl * std::exp(i * a); + } + + int totNClusters; + int nDuplClusters; + + for (unsigned int iROF = 0; iROF < mTracksROFRecords.size(); iROF++) { // loop on ROFRecords array + + rofIndexTrack = mTracksROFRecords[iROF].getFirstEntry(); + rofNEntriesTrack = mTracksROFRecords[iROF].getNEntries(); + + rofIndexClus = mClustersROFRecords[iROF].getFirstEntry(); + rofNEntriesClus = mClustersROFRecords[iROF].getNEntries(); + + ////// imposing cuts on the tracks = collecting tracks for the denominator + for (unsigned int iTrack = rofIndexTrack; iTrack < rofIndexTrack + rofNEntriesTrack; iTrack++) { // loop on tracks per ROF + auto track = mTracks[iTrack]; + o2::track::TrackParCov trackParCov = mTracks[iTrack]; + + auto pt = trackParCov.getPt(); // Always 0.6 GeV/c for B = 0 T + auto eta = trackParCov.getEta(); + float phi = -999.; + float phiOriginal = -999.; + + float chi2 = track.getChi2(); + + float ip[2]; + track.getImpactParams(0, 0, 0, 0, ip); + + // float phiTrack = trackParCov.getPhi(); // * 180 / M_PI; + + // applying the cuts on the track - only eta + if (eta < mEtaCuts[0] || eta >= mEtaCuts[1]) { + continue; + } + + int firstClus = track.getFirstClusterEntry(); // get the first cluster of the track + int ncl = track.getNumberOfClusters(); // get the number of clusters of the track + + //// keeping only 7 clusters track to reduce fakes + if (ncl < 7) { + continue; + } + + o2::MCCompLabel tracklab; + if (isMC) { + tracklab = mTracksMCLabels[iTrack]; + if (tracklab.isFake()) { + continue; + } + } + + if (mVerboseOutput && isMC) { + LOGP(info, "track Label: "); + tracklab.print(); + } + + for (int iclTrack = firstClus; iclTrack < firstClus + ncl; iclTrack++) { // loop on clusters associated to the track to extract layer, stave and chip to restrict the possible matches to be searched with the DCA cut + auto& clusOriginal = mClusters[mInputITSidxs[iclTrack]]; + auto clusOriginalPoint = mITSClustersArray[mInputITSidxs[iclTrack]]; + auto layerOriginal = mGeometry->getLayer(clusOriginal.getSensorID()); + + UShort_t rowOriginal = clusOriginal.getRow(); + UShort_t colOriginal = clusOriginal.getCol(); + + /// filling some chip maps + if (clusOriginal.getChipID() >= 0 && clusOriginal.getChipID() <= 8) { + l0_00->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9)), clusOriginal.getRow()); + } + if (clusOriginal.getChipID() >= 252 && clusOriginal.getChipID() <= 260) { + l1_15->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9)), clusOriginal.getRow()); + } + if (clusOriginal.getChipID() >= 423 && clusOriginal.getChipID() <= 431) { + l2_19->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9)), clusOriginal.getRow()); + } + + //// only IB + if (layerOriginal >= NLAYERS) { + continue; + } + + chipmap->Fill(clusOriginal.getCol(), clusOriginal.getRow()); + + IPOriginalxy[layerOriginal]->Fill(ip[0]); + IPOriginalz[layerOriginal]->Fill(ip[1]); + + ///// cluster point and conversion from track local coordinates to global coordinates + o2::math_utils::Point3D clusOriginalPointTrack = {clusOriginalPoint.getX(), clusOriginalPoint.getY(), clusOriginalPoint.getZ()}; + o2::math_utils::Point3D clusOriginalPointGlob = mGeometry->getMatrixT2G(clusOriginal.getSensorID()) * clusOriginalPointTrack; + phiOriginal = clusOriginalPointGlob.phi(); // * 180 / M_PI; + + if (abs(clusOriginalPointGlob.y()) < 0.5) { ///// excluding gap between bottom and top barrels + continue; + } + + if (abs(clusOriginalPointGlob.z()) >= 10) { /// excluding external z + continue; + } + + if (rowOriginal < 2 || (rowOriginal > 15 && rowOriginal < 496) || rowOriginal > 509) { //// cutting on the row + continue; + } + + if (mUseMC) { //// excluding known bad chips in MC which are not bad in data --- to be checked based on the anchored run + if (std::find(mExcludedChipMC.begin(), mExcludedChipMC.end(), clusOriginal.getChipID()) != mExcludedChipMC.end()) { + continue; + } + } + + if (clusOriginal.getCol() < 160 || clusOriginal.getCol() > 870) { /// excluding the gap between two chips in the same stave (comment to obtain the plot efficiency col vs eta) + continue; + } + + /// if the track passes the cuts, fill the den and go ahead + m2DClusterOriginalPositions->Fill(clusOriginalPointGlob.x(), clusOriginalPointGlob.y()); + m3DClusterPositions->Fill(clusOriginalPointGlob.x(), clusOriginalPointGlob.y(), clusOriginalPointGlob.z()); + chi2trackAccepted->Fill(chi2); + denPt[layerOriginal]->Fill(pt); + denPhi[layerOriginal]->Fill(phiOriginal); + denEta[layerOriginal]->Fill(eta); + denRow[layerOriginal]->Fill(rowOriginal); + denCol[layerOriginal]->Fill(clusOriginal.getCol()); + denZ[layerOriginal]->Fill(clusOriginalPointGlob.z()); + nTracksSelected[layerOriginal]++; + mDenColEta[layerOriginal]->Fill(clusOriginal.getCol(), eta); + mDenRowPhi[layerOriginal]->Fill(clusOriginal.getRow(), clusOriginalPointGlob.z()); + mDenRowCol[layerOriginal]->Fill(clusOriginal.getRow(), clusOriginal.getCol()); + denLayers->Fill(layerOriginal); + + /// if the cuts up to here are passed, then search for the duplicated cluster, otherwise go to the next cluster + gsl::span labsOriginal = {}; + if (isMC) { + labsOriginal = mClustersMCLCont->getLabels(mInputITSidxs[iclTrack]); // get labels of the cluster associated to the track (original) + } + + auto staveOriginal = mGeometry->getStave(clusOriginal.getSensorID()); + auto chipOriginal = mGeometry->getChipIdInStave(clusOriginal.getSensorID()); + + std::tuple> clusID_rDCA_label = {0, 999., gsl::span()}; // inizializing tuple with dummy values (if data, ignore the third value) + + bool adjacentFound = 0; + float phiDuplicated = -999.; + float ptDuplicated = -999.; + float etaDuplicated = -999.; + float clusZ = -999.; + + o2::itsmft::CompClusterExt clusDuplicatedSelected = o2::itsmft::CompClusterExt(); + + /// for each original cluster iterate over all the possible duplicated clusters to select the "adjacent" clusters (stave +-1, chip =,+-1) and calculate the DCA with the track. Then choose the closest one. + for (unsigned int iClus = rofIndexClus; iClus < rofIndexClus + rofNEntriesClus; iClus++) { // iteration over ALL the clusters in the ROF + auto clusDuplicated = mClusters[iClus]; + auto clusDuplicatedPoint = mITSClustersArray[iClus]; + + o2::math_utils::Point3D clusDuplicatedPointTrack = {clusDuplicatedPoint.getX(), clusDuplicatedPoint.getY(), clusDuplicatedPoint.getZ()}; + o2::math_utils::Point3D clusDuplicatedPointGlob = mGeometry->getMatrixT2G(clusDuplicated.getSensorID()) * clusDuplicatedPointTrack; + phi = clusDuplicatedPointGlob.phi(); // * 180 / M_PI; + + //// applying constraints: the cluster should be on the same layer, should be on an adjacent stave and on the same or adjacent chip position + if (clusDuplicated.getSensorID() == clusOriginal.getSensorID()) { + continue; + } + auto layerDuplicated = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerDuplicated != layerOriginal) { + continue; + } + auto staveDuplicated = mGeometry->getStave(clusDuplicated.getSensorID()); + if (abs(staveDuplicated - staveOriginal) != 1) { + continue; + } + auto chipDuplicated = mGeometry->getChipIdInStave(clusDuplicated.getSensorID()); + if (abs(chipDuplicated - chipOriginal) > 1) { + continue; + } + + gsl::span labsDuplicated = {}; + if (isMC) { + labsDuplicated = mClustersMCLCont->getLabels(iClus); + } + + /// if the cheks are passed, then calculate the DCA + /// Compute the DCA between the duplicated cluster location and the track + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusDuplicated.getSensorID())); + if (!propagator->propagateToDCA(clusDuplicatedPointGlob, trackParCov, b, 2.f, matCorr, &clusDuplicatedDCA)) { // check if the propagation fails + continue; + } + + DCAxyData[layerDuplicated]->Fill(clusDuplicatedDCA[0]); + DCAzData[layerDuplicated]->Fill(clusDuplicatedDCA[1]); + + // Imposing that the distance between the duplicated cluster and the track is less than x sigma + if (!(clusDuplicatedDCA[0] > mDCACutsXY[layerDuplicated][0] && clusDuplicatedDCA[0] < mDCACutsXY[layerDuplicated][1] && clusDuplicatedDCA[1] > mDCACutsZ[layerDuplicated][0] && clusDuplicatedDCA[1] < mDCACutsZ[layerDuplicated][1])) { + DCAxyRejected[layerDuplicated]->Fill(clusDuplicatedDCA[0]); + DCAzRejected[layerDuplicated]->Fill(clusDuplicatedDCA[1]); + continue; + } + + m2DClusterDuplicatedPositions->Fill(clusDuplicatedPointGlob.x(), clusDuplicatedPointGlob.y()); + m3DDuplicatedClusterPositions->Fill(clusDuplicatedPointGlob.x(), clusDuplicatedPointGlob.y(), clusDuplicatedPointGlob.z()); + + if (mVerboseOutput) { + LOGP(info, "Propagation ok"); + } + double rDCA = std::hypot(clusDuplicatedDCA[0], clusDuplicatedDCA[1]); + + // taking the closest cluster within x sigma + if (rDCA < std::get<1>(clusID_rDCA_label)) { // updating the closest cluster + if (isMC) { + clusID_rDCA_label = {iClus, rDCA, labsDuplicated}; + } else { + clusID_rDCA_label = {iClus, rDCA, gsl::span()}; + } + phiDuplicated = phiOriginal; + ptDuplicated = pt; + etaDuplicated = eta; + clusZ = clusOriginalPointGlob.z(); + clusDuplicatedSelected = clusDuplicated; + } + adjacentFound = 1; + } // end loop on all the clusters in the rof -> at this point we have the information on the closest cluster (if there is one) + + // here clusID_rDCA_label is updated with the closest cluster to the track other than the original one + + if (!adjacentFound) { + radiusNotFound[layerOriginal]->Fill(sqrt(clusOriginalPointGlob.x() * clusOriginalPointGlob.x() + clusOriginalPointGlob.y() * clusOriginalPointGlob.y())); + colNotFound[layerOriginal]->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9))); + rowNotFound[layerOriginal]->Fill(rowOriginal); + zNotFound[layerOriginal]->Fill(clusOriginalPointGlob.z()); + phiNotFound[layerOriginal]->Fill(phiOriginal); + continue; + } + + chipOrigVsOverlap->Fill(clusOriginal.getChipID() % 9, clusDuplicatedSelected.getChipID() % 9); + mChipFound->Fill(clusOriginal.getChipID()); + zFound[layerOriginal]->Fill(clusOriginalPointGlob.z()); + radiusFound[layerOriginal]->Fill(sqrt(clusOriginalPointGlob.x() * clusOriginalPointGlob.x() + clusOriginalPointGlob.y() * clusOriginalPointGlob.y())); + colFoundOriginalVsDuplicated[layerOriginal]->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9)), clusDuplicatedSelected.getCol() + (1024 * (clusDuplicatedSelected.getChipID() % 9))); + colFoundOriginal[layerOriginal]->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9))); + m2DClusterFoundPositions->Fill(clusOriginalPointGlob.x(), clusOriginalPointGlob.y()); + phiFound[layerOriginal]->Fill(phiOriginal); + rowFound[layerOriginal]->Fill(rowOriginal); + nDuplClusters++; + nDuplicatedClusters[layerOriginal]++; + numPt[layerOriginal]->Fill(pt); + numPhi[layerOriginal]->Fill(phiDuplicated); + numEta[layerOriginal]->Fill(etaDuplicated); + numRow[layerOriginal]->Fill(rowOriginal); + numCol[layerOriginal]->Fill(clusOriginal.getCol()); + numZ[layerOriginal]->Fill(clusOriginalPointGlob.z()); + mZvsPhiDUplicated[layerOriginal]->Fill(clusZ, phiDuplicated); + mNumColEta[layerOriginal]->Fill(clusOriginal.getCol(), eta); + mNumRowPhi[layerOriginal]->Fill(clusOriginal.getRow(), clusOriginalPointGlob.z()); + mNumRowCol[layerOriginal]->Fill(clusOriginal.getRow(), clusOriginal.getCol()); + numLayers->Fill(layerOriginal); + + // checking if it is a good or fake match looking at the labels (only if isMC) + if (isMC) { + bool isGood = false; + for (auto lab : std::get<2>(clusID_rDCA_label)) { + if (lab == tracklab) { + isGood = true; + numPtGood[layerOriginal]->Fill(ptDuplicated); + numPhiGood[layerOriginal]->Fill(phiDuplicated); + numEtaGood[layerOriginal]->Fill(etaDuplicated); + numRowGood[layerOriginal]->Fill(rowOriginal); + numColGood[layerOriginal]->Fill(clusOriginal.getCol()); + numZGood[layerOriginal]->Fill(clusOriginalPointGlob.z()); + numGoodLayers->Fill(layerOriginal); + continue; + } + } + if (!isGood) { + numPtFake[layerOriginal]->Fill(ptDuplicated); + numPhiFake[layerOriginal]->Fill(phiDuplicated); + numEtaFake[layerOriginal]->Fill(etaDuplicated); + numRowFake[layerOriginal]->Fill(rowOriginal); + numColFake[layerOriginal]->Fill(clusOriginal.getCol()); + numZFake[layerOriginal]->Fill(clusOriginalPointGlob.z()); + numFakeLayers->Fill(layerOriginal); + } + } + } // end loop on clusters associated to the track + totNClusters += NLAYERS; + } // end loop on tracks per ROF + } // end loop on ROFRecords array + + std::cout << " Num of duplicated clusters L0: " << nDuplicatedClusters[0] << " N tracks selected: " << nTracksSelected[0] << std::endl; + std::cout << " Num of duplicated clusters L1: " << nDuplicatedClusters[1] << " N tracks selected: " << nTracksSelected[1] << std::endl; + std::cout << " Num of duplicated clusters L2: " << nDuplicatedClusters[2] << " N tracks selected: " << nTracksSelected[2] << std::endl; + + std::cout << " --------- N total clusters: " << totNClusters << std::endl; + std::cout << " --------- N duplicated clusters: " << nDuplClusters << std::endl; +} + +void EfficiencyStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + LOGP(info, "--------------- process"); + + o2::base::GRPGeomHelper::instance().getGRPMagField()->print(); + + if (mUseMC) { + // getDCAClusterTrackMC(); + studyDCAcutsMC(); + // studyClusterSelectionMC(); + // countDuplicatedAfterCuts(); + getEfficiency(mUseMC); + } else { + getEfficiency(mUseMC); + } + + LOGP(info, "** Found in {} rofs:\n\t- {} clusters\n\t", + mClustersROFRecords.size(), mClusters.size()); + + if (mUseMC) { + LOGP(info, "mClusters size: {}, mClustersROFRecords size: {}, mClustersMCLCont size: {}, mClustersconverted size: {} ", mClusters.size(), mClustersROFRecords.size(), mClustersMCLCont->getNElements(), mITSClustersArray.size()); + LOGP(info, "mTracks size: {}, mTracksROFRecords size: {}, mTracksMCLabels size: {}", mTracks.size(), mTracksROFRecords.size(), mTracksMCLabels.size()); + } else { + LOGP(info, "mClusters size: {}, mClustersROFRecords size: {}, mClustersconverted size: {} ", mClusters.size(), mClustersROFRecords.size(), mITSClustersArray.size()); + LOGP(info, "mTracks size: {}, mTracksROFRecords size: {}", mTracks.size(), mTracksROFRecords.size()); + } +} + +void EfficiencyStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + static bool initOnceDone = false; + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + mGeometry = GeometryTGeo::Instance(); + mGeometry->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G, o2::math_utils::TransformType::L2G)); + } +} + +void EfficiencyStudy::endOfStream(EndOfStreamContext& ec) +{ + LOGP(info, "--------------- endOfStream"); + + mOutFile->mkdir("EfficiencyFinal/"); + mOutFile->mkdir("DCAFinal/"); + mOutFile->mkdir("NotFoundChecks/"); + + mOutFile->mkdir("DCA/"); + mOutFile->mkdir("Pt_Eta_Phi/"); + + if (mUseMC) { + + mOutFile->cd("DCA"); + mDCAxyDuplicated->Write(); + mDCAzDuplicated->Write(); + for (int i = 0; i < NLAYERS; i++) { + mDCAxyDuplicated_layer[i]->Write(); + mDCAzDuplicated_layer[i]->Write(); + + mDCAxyOriginal[i]->Write(); + mDCAzOriginal[i]->Write(); + } + + mOutFile->cd("Pt_Eta_Phi/"); + for (int i = 0; i < NLAYERS; i++) { + mDuplicatedPhiAllPt[i]->Write(); + mPtDuplicated[i]->Write(); + mEtaDuplicated[i]->Write(); + mPhiDuplicated[i]->Write(); + mPhiOriginalIfDuplicated[i]->Write(); + mDuplicatedPt[i]->Write(); + mDuplicatedPtEta[i]->Write(); + mDuplicatedPtPhi[i]->Write(); + mDuplicatedEtaPhi[i]->Write(); + mDuplicatedEtaAllPt[i]->Write(); + mDuplicatedRow[i]->Write(); + mDuplicatedCol[i]->Write(); + mDuplicatedZ[i]->Write(); + + for (int p = 0; p < 3; p++) { + mDuplicatedEta[i][p]->Write(); + mDuplicatedPhi[i][p]->Write(); + } + mPt_EtaDupl[i]->Write(); + } + } + + mOutFile->cd("Pt_Eta_Phi/"); + for (int i = 0; i < NLAYERS; i++) { + IPOriginalxy[i]->Write(); + IPOriginalz[i]->Write(); + mPhiOriginal[i]->Write(); + mPtOriginal[i]->Write(); + mEtaOriginal[i]->Write(); + mZvsPhiDUplicated[i]->Write(); + chipRowDuplicated[i]->Write(); + chipRowOriginalIfDuplicated[i]->Write(); + } + + mOutFile->mkdir("chi2"); + mOutFile->cd("chi2/"); + + chi2trackAccepted->Write(); + + mOutFile->cd("EfficiencyFinal/"); + TList listNum; + TList listDen; + auto numPhiAll = std::unique_ptr((TH1D*)numPhi[0]->Clone("numPhiAll")); + auto denPhiAll = std::unique_ptr((TH1D*)denPhi[0]->Clone("denPhiAll")); + + TList listNumColEta; + TList listDenColEta; + auto numColEtaAll = std::unique_ptr((TH1D*)mNumColEta[0]->Clone("numColEtaAll")); + auto denColEtaAll = std::unique_ptr((TH1D*)mDenColEta[0]->Clone("denColEtaAll")); + + TList listNumRowPhi; + TList listDenRowPhi; + auto numRowPhiAll = std::unique_ptr((TH1D*)mNumRowPhi[0]->Clone("numRowPhiAll")); + auto denRowPhiAll = std::unique_ptr((TH1D*)mDenRowPhi[0]->Clone("denRowPhiAll")); + + TList listNumRowCol; + TList listDenRowCol; + auto numRowColAll = std::unique_ptr((TH1D*)mNumRowCol[0]->Clone("numRowColAll")); + auto denRowColAll = std::unique_ptr((TH1D*)mDenRowCol[0]->Clone("denRowColAll")); + + std::unique_ptr effLayers = std::make_unique(*numLayers, *denLayers); + effLayers->SetName("effLayers"); + effLayers->SetTitle("; ;Efficiency"); + std::unique_ptr effLayersGood = std::make_unique(*numGoodLayers, *denLayers); + effLayersGood->SetName("effLayersGood"); + effLayersGood->SetTitle("; ;Efficiency Good Matches"); + std::unique_ptr effLayersFake = std::make_unique(*numFakeLayers, *denLayers); + effLayersFake->SetName("effLayersFake"); + effLayersFake->SetTitle("; ;Efficiency Fake Matches"); + effLayers->Write(); + effLayersGood->Write(); + effLayersFake->Write(); + denLayers->Write(); + numLayers->Write(); + numGoodLayers->Write(); + numFakeLayers->Write(); + + for (int l = 0; l < NLAYERS; l++) { + + std::unique_ptr effPt = std::make_unique(*numPt[l], *denPt[l]); + effPt->SetName(Form("effPt_layer%d", l)); + effPt->SetTitle(Form("L%d;p_{T} (GeV/c);Efficiency", l)); + std::unique_ptr effPtGood = std::make_unique(*numPtGood[l], *denPt[l]); + effPtGood->SetName(Form("effPtGood_layer%d", l)); + effPtGood->SetTitle(Form("L%d;p_{T} (GeV/c);Efficiency Good Matches", l)); + std::unique_ptr effPtFake = std::make_unique(*numPtFake[l], *denPt[l]); + effPtFake->SetName(Form("effPtFake_layer%d", l)); + effPtFake->SetTitle(Form("L%d;p_{T} (GeV/c);Efficiency Fake Matches", l)); + effPt->Write(); + effPtGood->Write(); + effPtFake->Write(); + + std::unique_ptr effPhi = std::make_unique(*numPhi[l], *denPhi[l]); + effPhi->SetName(Form("effPhi_layer%d", l)); + effPhi->SetTitle(Form("L%d;#phi;Efficiency", l)); + std::unique_ptr effPhiGood = std::make_unique(*numPhiGood[l], *denPhi[l]); + effPhiGood->SetName(Form("effPhiGood_layer%d", l)); + effPhiGood->SetTitle(Form("L%d;#phi;Efficiency Good Matches", l)); + std::unique_ptr effPhiFake = std::make_unique(*numPhiFake[l], *denPhi[l]); + effPhiFake->SetName(Form("effPhiFake_layer%d", l)); + effPhiFake->SetTitle(Form("L%d;#phi;Efficiency Fake Matches", l)); + effPhi->Write(); + effPhiGood->Write(); + effPhiFake->Write(); + listNum.Add(numPhi[l].get()); + listDen.Add(denPhi[l].get()); + + std::unique_ptr effEta = std::make_unique(*numEta[l], *denEta[l]); + effEta->SetName(Form("effEta_layer%d", l)); + effEta->SetTitle(Form("L%d;#eta;Efficiency", l)); + std::unique_ptr effEtaGood = std::make_unique(*numEtaGood[l], *denEta[l]); + effEtaGood->SetName(Form("effEtaGood_layer%d", l)); + effEtaGood->SetTitle(Form("L%d;#eta;Efficiency Good Matches", l)); + std::unique_ptr effEtaFake = std::make_unique(*numEtaFake[l], *denEta[l]); + effEtaFake->SetName(Form("effEtaFake_layer%d", l)); + effEtaFake->SetTitle(Form("L%d;#eta;Efficiency Fake Matches", l)); + effEta->Write(); + effEtaGood->Write(); + effEtaFake->Write(); + + std::unique_ptr effRow = std::make_unique(*numRow[l], *denRow[l]); + effRow->SetName(Form("effRow_layer%d", l)); + effRow->SetTitle(Form("L%d;#Row;Efficiency", l)); + std::unique_ptr effRowGood = std::make_unique(*numRowGood[l], *denRow[l]); + effRowGood->SetName(Form("effRowGood_layer%d", l)); + effRowGood->SetTitle(Form("L%d;#Row;Efficiency Good Matches", l)); + std::unique_ptr effRowFake = std::make_unique(*numRowFake[l], *denRow[l]); + effRowFake->SetName(Form("effRowFake_layer%d", l)); + effRowFake->SetTitle(Form("L%d;#Row;Efficiency Fake Matches", l)); + effRow->Write(); + effRowGood->Write(); + effRowFake->Write(); + + std::unique_ptr effCol = std::make_unique(*numCol[l], *denCol[l]); + effCol->SetName(Form("effCol_layer%d", l)); + effCol->SetTitle(Form("L%d;#Col;Efficiency", l)); + std::unique_ptr effColGood = std::make_unique(*numColGood[l], *denCol[l]); + effColGood->SetName(Form("effColGood_layer%d", l)); + effColGood->SetTitle(Form("L%d;#Col;Efficiency Good Matches", l)); + std::unique_ptr effColFake = std::make_unique(*numColFake[l], *denCol[l]); + effColFake->SetName(Form("effColFake_layer%d", l)); + effColFake->SetTitle(Form("L%d;#Col;Efficiency Fake Matches", l)); + effCol->Write(); + effColGood->Write(); + effColFake->Write(); + + std::unique_ptr effZ = std::make_unique(*numZ[l], *denZ[l]); + effZ->SetName(Form("effZ_layer%d", l)); + effZ->SetTitle(Form("L%d;#Z (cm);Efficiency", l)); + std::unique_ptr effZGood = std::make_unique(*numZGood[l], *denZ[l]); + effZGood->SetName(Form("effZGood_layer%d", l)); + effZGood->SetTitle(Form("L%d;#Z (cm);Efficiency Good Matches", l)); + std::unique_ptr effZFake = std::make_unique(*numZFake[l], *denZ[l]); + effZFake->SetName(Form("effZFake_layer%d", l)); + effZFake->SetTitle(Form("L%d;#Z (cm);Efficiency Fake Matches", l)); + effZ->Write(); + effZGood->Write(); + effZFake->Write(); + + std::unique_ptr effColEta = std::make_unique(*mNumColEta[l], *mDenColEta[l]); + effColEta->SetName(Form("effColEta_layer%d", l)); + effColEta->SetTitle(Form("L%d;Column;#eta", l)); + effColEta->Write(); + + listNumColEta.Add(mNumColEta[l].get()); + listDenColEta.Add(mDenColEta[l].get()); + + std::unique_ptr effRowPhi = std::make_unique(*mNumRowPhi[l], *mDenRowPhi[l]); + effRowPhi->SetName(Form("effRowPhi_layer%d", l)); + effRowPhi->SetTitle(Form("L%d;Column;#eta", l)); + effRowPhi->Write(); + + listNumRowPhi.Add(mNumRowPhi[l].get()); + listDenRowPhi.Add(mDenRowPhi[l].get()); + + std::unique_ptr effRowCol = std::make_unique(*mNumRowCol[l], *mDenRowCol[l]); + effRowCol->SetName(Form("effRowCol_layer%d", l)); + effRowCol->SetTitle(Form("L%d;Column;#eta", l)); + effRowCol->Write(); + + listNumRowCol.Add(mNumRowCol[l].get()); + listDenRowCol.Add(mDenRowCol[l].get()); + + mNumRowCol[l]->Write(); + mDenRowCol[l]->Write(); + mNumRowPhi[l]->Write(); + mDenRowPhi[l]->Write(); + mNumColEta[l]->Write(); + mDenColEta[l]->Write(); + numPhi[l]->Write(); + denPhi[l]->Write(); + numPt[l]->Write(); + denPt[l]->Write(); + numEta[l]->Write(); + denEta[l]->Write(); + numRow[l]->Write(); + denRow[l]->Write(); + numCol[l]->Write(); + denCol[l]->Write(); + numZ[l]->Write(); + denZ[l]->Write(); + } + numPhiAll->Merge(&listNum); + denPhiAll->Merge(&listDen); + + numColEtaAll->Merge(&listNumColEta); + denColEtaAll->Merge(&listDenColEta); + + numRowPhiAll->Merge(&listNumRowPhi); + denRowPhiAll->Merge(&listDenRowPhi); + + numRowColAll->Merge(&listNumRowCol); + denRowColAll->Merge(&listDenRowCol); + + std::unique_ptr effPhiAll = std::make_unique(*numPhiAll, *denPhiAll); + effPhiAll->SetName("effPhi_AllLayers"); + effPhiAll->SetTitle("L0 + L1 + L2;#phi;Efficiency"); + effPhiAll->Write(); + numPhiAll->Write(); + denPhiAll->Write(); + + std::unique_ptr effColEtaAll = std::make_unique(*numColEtaAll, *denColEtaAll); + effColEtaAll->SetName("effColEta_AllLayers"); + effColEtaAll->SetTitle("L0 + L1 + L2;Column;#eta"); + effColEtaAll->Write(); + numColEtaAll->Write(); + denColEtaAll->Write(); + + std::unique_ptr effRowPhiAll = std::make_unique(*numRowPhiAll, *denRowPhiAll); + effRowPhiAll->SetName("effRowPhi_AllLayers"); + effRowPhiAll->SetTitle("L0 + L1 + L2;Column;#eta"); + effRowPhiAll->Write(); + numRowPhiAll->Write(); + denRowPhiAll->Write(); + + std::unique_ptr effRowColAll = std::make_unique(*numRowColAll, *denRowColAll); + effRowColAll->SetName("effRowCol_AllLayers"); + effRowColAll->SetTitle("L0 + L1 + L2;Column;#eta"); + effRowColAll->Write(); + numRowColAll->Write(); + denRowColAll->Write(); + + mOutFile->cd("DCAFinal/"); + + for (int l = 0; l < NLAYERS; l++) { + DCAxyData[l]->Write(); + DCAzData[l]->Write(); + DCAxyRejected[l]->Write(); + DCAzRejected[l]->Write(); + } + + mOutFile->cd("NotFoundChecks/"); + + for (int l = 0; l < NLAYERS; l++) { + phiFound[l]->Write(); + phiNotFound[l]->Write(); + rowFound[l]->Write(); + rowNotFound[l]->Write(); + zFound[l]->Write(); + zNotFound[l]->Write(); + radiusFound[l]->Write(); + radiusNotFound[l]->Write(); + colFoundOriginalVsDuplicated[l]->Write(); + colFoundOriginal[l]->Write(); + colNotFound[l]->Write(); + } + mChipFound->Write(); + mChipNotFound->Write(); + m2DClusterFoundPositions->Write(); + l0_00->Write(); + l1_15->Write(); + l2_19->Write(); + chipOrigVsOverlap->Write(); + chipmap->SetContour(100); + chipmap->Write(); + + mOutFile->Close(); +} + +void EfficiencyStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + std::cout << "-------- finaliseCCDB" << std::endl; + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); + return; + } +} + +DataProcessorSpec getEfficiencyStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestClusters(srcClustersMask, useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + return DataProcessorSpec{ + "its-efficiency-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, srcTracksMask, useMC, kineReader, ggRequest)}, + Options{}}; +} + +} // namespace o2::its::study diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/Helpers.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/Helpers.cxx new file mode 100644 index 0000000000000..a5b3495047934 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/Helpers.cxx @@ -0,0 +1,224 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include + +// o2 includes +#include "ITSStudies/Helpers.h" +#include "TLegend.h" + +using namespace o2::its::study; + +//______________________________________________________________________________ +std::vector helpers::makeLogBinning(const int nbins, const double min, const double max) +{ + assert(min > 0); + assert(min < max); + + std::vector binLim(nbins + 1); + + const double expMax = std::log(max / min); + const double binWidth = expMax / nbins; + + binLim[0] = min; + binLim[nbins] = max; + + for (Int_t i = 1; i < nbins; ++i) { + binLim[i] = min * std::exp(i * binWidth); + } + + return binLim; +} + +//______________________________________________________________________________ +void helpers::setStyleHistogram1D(TH1F& histo, int color) +{ + // common and 1D case + histo.SetStats(0); + histo.SetMinimum(20); + histo.SetMaximum(300); + histo.SetLineColor(color); + histo.SetLineWidth(1); + histo.SetMarkerColor(color); + histo.SetMarkerSize(0.05); + histo.SetMarkerStyle(1); + histo.SetTitle(""); +} + +//______________________________________________________________________________ +void helpers::setStyleHistogram1D(TH1F& histo, int color, TString title, TString titleYaxis, TString titleXaxis) +{ + // common and 1D case + helpers::setStyleHistogram1D(histo, color); + histo.GetXaxis()->SetTitle(titleXaxis.Data()); + histo.GetYaxis()->SetTitle(titleYaxis.Data()); + histo.GetXaxis()->SetTitleOffset(1.4); + histo.GetYaxis()->SetTitleOffset(1.2); +} + +//______________________________________________________________________________ +void helpers::setStyleHistogram1DMeanValues(TH1F& histo, int color) +{ + // common and 1D case + helpers::setStyleHistogram1D(histo, color); + histo.SetMinimum(-15.); + histo.SetMaximum(15.); + histo.GetXaxis()->SetTitleOffset(1.4); + histo.GetYaxis()->SetTitleOffset(1.2); +} + +//______________________________________________________________________________ +void helpers::setStyleHistogram2D(TH2F& histo) +{ + // common and 1D case + histo.SetStats(0); + histo.GetYaxis()->SetRangeUser(-600, 600); + histo.GetXaxis()->SetTitleOffset(1.2); + histo.GetYaxis()->SetTitleOffset(1.2); +} + +//______________________________________________________________________________ +TCanvas* helpers::prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TH1F& h2, int color2) +{ + TCanvas* c1 = new TCanvas(); + c1->SetLogy(); + c1->SetGridy(); + c1->SetLogx(); + c1->SetGridx(); + setStyleHistogram1D(h1, color1); + helpers::setStyleHistogram1D(h2, color2); + h1.Draw(); + h2.Draw("same"); + return c1; +} + +//______________________________________________________________________________ +TCanvas* helpers::prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2, bool logScale) +{ + TCanvas* c1 = new TCanvas(); + if (logScale) { + c1->SetLogy(); // c1->SetGridy(); + c1->SetLogx(); // c1->SetGridx(); + } + TString direction = ""; + TString histoName1 = h1.GetName(); + TString histoName2 = h2.GetName(); + if (histoName1.Contains("Xy")) { + direction = "XY"; + } + if (histoName1.Contains("Z")) { + direction = "Z"; + } + if ((histoName1.Contains("Xy")) && (histoName2.Contains("Z"))) { + direction = ""; + } + helpers::setStyleHistogram1D(h1, color1, "", Form("Pointing Resolution %s (#mum)", direction.Data()), h1.GetXaxis()->GetName()); + helpers::setStyleHistogram1D(h2, color2, "", Form("Pointing Resolution %s (#mum)", direction.Data()), h2.GetXaxis()->GetName()); + TLegend* leg = new TLegend(0.6, 0.3, 0.8, 0.5); + leg->SetBorderSize(0); + leg->SetFillStyle(0); + leg->AddEntry(&h1, nameHisto1.Data(), "lp"); + leg->AddEntry(&h2, nameHisto2.Data(), "lp"); + h1.Draw(); + h2.Draw("same"); + leg->Draw("same"); + return c1; +} + +//______________________________________________________________________________ +TCanvas* helpers::prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2, TString intRate) +{ + TCanvas* c1 = new TCanvas(); + c1->SetLogy(); // c1->SetGridy(); + c1->SetLogx(); // c1->SetGridx(); + helpers::setStyleHistogram1D(h1, color1); + helpers::setStyleHistogram1D(h2, color2); + TLegend* leg = new TLegend(0.2, 0.3, 0.5, 0.5); + leg->SetBorderSize(0); + leg->SetFillStyle(0); + leg->AddEntry(&h1, nameHisto1.Data(), "lp"); + leg->AddEntry(&h2, nameHisto2.Data(), "lp"); + h1.Draw(); + h2.Draw("same"); + leg->Draw("same"); + TPaveText* paveText; + if (!intRate) { + helpers::paveTextITS(paveText, intRate); + } + return c1; +} + +//______________________________________________________________________________ +TCanvas* helpers::prepareSimpleCanvas2DcaMeanValues(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2) +{ + TCanvas* c1 = new TCanvas(); + c1->SetLogx(); + helpers::setStyleHistogram1DMeanValues(h1, color1); + helpers::setStyleHistogram1DMeanValues(h2, color2); + TLegend* leg = new TLegend(0.2, 0.15, 0.5, 0.35); + leg->SetBorderSize(0); + leg->SetFillStyle(0); + leg->AddEntry(&h1, nameHisto1.Data(), "lp"); + leg->AddEntry(&h2, nameHisto2.Data(), "lp"); + h1.Draw(); + h2.Draw("same"); + leg->Draw("same"); + return c1; +} + +//______________________________________________________________________________ +TCanvas* helpers::plot2DwithMeanAndSigma(TH2F& h2D, TH1F& hMean, TH1F& hSigma, int color) +{ + TCanvas* c1 = new TCanvas(); + helpers::setStyleHistogram2D(h2D); + helpers::setStyleHistogram1D(hSigma, color); + h2D.Draw("colz"); + hMean.Draw("same"); + TGraphAsymmErrors* gSigma; + ConvertTH1ToTGraphAsymmError(hMean, hSigma, gSigma); + gSigma->SetLineColor(kRed); + gSigma->SetFillStyle(0); + gSigma->Draw("E2same"); + return c1; +} + +//______________________________________________________________________________ +void helpers::paveTextITS(TPaveText* pave, TString intRate) +{ + pave->SetFillStyle(0); + pave->SetBorderSize(0); + pave->SetFillColor(0); + pave->SetTextFont(53); + pave->SetTextSize(12); + pave->AddText("ALICE"); + pave->AddText("Run3 ITS Performaces"); + pave->AddText(Form("Interaction Rate = %s", intRate.Data())); +} + +//______________________________________________________________________________ +void helpers::ConvertTH1ToTGraphAsymmError(TH1F& hMean, TH1F& hSigma, TGraphAsymmErrors*& gr) +{ + const Int_t nbinsxx = hMean.GetNbinsX() + 1; + Double_t x[nbinsxx], y[nbinsxx], ex1[nbinsxx], ex2[nbinsxx], ey1[nbinsxx], ey2[nbinsxx]; + + for (int i = 0; i < nbinsxx; i++) { + x[i] = hMean.GetBinCenter(i); + y[i] = hMean.GetBinContent(i); + ex1[i] = hMean.GetBinCenter(i) - hMean.GetBinLowEdge(i); + ex2[i] = ex1[i]; + ey1[i] = hSigma.GetBinContent(i); + ey2[i] = hSigma.GetBinContent(i); + } + + gr = new TGraphAsymmErrors(nbinsxx, x, y, ex1, ex2, ey1, ey2); + return; +} diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSBeamBackgroundStudy.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSBeamBackgroundStudy.cxx new file mode 100644 index 0000000000000..fc9e139648672 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSBeamBackgroundStudy.cxx @@ -0,0 +1,739 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITSStudies/ITSBeamBackgroundStudy.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DataFormatsParameters/GRPObject.h" + +#include "CCDB/BasicCCDBManager.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" + +#include +#include +#include + +#include +#include + +#include "Framework/Task.h" +#include "Framework/Logger.h" + +// ZDC +#include "DataFormatsZDC/RecEventFlat.h" + +using namespace o2::framework; +using namespace o2::globaltracking; +using GTrackID = o2::dataformats::GlobalTrackID; + +namespace o2::its::study +{ +class ITSBeamBackgroundStudy : public Task +{ + public: + ITSBeamBackgroundStudy(std::shared_ptr dr, + std::shared_ptr gr, + bool isMC) : mDataRequest{dr}, mGGCCDBRequest(gr), mUseMC(isMC) {} + + void init(InitContext& ic) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void save_and_reset(); + + // Custom + void process(o2::globaltracking::RecoContainer& recoData); + void updateTimeDependentParams(ProcessingContext& pc); + + private: + void getClusterPatterns(gsl::span&, gsl::span&, const o2::itsmft::TopologyDictionary&); + std::vector mPatterns; + + // ITS layout + int NStaves[7] = {12, 16, 20, 24, 30, 42, 48}; + int N_STAVES_IB = 48; + int N_CHIP_IB = 432; + + // Utilities + int ChipToLayer(int chip); + double ChipToPhi(int chip); + bool searchBCfromMap(std::map>& BCperorbit, long target_orbit, int target_bc); + + std::shared_ptr mGGCCDBRequest; + std::shared_ptr mDataRequest; + bool mUseMC; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + + int mTFn = 0; + int mTF_first_after_dump = 1; + + int mStrobeFallBack = 594; + int mStrobe = mStrobeFallBack; + + // TODO: the following should be make configurable + + std::pair TimeWindowZDC = std::make_pair(5., 11.); + std::pair TimeWindowZNAr = std::make_pair(5.64507, 9.64507); + std::pair TimeWindowZNCr = std::make_pair(4.21299, 8.21299); + std::pair TimeWindowZNAl = std::make_pair(-11.3549, -8.35493); + std::pair TimeWindowZNCl = std::make_pair(-12.787, -9.78701); + + int targetClusterMinCol = 128; // definition of anomalous cluster + int targetClusterMaxRow = 29; // definition of anomalous cluster + + int mDumpEveryTF = 10; // use -1 to save only at the end + int mSkimmedOnlyAfterTF = 15000; + std::string mOutputChip = "chipevents"; + std::string mOutoutChipSkim = "chipeventstarget"; + + TH1F* TimeWindowCut; + TH1F* ZNACall; + TH1F* ZNCCall; + TH1F* ZDCAtagBC; + TH1F* ZDCCtagBC; + TH1I* Counters; + TTree* ITSChipEvtTree; + TTree* ITSChipEvtTargetTree; + + // Tree variables + int Tbc; + long Torbit; + int Tchip; + double Tphi; + int TZDCtag; + int Tnhit, Tnclus, Tnhit_no1pix, Tnclus_no1pix; + int Tnclus_s20, Tnclus_s100, Tnclus_s150; + int Tnclus_c20, Tnclus_c100, Tnclus_c128; + int Tnclus_target; + double Tnhit1, Tnhit10; + int Tmissingafter, Tmissingafter2; + int Tmincol, Tmaxcol; +}; + +void ITSBeamBackgroundStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + // o2::base::GRPGeomHelper::instance().checkUpdates(pc); + // static bool initOnceDone = false; + // if (!initOnceDone) { // this param need to be queried only once + // initOnceDone = true; + // // mGeom = o2::its::GeometryTGeo::Instance(); + // // mGeom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + // } +} + +void ITSBeamBackgroundStudy::init(InitContext& ic) +{ + LOGP(info, "Initializing ITSBeamBackgroundStudy"); + LOGP(info, "Fetching ClusterDictionary"); + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL("http://alice-ccdb.cern.ch"); + mgr.setTimestamp(o2::ccdb::getCurrentTimestamp()); + mDict = mgr.get("ITS/Calib/ClusterDictionary"); + + LOGP(info, "Setting up trees and histograms. They will be dumped on file every {} TFs", mDumpEveryTF); + + TimeWindowCut = new TH1F("ZDC bkg region", "ZNAr, ZNCr, -ZNAl, -ZNCl", 8, 0, 8); + TimeWindowCut->SetBinContent(1, TimeWindowZNAr.first); + TimeWindowCut->SetBinContent(2, TimeWindowZNAr.second); + TimeWindowCut->SetBinContent(3, TimeWindowZNCr.first); + TimeWindowCut->SetBinContent(4, TimeWindowZNCr.second); + TimeWindowCut->SetBinContent(5, -TimeWindowZNAl.first); + TimeWindowCut->SetBinContent(6, -TimeWindowZNAl.second); + TimeWindowCut->SetBinContent(7, -TimeWindowZNCl.first); + TimeWindowCut->SetBinContent(8, -TimeWindowZNCl.second); + ZNACall = new TH1F("ZNACall", "ZNACall", 40, -20, 20); + ZNCCall = new TH1F("ZNCCall", "ZNCCall", 40, -20, 20); + ZDCAtagBC = new TH1F("ZDCA tagged BC", "ZDCA tagged bc", 3564, 0, 3564); + ZDCCtagBC = new TH1F("ZDCC tagged BC", "ZDCC tagged bc", 3564, 0, 3564); + + Counters = new TH1I("Counters", "Counters", 20, 1, 21); + Counters->GetXaxis()->SetBinLabel(1, "TF"); + Counters->GetXaxis()->SetBinLabel(2, "ROF"); + Counters->GetXaxis()->SetBinLabel(3, "ZDCA evt"); + Counters->GetXaxis()->SetBinLabel(4, "ROF-ZDCA tagged"); + Counters->GetXaxis()->SetBinLabel(5, "ITStag any"); + Counters->GetXaxis()->SetBinLabel(6, "ITStag TO"); + Counters->GetXaxis()->SetBinLabel(7, "ITStag any + ZDC"); + Counters->GetXaxis()->SetBinLabel(8, "ITStag TO + ZDC"); + Counters->GetXaxis()->SetBinLabel(9, "ZDCC evt"); + Counters->GetXaxis()->SetBinLabel(10, "ROF-ZDCC tagged"); + + ITSChipEvtTree = new TTree("chipevt", "chipevt"); + + // Chip event branches + ITSChipEvtTree->Branch("TFprogress", &mTFn, "TFprogress/I"); + ITSChipEvtTree->Branch("orbit", &Torbit, "orbit/L"); + ITSChipEvtTree->Branch("bc", &Tbc, "bc/I"); + ITSChipEvtTree->Branch("chip", &Tchip, "chip/I"); + ITSChipEvtTree->Branch("phi", &Tphi, "phi/D"); + ITSChipEvtTree->Branch("zdctag", &TZDCtag, "zdctag/I"); + ITSChipEvtTree->Branch("nhit", &Tnhit, "nhit/I"); + ITSChipEvtTree->Branch("nhit_no1pix", &Tnhit_no1pix, "nhit_no1pix/I"); + ITSChipEvtTree->Branch("size1", &Tnhit1, "size1/D"); + ITSChipEvtTree->Branch("size10", &Tnhit10, "size10/D"); + ITSChipEvtTree->Branch("nclus", &Tnclus, "nclus/I"); + ITSChipEvtTree->Branch("nclus_no1pix", &Tnclus_no1pix, "nclus_no1pix/I"); + ITSChipEvtTree->Branch("nclus_target", &Tnclus_target, "nclus_target/I"); + ITSChipEvtTree->Branch("missingafter", &Tmissingafter, "missingafter/I"); + ITSChipEvtTree->Branch("missingafter2", &Tmissingafter2, "missingafter2/I"); + ITSChipEvtTree->Branch("mincol", &Tmincol, "mincol/I"); + ITSChipEvtTree->Branch("maxcol", &Tmaxcol, "maxcol/I"); + + ITSChipEvtTargetTree = ITSChipEvtTree->CloneTree(0); + ITSChipEvtTargetTree->SetName("chipevttarget"); + ITSChipEvtTargetTree->SetTitle("chipevttarget"); +} + +void ITSBeamBackgroundStudy::save_and_reset() +{ + + std::string outfile11 = mOutoutChipSkim + "_" + std::to_string(mTF_first_after_dump) + "_" + std::to_string(mTFn) + ".root"; + LOGP(info, "Writing ROOT file {}", outfile11); + TFile* F11 = TFile::Open(outfile11.c_str(), "recreate"); + TimeWindowCut->Write(); + ZNACall->Write(); + ZNCCall->Write(); + ZDCAtagBC->Write(); + ZDCCtagBC->Write(); + Counters->Write(); + ITSChipEvtTargetTree->Write(); + F11->Close(); + delete F11; + + if (mTFn <= mSkimmedOnlyAfterTF) { + std::string outfile1 = mOutputChip + "_" + std::to_string(mTF_first_after_dump) + "_" + std::to_string(mTFn) + ".root"; + LOGP(info, "Writing ROOT file {}", outfile1); + TFile* F1 = TFile::Open(outfile1.c_str(), "recreate"); + TimeWindowCut->Write(); + ZNACall->Write(); + ZNCCall->Write(); + ZDCAtagBC->Write(); + ZDCCtagBC->Write(); + Counters->Write(); + ITSChipEvtTree->Write(); // chip events and the skimmed one + ITSChipEvtTargetTree->Write(); + F1->Close(); + delete F1; + } + + LOGP(info, "Resetting historgrams and trees"); + // Delete clears data but keep the branch setup intact + ITSChipEvtTree->Reset(); // Delete(""); + ITSChipEvtTargetTree->Reset(); // Delete(""); + ZNACall->Reset(); + ZNCCall->Reset(); + ZDCAtagBC->Reset(); + ZDCCtagBC->Reset(); + Counters->Reset(); + + mTF_first_after_dump = mTFn + 1; +} + +void ITSBeamBackgroundStudy::endOfStream(EndOfStreamContext&) +{ + LOGP(info, "End of stream for ITSBeamBackgroundStudy"); + + save_and_reset(); + + delete TimeWindowCut; + delete ZNACall; + delete ZNCCall; + delete ZDCAtagBC; + delete ZDCCtagBC; + delete Counters; + delete ITSChipEvtTree; + delete ITSChipEvtTargetTree; +} + +void ITSBeamBackgroundStudy::run(ProcessingContext& pc) +{ + + if (mTFn == std::numeric_limits::max()) { + LOGP(error, "Max {} TFs exceeded. Skipping all next events", mTFn); + return; + } + + mTFn++; + + if (mDumpEveryTF > 0 && mTFn > 0 && (mTFn % mDumpEveryTF) == 0) { + LOGP(info, "Reached TF #{}. Exporting new root files", mTFn); + save_and_reset(); + } + + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + // updateTimeDependentParams(pc); + LOGP(info, "Calling process() for TF: {}", mTFn); + process(recoData); +} + +void ITSBeamBackgroundStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + return; +} + +// Custom area +void ITSBeamBackgroundStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + + LOGP(info, "Processing RecoContainer"); + Counters->Fill(1); + + LOGP(info, "Retrieving ZDC data"); + auto RecBC = recoData.getZDCBCRecData(); + auto Energy = recoData.getZDCEnergy(); + auto TDCData = recoData.getZDCTDCData(); + auto Info2 = recoData.getZDCInfo(); + LOGP(info, "sizeof ZDC RC: {}, {}, {}, {}", RecBC.size(), Energy.size(), TDCData.size(), Info2.size()); + + LOGP(info, "Retrieving ITS clusters"); + auto rofRecVec = recoData.getITSClustersROFRecords(); + auto clusArr = recoData.getITSClusters(); + auto clusPatt = recoData.getITSClustersPatterns(); + LOGP(info, "sizeof ITS RC: {}, {}, {}", clusArr.size(), clusPatt.size(), rofRecVec.size()); + + // TODO: improve this + if (rofRecVec.size() == 576 || rofRecVec.size() == 192) { + mStrobe = 3564 / (rofRecVec.size() / 32); + LOGP(info, "Assuimg TF length = 32 orbits and setting strobe length to {} bc", mStrobe); + } else { + mStrobe = mStrobeFallBack; + LOGP(warning, "Unforeseen number of ROFs in the loop. Using the strobe length fall back value {}", mStrobe); + } + + std::map> ZNArtag{}; // ZDCAtag[orbit] = + std::map> ZNCrtag{}; + std::map> ZNAltag{}; + std::map> ZNCltag{}; + + // ________________________________________________________________ + // FILLING ZDC ARRAY + o2::zdc::RecEventFlat ev; + + ev.init(RecBC, Energy, TDCData, Info2); + + int bkgcounterAr = 0, bkgcounterCr = 0; + int bkgcounterAl = 0, bkgcounterCl = 0; + while (ev.next()) { + + int32_t itdcA = o2::zdc::TDCZNAC; // should be == 0 + int32_t itdcC = o2::zdc::TDCZNCC; + long zdcorbit = (long)ev.ir.orbit; + + // ZDC - A side + int nhitA = ev.NtdcV(itdcA); + for (int32_t ipos = 0; ipos < nhitA; ipos++) { + + double mytdc = o2::zdc::FTDCVal * ev.TDCVal[itdcA][ipos]; + + ZNACall->Fill(mytdc); + + if (mytdc >= TimeWindowZNAr.first && mytdc <= TimeWindowZNAr.second) { + + // Backgroud event found here! + bkgcounterAr++; + Counters->Fill(3); + ZDCAtagBC->Fill(ev.ir.bc); + + if (ZNArtag.find(zdcorbit) != ZNArtag.end()) { + bool double_count_bkg = ZNArtag[zdcorbit].insert((int)ev.ir.bc).second; + if (double_count_bkg) { + LOGP(warning, "Multiple ZDCAr counts in the same orbit/bc {}/{}", zdcorbit, ev.ir.bc); + } + } else { + std::set zdcbcs{(int)ev.ir.bc}; + ZNArtag[zdcorbit] = zdcbcs; + } + + } // and of ZNAr time window + + if (mytdc >= TimeWindowZNAl.first && mytdc <= TimeWindowZNAl.second) { + + // Backgroud event found here! + bkgcounterAl++; + Counters->Fill(3); + ZDCAtagBC->Fill(ev.ir.bc); + + if (ZNAltag.find(zdcorbit) != ZNAltag.end()) { + bool double_count_bkg = ZNAltag[zdcorbit].insert((int)ev.ir.bc).second; + if (double_count_bkg) { + LOGP(warning, "Multiple ZDCAl counts in the same orbit/bc {}/{}", zdcorbit, ev.ir.bc); + } + } else { + std::set zdcbcs{(int)ev.ir.bc}; + ZNAltag[zdcorbit] = zdcbcs; + } + + } // and of ZNAl time window + } + + // ZDC - C side + int nhitC = ev.NtdcV(itdcC); + for (int32_t ipos = 0; ipos < nhitC; ipos++) { + + double mytdc = o2::zdc::FTDCVal * ev.TDCVal[itdcC][ipos]; + + ZNCCall->Fill(mytdc); + + if (mytdc >= TimeWindowZNCr.first && mytdc <= TimeWindowZNCr.second) { + + // Backgroud event found here! + bkgcounterCr++; + Counters->Fill(9); + ZDCCtagBC->Fill(ev.ir.bc); + + if (ZNCrtag.find(zdcorbit) != ZNCrtag.end()) { + bool double_count_bkg = ZNCrtag[zdcorbit].insert((int)ev.ir.bc).second; + if (double_count_bkg) { + LOGP(warning, "Multiple ZNCr counts in the same orbit/bc {}/{}", zdcorbit, ev.ir.bc); + } + } else { + std::set zdcbcs{(int)ev.ir.bc}; + ZNCrtag[zdcorbit] = zdcbcs; + } + + } // end of ZNCr time window + + if (mytdc >= TimeWindowZNCl.first && mytdc <= TimeWindowZNCl.second) { + + // Backgroud event found here! + bkgcounterCl++; + Counters->Fill(9); + ZDCCtagBC->Fill(ev.ir.bc); + + if (ZNCltag.find(zdcorbit) != ZNCltag.end()) { + bool double_count_bkg = ZNCltag[zdcorbit].insert((int)ev.ir.bc).second; + if (double_count_bkg) { + LOGP(warning, "Multiple ZNCl counts in the same orbit/bc {}/{}", zdcorbit, ev.ir.bc); + } + } else { + std::set zdcbcs{(int)ev.ir.bc}; + ZNCltag[zdcorbit] = zdcbcs; + } + + } // end of ZNCl time window + } + } // end of while ev.next() + + LOGP(info, "Found background envents from ZNAright/left {}/{} -- from ZNCright/left {}/{}", bkgcounterAr, bkgcounterAl, bkgcounterCr, bkgcounterCl); + //__________________________________________________________________ + + getClusterPatterns(clusArr, clusPatt, *mDict); + + int inTFROFcounter = -1; + + std::vector ChipSeenInThisROF(N_CHIP_IB, false); // ChipSeenInThisROF[chipid] = true/false + std::vector ChipSeenInLastROF(N_CHIP_IB, false); // ChipSeenInLastROF[chipid] = true/false + std::vector ChipSeenInLast2ROF(N_CHIP_IB, false); // ChipSeenInLast2ROF[chipid] = true/false + + // Begin loop over ROFs + for (auto it = rofRecVec.rbegin(); it != rofRecVec.rend(); ++it) { + + auto& rofRec = *it; + + inTFROFcounter++; + + Counters->Fill(2); + + ChipSeenInLast2ROF = ChipSeenInLastROF; + ChipSeenInLastROF = ChipSeenInThisROF; + std::fill(ChipSeenInThisROF.begin(), ChipSeenInThisROF.end(), false); + + auto clustersInRof = rofRec.getROFData(clusArr); + auto patternsInRof = rofRec.getROFData(mPatterns); + + Tbc = (int)rofRec.getBCData().bc; + Torbit = (long)rofRec.getBCData().orbit; + + if (inTFROFcounter < 1) { + LOGP(info, "First of TF: ITS orbit/bc {}/{}", Torbit, Tbc); + } + + // shifting by 60 bc + int eff_bc = Tbc + 60; + long eff_orbit = Torbit; + if (eff_bc > 3563) { + eff_bc -= 3564; + eff_orbit += 1; + } + + // Making a bitmask with ZDC tags for this bc + bool isZNArtagged = searchBCfromMap(ZNArtag, (long)eff_orbit, eff_bc); + if (isZNArtagged) { + Counters->Fill(4); + } + + bool isZNAltagged = searchBCfromMap(ZNAltag, (long)eff_orbit, eff_bc); + if (isZNAltagged) { + Counters->Fill(4); + } + + bool isZNCrtagged = searchBCfromMap(ZNCrtag, (long)eff_orbit, eff_bc); + if (isZNCrtagged) { + Counters->Fill(10); + } + + bool isZNCltagged = searchBCfromMap(ZNCltag, (long)eff_orbit, eff_bc); + if (isZNCltagged) { + Counters->Fill(10); + } + + TZDCtag = 0; + TZDCtag |= (isZNArtagged << 0); + TZDCtag |= (isZNAltagged << 1); + TZDCtag |= (isZNCrtagged << 2); + TZDCtag |= (isZNCltagged << 3); + + if (TZDCtag > 0) { + LOGP(info, "ZDC tag with mask {}: ZNAright = {} - ZNAleft = {} - ZNCright = {} - ZNCleft = {}", + TZDCtag, (TZDCtag >> 0) & 1, (TZDCtag >> 1) & 1, (TZDCtag >> 2) & 1, (TZDCtag >> 3) & 1); + } + + // preparing arrays for clusters analysis + std::set AvailableChips{}; + std::map> MAPsize{}; // MAP[chip] = {list if sizes} + std::map> MAPcols{}; // MAP[chip] = {list of column span} + std::map MAPntarget{}; // MAP[chip] = number of bad clusters in chip + std::map MAPcoo_mincol{}; // MAP[chip] = minimum of column coordinate + std::map MAPcoo_maxcol{}; // MAP[chip] = maximum of (column coordinate + colspan) + + // Finally loop over clusters + int ntarget_in_rof = 0; + for (int iclus = 0; iclus < clustersInRof.size(); iclus++) { + + const auto& compClus = clustersInRof[iclus]; + + auto chipid = compClus.getSensorID(); + + // Analyze only IB + if (ChipToLayer(chipid) > 2) { + continue; + } + + ChipSeenInThisROF[chipid] = true; + + int coo_col = (int)compClus.getCol(); + int coo_row = (int)compClus.getRow(); + + auto patti = patternsInRof[iclus]; + int npix = patti.getNPixels(); + int colspan = patti.getColumnSpan(); + int rowspan = patti.getRowSpan(); + + bool newchip = AvailableChips.insert(chipid).second; + if (newchip) { + MAPsize[chipid] = std::vector{}; + MAPcols[chipid] = std::vector{}; + MAPntarget[chipid] = 0; + MAPcoo_mincol[chipid] = coo_col; + MAPcoo_maxcol[chipid] = coo_col + colspan; + } + + MAPsize[chipid].push_back(npix); + MAPcols[chipid].push_back(colspan); + if (colspan >= targetClusterMinCol && rowspan <= targetClusterMaxRow) { + // Anomalous cluster found + MAPntarget[chipid] += 1; + ntarget_in_rof++; + } + MAPcoo_mincol[chipid] = TMath::Min(MAPcoo_mincol[chipid], coo_col); + MAPcoo_maxcol[chipid] = TMath::Max(MAPcoo_maxcol[chipid], coo_col + colspan); + + } // end of loop over clusters in rof + + if (ntarget_in_rof == 0 && mTFn > mSkimmedOnlyAfterTF + 2) { // extra 2 to avoid edge effects? + // do not need extra computations for this rof since it will not be saved in any case + continue; + } + + for (int ic : AvailableChips) { + + Tchip = ic; + + if (inTFROFcounter < 1) { + Tmissingafter = -1; + } else if (ChipSeenInLastROF[ic]) { + Tmissingafter = 0; + } else { + Tmissingafter = 1; + } + + if (inTFROFcounter < 2) { + Tmissingafter2 = -1; + } else if (ChipSeenInLast2ROF[ic]) { + Tmissingafter2 = 0; + } else { + Tmissingafter2 = 1; + } + + Tphi = ChipToPhi(ic); + + Tnclus = MAPsize[ic].size(); + Tmincol = MAPcoo_mincol[ic]; + Tmaxcol = MAPcoo_maxcol[ic]; + + std::sort(MAPsize[ic].begin(), MAPsize[ic].end(), std::greater<>()); + + Tnhit = Tnclus_s20 = Tnclus_s100 = Tnclus_s150 = 0; + Tnhit1 = Tnhit10 = 0.; + Tnclus_c20 = Tnclus_c100 = Tnclus_c128 = 0; + Tnclus_target = MAPntarget[ic]; + Tnhit_no1pix = 0; + Tnclus_no1pix = 0; + + int nhit_no1pix = 0; + int nclus10 = 0, nclus1 = 0; + + for (int nh : MAPsize[ic]) { + + Tnhit += nh; + + if (nh > 1) { + Tnhit_no1pix += nh; + Tnclus_no1pix += 1; + } + + if (nclus10 < 10) { + nclus10++; + Tnhit10 += 1. * nh; + } + + if (nclus1 < 1) { + nclus1++; + Tnhit1 += 1. * nh; + } + + Tnclus_s20 += (nh >= 20); + Tnclus_s100 += (nh >= 100); + Tnclus_s150 += (nh >= 150); + } + + Tnhit10 = (nclus10 == 0) ? 0. : 1. * Tnhit10 / nclus10; + + for (int nc : MAPcols[ic]) { + Tnclus_c20 += (nc >= 20); + Tnclus_c100 += (nc >= 100); + Tnclus_c128 += (nc >= 128); + } + + ITSChipEvtTree->Fill(); + if (Tnclus_target > 0) { + ITSChipEvtTargetTree->Fill(); + } + + } // end of loop over available chips + } // end of loop over ROFs +} + +// TODO: To be improved using geometry tools +int ITSBeamBackgroundStudy::ChipToLayer(int chip) +{ + if (chip < 108) { + return 0; + } + if (chip < 252) { + return 1; + } + if (chip < 432) { + return 2; + } + if (chip < 3120) { + return 3; + } + if (chip < 6480) { + return 4; + } + if (chip < 14712) { + return 5; + } + return 6; +} + +// TODO: To be improved using geometry tools +double ITSBeamBackgroundStudy::ChipToPhi(int chip) +{ + int staveinlayer = (int)(chip / 9); + for (int il = 0; il < ChipToLayer(chip); il++) { + staveinlayer -= NStaves[il]; + } + return 2. * TMath::Pi() * (0.5 + staveinlayer) / NStaves[ChipToLayer(chip)]; +} + +bool ITSBeamBackgroundStudy::searchBCfromMap(std::map>& BCperorbit, long its_orbit, int its_bc) +{ + auto it = BCperorbit.find(its_orbit); + if (it == BCperorbit.end()) { + return false; + } + + for (auto bc : it->second) { + if ((bc / mStrobe) == (its_bc / mStrobe)) { + return true; + } + } + return false; +} + +void ITSBeamBackgroundStudy::getClusterPatterns(gsl::span& ITSclus, gsl::span& ITSpatt, const o2::itsmft::TopologyDictionary& mdict) +{ + mPatterns.clear(); + mPatterns.reserve(ITSclus.size()); + auto pattIt = ITSpatt.begin(); + + for (unsigned int iClus{0}; iClus < ITSclus.size(); ++iClus) { + auto& clus = ITSclus[iClus]; + + auto pattID = clus.getPatternID(); + o2::itsmft::ClusterPattern patt; + + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mdict.isGroup(pattID)) { + patt.acquirePattern(pattIt); + } else { + patt = mdict.getPattern(pattID); + } + + mPatterns.push_back(patt); + } +} + +// getter +DataProcessorSpec getITSBeamBackgroundStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC) +{ + + // std::cout<<"DEBBUG track and clus masks "< outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestClusters(srcClustersMask, useMC); + // dataRequest->requestTracks(GTrackID::getSourcesMask("ZDC"), useMC); + + dataRequest->requestTracks(srcTracksMask, useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + return DataProcessorSpec{ + "its-beambkg-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, + Options{}}; +} + +} // namespace o2::its::study diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx new file mode 100644 index 0000000000000..c0b2d2863f3cc --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx @@ -0,0 +1,36 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITSStudies/ITSStudiesConfigParam.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ +static auto& sAvgClusSizeParamITS = o2::its::study::ITSAvgClusSizeParamConfig::Instance(); +static auto& sPIDStudyParamITS = o2::its::study::PIDStudyParamConfig::Instance(); +static auto& sCheckTracksParamsITS = o2::its::study::ITSCheckTracksParamConfig::Instance(); +static auto& sImpactParameterParamsITS = o2::its::study::ITSImpactParameterParamConfig::Instance(); +static auto& sAnomalyStudy = o2::its::study::AnomalyStudyParamConfig::Instance(); +static auto& sEfficiencyParamsITS = o2::its::study::ITSEfficiencyParamConfig::Instance(); + +O2ParamImpl(o2::its::study::ITSAvgClusSizeParamConfig); +O2ParamImpl(o2::its::study::PIDStudyParamConfig); +O2ParamImpl(o2::its::study::ITSCheckTracksParamConfig); +O2ParamImpl(o2::its::study::ITSImpactParameterParamConfig); +O2ParamImpl(o2::its::study::AnomalyStudyParamConfig); +O2ParamImpl(o2::its::study::ITSEfficiencyParamConfig); + +} // namespace study +} // namespace its +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h new file mode 100644 index 0000000000000..d56d718390b47 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::its::study::ITSAvgClusSizeParamConfig + ; +#pragma link C++ class o2::its::study::PIDStudyParamConfig + ; +#pragma link C++ class o2::its::study::ITSImpactParameterParamConfig + ; +#pragma link C++ class o2::its::study::AnomalyStudyParamConfig + ; +#pragma link C++ class o2::its::study::ITSEfficiencyParamConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::ITSAvgClusSizeParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::PIDStudyParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::ITSCheckTracksParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::ITSImpactParameterParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::AnomalyStudyParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::ITSEfficiencyParamConfig> + ; +#pragma link C++ function o2::its::studies::makeLogBinning + ; + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx new file mode 100644 index 0000000000000..9d060e71fa6d6 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx @@ -0,0 +1,561 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Skeleton derived from RS's code in ITSOffStudy + +#include +#include +#include +#include +#include "ITSStudies/ImpactParameter.h" +#include "ITSStudies/TrackCuts.h" +#include "ITSStudies/Helpers.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/Task.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DetectorsVertexing/PVertexer.h" +#include "DetectorsBase/Propagator.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DataFormatsParameters/GRPECSObject.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "Framework/DeviceSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "Framework/ConfigParamRegistry.h" +#include +#include +#include +#include +#include +#include +#include "TGeoGlobalMagField.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ +using namespace o2::framework; +using namespace o2::globaltracking; +using namespace o2::its::study; + +using DetID = o2::detectors::DetID; +using PVertex = o2::dataformats::PrimaryVertex; +using GTrackID = o2::dataformats::GlobalTrackID; + +class ImpactParameterStudy final : public Task +{ + public: + ImpactParameterStudy(std::shared_ptr dr, + std::shared_ptr gr, + mask_t src, + bool useMC) : mDataRequest(dr), + mGGCCDBRequest(gr), + mTracksSrc(src), + mUseMC(useMC) {} + ~ImpactParameterStudy() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void process(o2::globaltracking::RecoContainer&); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + void saveHistograms(); + void plotHistograms(); + std::shared_ptr mGGCCDBRequest; + GTrackID::mask_t mTracksSrc{}; + bool mUseMC{false}; ///< MC flag + o2::vertexing::PVertexer mVertexer; + float mITSROFrameLengthMUS = 0.; + float mITSROFBiasMUS = 0.; + std::unique_ptr mDBGOut; + // output histograms + std::unique_ptr mHistoContributorsPV{}; + std::unique_ptr mHistoTrackType; + std::unique_ptr mHistoTrackTypeRej; + std::unique_ptr mHistoTrackTypeAcc; + std::unique_ptr mHistoXPvVsRefitted{}; + std::unique_ptr mHistoYPvVsRefitted{}; + std::unique_ptr mHistoZPvVsRefitted{}; + std::unique_ptr mHistoXDeltaPVrefit{}; + std::unique_ptr mHistoYDeltaPVrefit{}; + std::unique_ptr mHistoZDeltaPVrefit{}; + std::unique_ptr mHistoImpParXy{}; + std::unique_ptr mHistoImpParZ{}; + std::unique_ptr mHistoImpParXyPhi{}; + std::unique_ptr mHistoImpParZPhi{}; + std::unique_ptr mHistoImpParXyTop{}; + std::unique_ptr mHistoImpParZTop{}; + std::unique_ptr mHistoImpParXyBottom{}; + std::unique_ptr mHistoImpParZBottom{}; + std::unique_ptr mHistoImpParXyPositiveCharge{}; + std::unique_ptr mHistoImpParZPositiveCharge{}; + std::unique_ptr mHistoImpParXyNegativeCharge{}; + std::unique_ptr mHistoImpParZNegativeCharge{}; + std::unique_ptr mHistoImpParXyMeanPhi{}; + std::unique_ptr mHistoImpParZMeanPhi{}; + std::unique_ptr mHistoImpParXySigmaPhi{}; + std::unique_ptr mHistoImpParZSigmaPhi{}; + std::unique_ptr mHistoImpParXySigma{}; + std::unique_ptr mHistoImpParZSigma{}; + std::unique_ptr mHistoImpParXySigmaTop{}; + std::unique_ptr mHistoImpParZSigmaTop{}; + std::unique_ptr mHistoImpParXySigmaBottom{}; + std::unique_ptr mHistoImpParZSigmaBottom{}; + std::unique_ptr mHistoImpParXyMeanTop{}; + std::unique_ptr mHistoImpParZMeanTop{}; + std::unique_ptr mHistoImpParXyMeanBottom{}; + std::unique_ptr mHistoImpParZMeanBottom{}; + std::unique_ptr mHistoImpParXySigmaPositiveCharge{}; + std::unique_ptr mHistoImpParZSigmaPositiveCharge{}; + std::unique_ptr mHistoImpParXySigmaNegativeCharge{}; + std::unique_ptr mHistoImpParZSigmaNegativeCharge{}; + + // output file + TString mOutName{}; + + // Data + std::shared_ptr mDataRequest; + gsl::span mPVertices; +}; + +void ImpactParameterStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + auto& params = ITSImpactParameterParamConfig::Instance(); + mOutName = params.outFileName; + mDBGOut = std::make_unique(mOutName.Data(), "recreate"); + + std::vector logPtBinning = helpers::makeLogBinning(100, 0.1, 10); + mHistoTrackType = std::make_unique("trackType", "# Track Type", 32, -0.5, 31.5); + mHistoTrackTypeRej = std::make_unique("trackTypeRej", "# Rejected Track Type", 32, -0.5, 31.5); + mHistoTrackTypeAcc = std::make_unique("trackTypeAcc", "# Filtered Track Type", 32, -0.5, 31.5); + mHistoContributorsPV = std::make_unique("nContribPVrefit", "# Contributors per PV", 100, 0, 100); + mHistoXPvVsRefitted = std::make_unique("histo2dXPvVsPVrefit", "#X PV vs PV_{-1}, #mum", 100, -10, 10, 100, -10, 10); + mHistoYPvVsRefitted = std::make_unique("histo2dYPvVsPVrefit", "#Y PV vs PV_{-1}, #mum", 100, -10, 10, 100, -10, 10); + mHistoZPvVsRefitted = std::make_unique("histo2dZPvVsPVrefit", "#Z PV vs PV_{-1}, #mum", 100, -10, 10, 100, -10, 10); + mHistoXDeltaPVrefit = std::make_unique("histoDeltaXPVrefit", "#DeltaX (PV-PV_{-1}), #mum", 300, -15, 15); + mHistoYDeltaPVrefit = std::make_unique("histoDeltaYPVrefit", "#DeltaY (PV-PV_{-1}), #mum", 300, -15, 15); + mHistoZDeltaPVrefit = std::make_unique("histoDeltaZPVrefit", "#DeltaZ (PV-PV_{-1}), #mum", 300, -15, 15); + mHistoImpParXyPhi = std::make_unique("histoImpParXyPhi", "#Phi; #phi; Impact Parameter XY (#mum);", 100, 0., 6.28, 100, -1000, 1000); + mHistoImpParZPhi = std::make_unique("histoImpParZPhi", "#Phi; #phi; Impact Parameter Z (#mum);", 100, 0., 6.28, 100, -1000, 1000); + mHistoImpParZ = std::make_unique("histoImpParZ", "Impact Parameter Z; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParXy = std::make_unique("histoImpParXy", "Impact Parameter XY; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParXyTop = std::make_unique("histoImpParXyTop", "Impact Parameter XY, #phi(track)<#pi; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParXyBottom = std::make_unique("histoImpParXyBottom", "Impact Parameter XY, #phi(track)>#pi; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParZTop = std::make_unique("histoImpParZTop", "Impact Parameter Z, #phi(track)<#pi; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParZBottom = std::make_unique("histoImpParZBottom", "Impact Parameter Z, #phi(track)>#pi; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParXyNegativeCharge = std::make_unique("histoImpParXyNegativeCharge", "Impact Parameter XY, sign<0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParXyPositiveCharge = std::make_unique("histoImpParXyPositiveCharge", "Impact Parameter XY, sign>0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParZNegativeCharge = std::make_unique("histoImpParZNegativeCharge", "Impact Parameter Z, sign<0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParZPositiveCharge = std::make_unique("histoImpParZPositiveCharge", "Impact Parameter Z, sign>0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + + mHistoImpParXySigma = std::make_unique("histoImpParXySigma", "Pointing Resolution XY; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZSigma = std::make_unique("histoImpParZSigma", "Pointing Resolution Z; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXyMeanPhi = std::make_unique("histoImpParXyMean", "Pointing Resolution XY; #phi; Mean #mum", 100, 0., 6.28); + mHistoImpParZMeanPhi = std::make_unique("histoImpParZMean", "Pointing Resolution Z; #phi; Mean #mum", 100, 0., 6.28); + mHistoImpParXySigmaPhi = std::make_unique("histoImpParXySigmaPhi", "Pointing Resolution XY; #phi; #sigma #mum", 100, 0., 6.28); + mHistoImpParZSigmaPhi = std::make_unique("histoImpParZSigmaPhi", "Pointing Resolution Z; #phi; #sigma #mum", 100, 0., 6.28); + mHistoImpParXySigmaTop = std::make_unique("histoImpParXySigmaTop", "Pointing Resolution XY, Top; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZSigmaTop = std::make_unique("histoImpParZSigmaTop", "Pointing Resolution Z, Top; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXySigmaBottom = std::make_unique("histoImpParXySigmaBottom", "Pointing Resolution XY, Bottom; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZSigmaBottom = std::make_unique("histoImpParZSigmaBottom", "Pointing Resolution Z, Bottom; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXyMeanTop = std::make_unique("histoImpParXyMeanTop", "Pointing Resolution XY, Top; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZMeanTop = std::make_unique("histoImpParZMeanTop", "Pointing Resolution Z, Top; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXyMeanBottom = std::make_unique("histoImpParXyMeanBottom", "Mean Pointing Resolution XY, Bottom; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZMeanBottom = std::make_unique("histoImpParZMeanBottom", "Mean Pointing Resolution Z, Bottom; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXySigmaPositiveCharge = std::make_unique("histoImpParXySigmaPositiveCharge", "Pointing Resolution XY, sign>0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZSigmaPositiveCharge = std::make_unique("histoImpParZSigmaPositiveCharge", "Pointing Resolution Z, sign>0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXySigmaNegativeCharge = std::make_unique("histoImpParXySigmaNegativeCharge", "Pointing Resolution XY, sign<0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZSigmaNegativeCharge = std::make_unique("histoImpParZSigmaNegativeCharge", "Pointing Resolution Z, sign<0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + + mHistoTrackType->SetDirectory(nullptr); + mHistoTrackTypeRej->SetDirectory(nullptr); + mHistoTrackTypeAcc->SetDirectory(nullptr); + mHistoContributorsPV->SetDirectory(nullptr); + mHistoXPvVsRefitted->SetDirectory(nullptr); + mHistoYPvVsRefitted->SetDirectory(nullptr); + mHistoZPvVsRefitted->SetDirectory(nullptr); + mHistoXDeltaPVrefit->SetDirectory(nullptr); + mHistoYDeltaPVrefit->SetDirectory(nullptr); + mHistoZDeltaPVrefit->SetDirectory(nullptr); + mHistoImpParZ->SetDirectory(nullptr); + mHistoImpParXy->SetDirectory(nullptr); + mHistoImpParXyPhi->SetDirectory(nullptr); + mHistoImpParZPhi->SetDirectory(nullptr); + mHistoImpParXyTop->SetDirectory(nullptr); + mHistoImpParXyBottom->SetDirectory(nullptr); + mHistoImpParZTop->SetDirectory(nullptr); + mHistoImpParZBottom->SetDirectory(nullptr); + mHistoImpParXyNegativeCharge->SetDirectory(nullptr); + mHistoImpParXyPositiveCharge->SetDirectory(nullptr); + mHistoImpParZNegativeCharge->SetDirectory(nullptr); + mHistoImpParZPositiveCharge->SetDirectory(nullptr); + mHistoImpParXyMeanPhi->SetDirectory(nullptr); + mHistoImpParZMeanPhi->SetDirectory(nullptr); + mHistoImpParXySigmaPhi->SetDirectory(nullptr); + mHistoImpParZSigmaPhi->SetDirectory(nullptr); + mHistoImpParXySigma->SetDirectory(nullptr); + mHistoImpParZSigma->SetDirectory(nullptr); + mHistoImpParXySigmaTop->SetDirectory(nullptr); + mHistoImpParZSigmaTop->SetDirectory(nullptr); + mHistoImpParXySigmaBottom->SetDirectory(nullptr); + mHistoImpParZSigmaBottom->SetDirectory(nullptr); + mHistoImpParXyMeanTop->SetDirectory(nullptr); + mHistoImpParZMeanTop->SetDirectory(nullptr); + mHistoImpParXyMeanBottom->SetDirectory(nullptr); + mHistoImpParZMeanBottom->SetDirectory(nullptr); + mHistoImpParXySigmaPositiveCharge->SetDirectory(nullptr); + mHistoImpParZSigmaPositiveCharge->SetDirectory(nullptr); + mHistoImpParXySigmaNegativeCharge->SetDirectory(nullptr); + mHistoImpParZSigmaNegativeCharge->SetDirectory(nullptr); +} + +void ImpactParameterStudy::run(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); + process(recoData); +} + +void ImpactParameterStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + auto& params = ITSImpactParameterParamConfig::Instance(); + + o2::base::GRPGeomHelper::instance().getGRPMagField()->print(); + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::vector vecPvContributorTrackParCov; + std::vector vec_globID_contr = {}; + std::vector trueVecPvContributorTrackParCov; + std::vector trueVec_globID_contr = {}; + float impParRPhi, impParZ; + constexpr float toMicrometers = 10000.f; // Conversion from [cm] to [mum] + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto pvertices = recoData.getPrimaryVertices(); + auto trmTPCTracks = recoData.getTPCTracks(); + + TrackCuts cuts; // ITS and TPC(commented) cut implementation + + int nv = vtxRefs.size() - 1; // The last entry is for unassigned tracks, ignore them + for (int iv = 0; iv < nv; iv++) { // Loop over PVs + LOGP(info, "*** NEW VERTEX {}***", iv); + const auto& vtref = vtxRefs[iv]; + const o2::dataformats::VertexBase& pv = pvertices[iv]; + int it = vtref.getFirstEntry(), itLim = it + vtref.getEntries(); + int i = 0; + + for (; it < itLim; it++) { + auto tvid = trackIndex[it]; + // LOGP(info,"ORIGIN: {}", tvid.getSourceName(tvid.getSource())); + mHistoTrackType->Fill(tvid.getSource()); + if (!recoData.isTrackSourceLoaded(tvid.getSource())) { + // LOGP(info,"SOURCE Rej: {}", tvid.getSourceName(tvid.getSource())); + mHistoTrackTypeRej->Fill(tvid.getSource()); + continue; + } + // LOGP(info,"ORIGIN: {} INDEX: {}", tvid.getSourceName(tvid.getSource()), trackIndex[it]); + mHistoTrackTypeAcc->Fill(tvid.getSource()); + const o2::track::TrackParCov& trc = recoData.getTrackParam(tvid); // The actual track + + auto refs = recoData.getSingleDetectorRefs(tvid); + if (!refs[GTrackID::ITS].isIndexSet()) { // might be an afterburner track + // LOGP(info, " ** AFTERBURN **"); + continue; + } + // Apply track selections + if (params.applyTrackCuts) { + if (!cuts.isSelected(tvid, recoData)) { + // LOGP(info, "Fail"); + continue; + } + } + // Vectors for reconstructing the vertex + vec_globID_contr.push_back(trackIndex[it]); + vecPvContributorTrackParCov.push_back(trc); + // Store vector with index and tracks ITS only -- same order as the GLOBAL vectors + int itsTrackID = refs[GTrackID::ITS].getIndex(); + const o2::track::TrackParCov& trcITS = recoData.getTrackParam(itsTrackID); // The actual ITS track + trueVec_globID_contr.push_back(itsTrackID); + trueVecPvContributorTrackParCov.push_back(trcITS); + // LOGP(info, "SOURCE: {} indexGLOBAL: {} indexITS: {}", tvid.getSourceName(tvid.getSource()), trackIndex[it], trueVec_globID_contr[i]); + i++; + } // end loop tracks + LOGP(info, "************ SIZE INDEX GLOBAL: {} ", vec_globID_contr.size()); + LOGP(info, "************ SIZE INDEX ITS: {} ", trueVec_globID_contr.size()); + + it = vtref.getFirstEntry(); + // Preparation PVertexer refit + o2::conf::ConfigurableParam::updateFromString("pvertexer.useMeanVertexConstraint=false"); + bool PVrefit_doable = mVertexer.prepareVertexRefit(vecPvContributorTrackParCov, pv); + if (!PVrefit_doable) { + LOG(info) << "Not enough tracks accepted for the refit --> Skipping vertex"; + } else { + mHistoContributorsPV->Fill(vecPvContributorTrackParCov.size()); + // Neglect vertices with small number of contributors (configurable) + if (vecPvContributorTrackParCov.size() < params.minNumberOfContributors) { + continue; + } + for (it = 0; it < vec_globID_contr.size(); it++) { + // vector of booleans to keep track of the track to be skipped + std::vector vec_useTrk_PVrefit(vec_globID_contr.size(), true); + auto tvid = vec_globID_contr[it]; + auto trackIterator = std::find(vec_globID_contr.begin(), vec_globID_contr.end(), vec_globID_contr[it]); + if (trackIterator != vec_globID_contr.end()) { + // LOGP(info,"************ Trackiterator: {} : {} ", it+1, vec_globID_contr[it]); + o2::dataformats::VertexBase PVbase_recalculated; + /// this track contributed to the PV fit: let's do the refit without it + const int entry = std::distance(vec_globID_contr.begin(), trackIterator); + if (!params.useAllTracks) { + vec_useTrk_PVrefit[entry] = false; /// remove the track from the PV refitting + } + auto Pvtx_refitted = mVertexer.refitVertex(vec_useTrk_PVrefit, pv); // vertex refit + // enable the dca recalculation for the current PV contributor, after removing it from the PV refit + bool recalc_imppar = true; + if (Pvtx_refitted.getChi2() < 0) { + // LOG(info) << "---> Refitted vertex has bad chi2 = " << Pvtx_refitted.getChi2(); + recalc_imppar = false; + } + vec_useTrk_PVrefit[entry] = true; /// restore the track for the next PV refitting + if (recalc_imppar) { + const double DeltaX = pv.getX() - Pvtx_refitted.getX(); + const double DeltaY = pv.getY() - Pvtx_refitted.getY(); + const double DeltaZ = pv.getZ() - Pvtx_refitted.getZ(); + mHistoXPvVsRefitted->Fill(pv.getX(), Pvtx_refitted.getX()); + mHistoYPvVsRefitted->Fill(pv.getY(), Pvtx_refitted.getY()); + mHistoZPvVsRefitted->Fill(pv.getZ(), Pvtx_refitted.getZ()); + mHistoXDeltaPVrefit->Fill(DeltaX); + mHistoYDeltaPVrefit->Fill(DeltaY); + mHistoZDeltaPVrefit->Fill(DeltaZ); + + // fill the newly calculated PV + PVbase_recalculated.setX(Pvtx_refitted.getX()); + PVbase_recalculated.setY(Pvtx_refitted.getY()); + PVbase_recalculated.setZ(Pvtx_refitted.getZ()); + PVbase_recalculated.setCov(Pvtx_refitted.getSigmaX2(), Pvtx_refitted.getSigmaXY(), Pvtx_refitted.getSigmaY2(), Pvtx_refitted.getSigmaXZ(), Pvtx_refitted.getSigmaYZ(), Pvtx_refitted.getSigmaZ2()); + + auto trueID = trueVec_globID_contr[it]; + const o2::track::TrackParCov& trc = recoData.getTrackParam(trueID); + auto pt = trc.getPt(); + std::array dcaInfo{-999., -999.}; + // LOGP(info, " ---> Bz={}", o2::base::Propagator::Instance()->getNominalBz()); + o2::track::TrackPar trcTmp{trc}; + if (o2::base::Propagator::Instance()->propagateToDCABxByBz({Pvtx_refitted.getX(), Pvtx_refitted.getY(), Pvtx_refitted.getZ()}, trcTmp, 2.f, matCorr, &dcaInfo)) { + impParRPhi = dcaInfo[0] * toMicrometers; + impParZ = dcaInfo[1] * toMicrometers; + mHistoImpParXy->Fill(pt, impParRPhi); + mHistoImpParZ->Fill(pt, impParZ); + double phi = trcTmp.getPhi(); + mHistoImpParXyPhi->Fill(phi, impParRPhi); + mHistoImpParZPhi->Fill(phi, impParZ); + if (phi < TMath::Pi()) { + mHistoImpParXyTop->Fill(pt, impParRPhi); + mHistoImpParZTop->Fill(pt, impParZ); + } + if (phi > TMath::Pi()) { + mHistoImpParXyBottom->Fill(pt, impParRPhi); + mHistoImpParZBottom->Fill(pt, impParZ); + } + double sign = trcTmp.getSign(); + if (sign < 0) { + mHistoImpParXyNegativeCharge->Fill(pt, impParRPhi); + mHistoImpParZNegativeCharge->Fill(pt, impParZ); + } else { + mHistoImpParXyPositiveCharge->Fill(pt, impParRPhi); + mHistoImpParZPositiveCharge->Fill(pt, impParZ); + } + } + } // end recalc impact param + } + } // end loop tracks in pv refitted + } // else pv refit duable */ + vec_globID_contr.clear(); + vecPvContributorTrackParCov.clear(); + trueVec_globID_contr.clear(); + trueVecPvContributorTrackParCov.clear(); + + } // end loop pv + + mHistoImpParXy->FitSlicesY(); + mHistoImpParZ->FitSlicesY(); + mHistoImpParXyPhi->FitSlicesY(); + mHistoImpParZPhi->FitSlicesY(); + mHistoImpParXyTop->FitSlicesY(); + mHistoImpParZTop->FitSlicesY(); + mHistoImpParXyBottom->FitSlicesY(); + mHistoImpParZBottom->FitSlicesY(); + mHistoImpParXyNegativeCharge->FitSlicesY(); + mHistoImpParZNegativeCharge->FitSlicesY(); + mHistoImpParXyPositiveCharge->FitSlicesY(); + mHistoImpParZPositiveCharge->FitSlicesY(); + mHistoImpParXySigma = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXy_2"))->Clone())); + mHistoImpParZSigma = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZ_2"))->Clone())); + mHistoImpParXyMeanPhi = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyPhi_1"))->Clone())); + mHistoImpParZMeanPhi = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZPhi_1"))->Clone())); + mHistoImpParXySigmaPhi = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyPhi_2"))->Clone())); + mHistoImpParZSigmaPhi = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZPhi_2"))->Clone())); + mHistoImpParXyMeanTop = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyTop_1"))->Clone())); + mHistoImpParZMeanTop = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZTop_1"))->Clone())); + mHistoImpParXyMeanBottom = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyBottom_1"))->Clone())); + mHistoImpParZMeanBottom = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZBottom_1"))->Clone())); + mHistoImpParXySigmaTop = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyTop_2"))->Clone())); + mHistoImpParZSigmaTop = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZTop_2"))->Clone())); + mHistoImpParXySigmaBottom = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyBottom_2"))->Clone())); + mHistoImpParZSigmaBottom = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZBottom_2"))->Clone())); + mHistoImpParXySigmaNegativeCharge = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyNegativeCharge_2"))->Clone())); + mHistoImpParZSigmaNegativeCharge = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZNegativeCharge_2"))->Clone())); + mHistoImpParXySigmaPositiveCharge = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyPositiveCharge_2"))->Clone())); + mHistoImpParZSigmaPositiveCharge = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZPositiveCharge_2"))->Clone())); +} +// end process + +void ImpactParameterStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + auto grp = o2::base::GRPGeomHelper::instance().getGRPECS(); + mVertexer.init(); + if (pc.services().get().inputTimesliceId == 0) { + } + } +} + +void ImpactParameterStudy::saveHistograms() +{ + mDBGOut.reset(); + TFile fout(mOutName.Data(), "update"); + fout.WriteTObject(mHistoContributorsPV.get()); + fout.WriteTObject(mHistoTrackType.get()); + fout.WriteTObject(mHistoTrackTypeRej.get()); + fout.WriteTObject(mHistoTrackTypeAcc.get()); + fout.WriteTObject(mHistoXPvVsRefitted.get()); + fout.WriteTObject(mHistoYPvVsRefitted.get()); + fout.WriteTObject(mHistoZPvVsRefitted.get()); + fout.WriteTObject(mHistoXDeltaPVrefit.get()); + fout.WriteTObject(mHistoYDeltaPVrefit.get()); + fout.WriteTObject(mHistoZDeltaPVrefit.get()); + fout.WriteTObject(mHistoImpParZ.get()); + fout.WriteTObject(mHistoImpParXy.get()); + fout.WriteTObject(mHistoImpParXyPhi.get()); + fout.WriteTObject(mHistoImpParZPhi.get()); + fout.WriteTObject(mHistoImpParXyTop.get()); + fout.WriteTObject(mHistoImpParZTop.get()); + fout.WriteTObject(mHistoImpParXyBottom.get()); + fout.WriteTObject(mHistoImpParZBottom.get()); + fout.WriteTObject(mHistoImpParXyPositiveCharge.get()); + fout.WriteTObject(mHistoImpParZPositiveCharge.get()); + fout.WriteTObject(mHistoImpParXyNegativeCharge.get()); + fout.WriteTObject(mHistoImpParZNegativeCharge.get()); + fout.WriteTObject(mHistoImpParZMeanPhi.get()); + fout.WriteTObject(mHistoImpParXyMeanPhi.get()); + fout.WriteTObject(mHistoImpParZSigmaPhi.get()); + fout.WriteTObject(mHistoImpParXySigmaPhi.get()); + fout.WriteTObject(mHistoImpParZSigma.get()); + fout.WriteTObject(mHistoImpParXySigma.get()); + fout.WriteTObject(mHistoImpParZMeanTop.get()); + fout.WriteTObject(mHistoImpParXyMeanTop.get()); + fout.WriteTObject(mHistoImpParZMeanBottom.get()); + fout.WriteTObject(mHistoImpParXyMeanBottom.get()); + fout.WriteTObject(mHistoImpParZSigmaTop.get()); + fout.WriteTObject(mHistoImpParXySigmaTop.get()); + fout.WriteTObject(mHistoImpParZSigmaBottom.get()); + fout.WriteTObject(mHistoImpParXySigmaBottom.get()); + fout.WriteTObject(mHistoImpParZSigmaPositiveCharge.get()); + fout.WriteTObject(mHistoImpParXySigmaPositiveCharge.get()); + fout.WriteTObject(mHistoImpParZSigmaNegativeCharge.get()); + fout.WriteTObject(mHistoImpParXySigmaNegativeCharge.get()); + LOGP(info, "Impact Parameters histograms stored in {}", mOutName.Data()); + fout.Close(); +} + +void ImpactParameterStudy::plotHistograms() +{ + TString directoryName = "./plotsImpactParameter"; + gSystem->mkdir(directoryName); + TCanvas* dcaXyVsdcaZ = helpers::prepareSimpleCanvas2Histograms(*mHistoImpParXySigma, kRed, "#sigma_{XY}", *mHistoImpParZSigma, kBlue, "#sigma_{Z}"); + TCanvas* dcaXyTopVsBottom = helpers::prepareSimpleCanvas2Histograms(*mHistoImpParXySigmaTop, kRed, "#sigma_{XY}^{TOP}", *mHistoImpParXySigmaBottom, kBlue, "#sigma_{XY}^{BOTTOM}"); + TCanvas* dcaZTopVsBottom = helpers::prepareSimpleCanvas2Histograms(*mHistoImpParZSigmaTop, kRed, "#sigma_{Z}^{TOP}", *mHistoImpParZSigmaBottom, kBlue, "#sigma_{Z}^{BOTTOM}"); + TCanvas* dcaXyPosVsNeg = helpers::prepareSimpleCanvas2Histograms(*mHistoImpParXySigmaPositiveCharge, kRed, "#sigma_{XY}^{positive}", *mHistoImpParXySigmaNegativeCharge, kBlue, "#sigma_{XY}^{negative}"); + TCanvas* dcaZPosVsNeg = helpers::prepareSimpleCanvas2Histograms(*mHistoImpParZSigmaPositiveCharge, kRed, "#sigma_{Z}^{positive}", *mHistoImpParZSigmaNegativeCharge, kBlue, "#sigma_{Z}^{negative}"); + TCanvas* dcaXYvsPhi = helpers::plot2DwithMeanAndSigma(*mHistoImpParXyPhi, *mHistoImpParXyMeanPhi, *mHistoImpParXySigmaPhi, kRed); + TCanvas* dcaZvsPhi = helpers::plot2DwithMeanAndSigma(*mHistoImpParZPhi, *mHistoImpParZMeanPhi, *mHistoImpParZSigmaPhi, kRed); + TCanvas* dcaXyPhiMeanTopVsBottom = helpers::prepareSimpleCanvas2DcaMeanValues(*mHistoImpParXyMeanTop, kRed, "mean_{XY}^{TOP}", *mHistoImpParXyMeanBottom, kBlue, "mean_{XY}^{BOTTOM}"); + TCanvas* dcaZPhiMeanTopVsBottom = helpers::prepareSimpleCanvas2DcaMeanValues(*mHistoImpParZMeanTop, kRed, "mean_{Z}^{TOP}", *mHistoImpParZMeanBottom, kBlue, "mean_{Z}^{BOTTOM}"); + + dcaXyVsdcaZ->SaveAs(Form("%s/ComparisonDCAXYvsZ.png", directoryName.Data())); + dcaXyTopVsBottom->SaveAs(Form("%s/ComparisonDCAXyTopVsBottom.png", directoryName.Data())); + dcaXyPosVsNeg->SaveAs(Form("%s/ComparisonDCAXyPosVsNeg.png", directoryName.Data())); + dcaZTopVsBottom->SaveAs(Form("%s/ComparisonDCAZTopVsBottom.png", directoryName.Data())); + dcaZPosVsNeg->SaveAs(Form("%s/ComparisonDCAZPosVsNeg.png", directoryName.Data())); + dcaXYvsPhi->SaveAs(Form("%s/dcaXYvsPhi.png", directoryName.Data())); + dcaZvsPhi->SaveAs(Form("%s/dcaZvsPhi.png", directoryName.Data())); + dcaXyPhiMeanTopVsBottom->SaveAs(Form("%s/dcaXyPhiMeanTopVsBottom.png", directoryName.Data())); + dcaZPhiMeanTopVsBottom->SaveAs(Form("%s/dcaZPhiMeanTopVsBottom.png", directoryName.Data())); +} + +void ImpactParameterStudy::endOfStream(EndOfStreamContext& ec) +{ + auto& params = ITSImpactParameterParamConfig::Instance(); + if (params.generatePlots) { + saveHistograms(); + plotHistograms(); + } +} + +void ImpactParameterStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } +} + +DataProcessorSpec getImpactParameterStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestPrimaryVertices(useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + + return DataProcessorSpec{ + "its-study-impactparameter", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracksMask, useMC)}, + Options{}}; +} + +} // namespace study +} // namespace its +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx new file mode 100644 index 0000000000000..9a7f6c218cd12 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx @@ -0,0 +1,351 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file PIDStudy.cxx +/// \brief Study to evaluate the PID performance of the ITS +/// \author Francesco Mazzaschi, fmazzasc@cern.ch + +#include "ITSStudies/PIDStudy.h" +#include "ITSStudies/ITSStudiesConfigParam.h" + +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "Steer/MCKinematicsReader.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "ITStracking/IOUtils.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/PID.h" +#include "ReconstructionDataFormats/V0.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/DCA.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsTPC/PIDResponse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; +using PVertex = o2::dataformats::PrimaryVertex; +using V0 = o2::dataformats::V0; +using ITSCluster = o2::BaseCluster; +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +using Track = o2::track::TrackParCov; +using TrackITS = o2::its::TrackITS; +using TrackTPC = o2::tpc::TrackTPC; +using TrackITSTPC = o2::dataformats::TrackTPCITS; +using PIDResponse = o2::tpc::PIDResponse; +using DCA = o2::dataformats::DCA; +using PID = o2::track::PID; + +// structure for storing the output tree +struct particle { + // mc properties + int pdg = -1; + bool fakeMatch = 0; + // reco properties + int sign = -1; + float p, pt, pTPC, pITS, eta, phi, tgL, chi2ITS, chi2TPC, chi2ITSTPC; + int nClusTPC; + float dEdx, nSigmaDeu, nSigmaP, nSigmaK, nSigmaPi, nSigmaE; + int clSizeL0, clSizeL1, clSizeL2, clSizeL3, clSizeL4, clSizeL5, clSizeL6; + std::array clSizesITS; +}; + +class PIDStudy : public Task +{ + public: + PIDStudy(std::shared_ptr dr, + std::shared_ptr gr, + bool isMC, + std::shared_ptr kineReader) : mDataRequest{dr}, mGGCCDBRequest(gr), mUseMC(isMC), mKineReader(kineReader){}; + ~PIDStudy() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + + private: + // Other functions + void process(o2::globaltracking::RecoContainer&); + void loadData(o2::globaltracking::RecoContainer&); + + // Helper functions + void saveOutput(); + void updateTimeDependentParams(ProcessingContext& pc); + void getClusterSizes(std::vector&, const gsl::span, gsl::span::iterator&, const o2::itsmft::TopologyDictionary*); + std::array getTrackClusterSizes(const TrackITS& track); + float computeNSigma(PID pid, TrackTPC& tpcTrack, float resolution); + + // Running options + bool mUseMC; + + PIDResponse mPIDresponse; + float mBBres; + // Data + std::shared_ptr mGGCCDBRequest; + std::shared_ptr mDataRequest; + std::vector mClusterSizes; + gsl::span mClusters; + gsl::span mInputITSidxs; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + + std::unique_ptr mDBGOut; + std::string mOutName; + std::shared_ptr mKineReader; +}; + +void PIDStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + LOGP(info, "Starting average cluster size study..."); + + if (mUseMC) { // for counting the missed K0shorts + mKineReader = std::make_unique("collisioncontext.root"); + } + auto& params = o2::its::study::PIDStudyParamConfig::Instance(); + mOutName = params.outFileName; + mPIDresponse.setBetheBlochParams(params.mBBpars); + mBBres = params.mBBres; + LOGP(info, "PID size study initialized."); + + // prepare output tree + mDBGOut = std::make_unique(mOutName.c_str(), "recreate"); +} + +void PIDStudy::run(ProcessingContext& pc) +{ + // auto geom = o2::its::GeometryTGeo::Instance(); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(recoData); +} + +void PIDStudy::getClusterSizes(std::vector& clusSizeVec, const gsl::span ITSclus, gsl::span::iterator& pattIt, const o2::itsmft::TopologyDictionary* mdict) +{ + for (unsigned int iClus{0}; iClus < ITSclus.size(); ++iClus) { + auto& clus = ITSclus[iClus]; + auto pattID = clus.getPatternID(); + int npix; + o2::itsmft::ClusterPattern patt; + + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mdict->isGroup(pattID)) { + patt.acquirePattern(pattIt); + npix = patt.getNPixels(); + } else { + npix = mdict->getNpixels(pattID); + patt = mdict->getPattern(pattID); + } + clusSizeVec[iClus] = npix; + } +} + +void PIDStudy::loadData(o2::globaltracking::RecoContainer& recoData) +{ + mInputITSidxs = recoData.getITSTracksClusterRefs(); + mClusters = recoData.getITSClusters(); + auto clusPatt = recoData.getITSClustersPatterns(); + mClusterSizes.resize(mClusters.size()); + auto pattIt = clusPatt.begin(); + getClusterSizes(mClusterSizes, mClusters, pattIt, mDict); +} + +void PIDStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + loadData(recoData); + auto ITSTPCtracks = recoData.getTPCITSTracks(); + LOGP(debug, "Found {} ITSTPC tracks.", ITSTPCtracks.size()); + + gsl::span mcLabelsITS, mcLabelsTPC; + if (mUseMC) { + mcLabelsITS = recoData.getITSTracksMCLabels(); + mcLabelsTPC = recoData.getTPCTracksMCLabels(); + LOGP(debug, "Found {} ITS labels.", mcLabelsITS.size()); + LOGP(debug, "Found {} TPC labels.", mcLabelsTPC.size()); + } + + for (unsigned int iTrack{0}; iTrack < ITSTPCtracks.size(); ++iTrack) { + + auto& ITSTPCtrack = ITSTPCtracks[iTrack]; + if (ITSTPCtrack.getRefITS().getSource() == GTrackID::ITSAB) { // excluding Afterburned tracks + continue; + } + particle part; + auto ITStrack = recoData.getITSTrack(ITSTPCtrack.getRefITS()); + auto TPCtrack = recoData.getTPCTrack(ITSTPCtrack.getRefTPC()); + + if (mUseMC) { + // MC info + auto& mcLabelITS = mcLabelsITS[ITSTPCtrack.getRefITS().getIndex()]; + auto& mcLabelTPC = mcLabelsTPC[ITSTPCtrack.getRefTPC().getIndex()]; + if (mcLabelITS.getTrackID() != (int)mcLabelTPC.getTrackID()) { + part.fakeMatch = 1; + } + auto mctrk = mKineReader->getTrack(mcLabelITS); + part.pdg = mctrk->GetPdgCode(); + } + + part.sign = ITSTPCtrack.getSign(); + part.clSizesITS = getTrackClusterSizes(ITStrack); + part.p = ITSTPCtrack.getP(); + part.pt = ITSTPCtrack.getPt(); + part.pTPC = TPCtrack.getP(); + part.pITS = ITStrack.getP(); + part.eta = ITSTPCtrack.getEta(); + part.phi = ITSTPCtrack.getPhi(); + part.tgL = ITSTPCtrack.getTgl(); + part.chi2ITS = ITStrack.getChi2(); + part.chi2TPC = TPCtrack.getChi2(); + part.chi2ITSTPC = ITSTPCtrack.getChi2Match(); + + part.clSizeL0 = part.clSizesITS[0]; + part.clSizeL1 = part.clSizesITS[1]; + part.clSizeL2 = part.clSizesITS[2]; + part.clSizeL3 = part.clSizesITS[3]; + part.clSizeL4 = part.clSizesITS[4]; + part.clSizeL5 = part.clSizesITS[5]; + part.clSizeL6 = part.clSizesITS[6]; + + // PID info + part.dEdx = TPCtrack.getdEdx().dEdxTotTPC; + part.nClusTPC = TPCtrack.getNClusters(); + // 7% resolution for all particles + part.nSigmaDeu = computeNSigma(PID::Deuteron, TPCtrack, mBBres); + part.nSigmaP = computeNSigma(PID::Proton, TPCtrack, mBBres); + part.nSigmaK = computeNSigma(PID::Kaon, TPCtrack, mBBres); + part.nSigmaPi = computeNSigma(PID::Pion, TPCtrack, mBBres); + part.nSigmaE = computeNSigma(PID::Electron, TPCtrack, mBBres); + + if (mUseMC) { + (*mDBGOut) << "outTree" + << "pdg=" << part.pdg << "fakeMatch=" << part.fakeMatch; + } + (*mDBGOut) << "outTree" + << "sign=" << part.sign << "p=" << part.p << "pt=" << part.pt << "pTPC=" << part.pTPC << "pITS=" << part.pITS + << "eta=" << part.eta << "phi=" << part.phi << "tgL=" << part.tgL << "chi2ITS=" << part.chi2ITS << "chi2TPC=" + << part.chi2TPC << "chi2ITSTPC=" << part.chi2ITSTPC << "dEdx=" << part.dEdx << "nClusTPC=" << part.nClusTPC + << "nSigmaDeu=" << part.nSigmaDeu << "nSigmaP=" << part.nSigmaP << "nSigmaK=" << part.nSigmaK << "nSigmaPi=" + << part.nSigmaPi << "nSigmaE=" << part.nSigmaE << "clSizeL0=" << part.clSizeL0 << "clSizeL1=" << part.clSizeL1 + << "clSizeL2=" << part.clSizeL2 << "clSizeL3=" << part.clSizeL3 << "clSizeL4=" << part.clSizeL4 << "clSizeL5=" + << part.clSizeL5 << "clSizeL6=" << part.clSizeL6 << "\n"; + } +} + +std::array PIDStudy::getTrackClusterSizes(const TrackITS& track) +{ + auto geom = o2::its::GeometryTGeo::Instance(); + std::array clusSizes = {-1, -1, -1, -1, -1, -1, -1}; + auto firstClus = track.getFirstClusterEntry(); + auto ncl = track.getNumberOfClusters(); + for (int icl = 0; icl < ncl; icl++) { + auto& clus = mClusters[mInputITSidxs[firstClus + icl]]; + auto& clSize = mClusterSizes[mInputITSidxs[firstClus + icl]]; + auto layer = geom->getLayer(clus.getSensorID()); + clusSizes[layer] = clSize; + } + return clusSizes; +} + +void PIDStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this param need to be queried only once + initOnceDone = true; + o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void PIDStudy::saveOutput() +{ + mDBGOut.reset(); + LOGP(info, "Stored histograms into {}", mOutName.c_str()); +} + +void PIDStudy::endOfStream(EndOfStreamContext& ec) +{ + // saveOutput(); +} + +void PIDStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + // o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); + return; + } +} + +float PIDStudy::computeNSigma(PID pid, TrackTPC& tpcTrack, float resolution) +{ + float nSigma = -999; + float bb = mPIDresponse.getExpectedSignal(tpcTrack, pid); + if (tpcTrack.getdEdx().dEdxTotTPC > 0) { + nSigma = (tpcTrack.getdEdx().dEdxTotTPC - bb) / (resolution * bb); + } + return nSigma; +} + +DataProcessorSpec getPIDStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestClusters(srcClustersMask, useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + return DataProcessorSpec{ + "its-pid-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, kineReader)}, + Options{}}; +} +} // namespace study +} // namespace its +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackCheck.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackCheck.cxx new file mode 100644 index 0000000000000..482f9bf1cc97d --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackCheck.cxx @@ -0,0 +1,1380 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITSStudies/TrackCheck.h" +#include "ITSStudies/ITSStudiesConfigParam.h" + +#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/MCTrack.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "CommonUtils/TreeStreamRedirector.h" + +#include "Framework/Task.h" +#include "Steer/MCKinematicsReader.h" +#include "ITSBase/GeometryTGeo.h" +#include "DetectorsBase/GRPGeomHelper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::its::study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; +using o2::steer::MCKinematicsReader; +class TrackCheckStudy final : public Task +{ + struct ParticleInfo { + int event; + int pdg; + float pt; + float eta; + float phi; + int mother; + int first; + float vx; + float vy; + float vz; + int unsigned short clusters = 0u; + unsigned char isReco = 0u; + unsigned char isFake = 0u; + bool isPrimary = false; + unsigned char storedStatus = 2; /// not stored = 2, fake = 1, good = 0 + const char* prodProcessName; + int prodProcess; + o2::its::TrackITS track; + }; + + public: + TrackCheckStudy(std::shared_ptr dr, + mask_t src, + bool useMC, + std::shared_ptr kineReader, + std::shared_ptr gr) : mDataRequest(dr), mTracksSrc(src), mKineReader(kineReader), mGGCCDBRequest(gr) + { + if (useMC) { + LOGP(info, "Read MCKine reader with {} sources", mKineReader->getNSources()); + } + } + + ~TrackCheckStudy() final = default; + void init(InitContext&) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void initialiseRun(o2::globaltracking::RecoContainer&); + void process(); + void setEfficiencyGraph(std::unique_ptr&, const char*, const char*, const int, const double, const double, const int, const double); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + std::string mOutFileName = "TrackCheckStudy.root"; + std::shared_ptr mKineReader; + GeometryTGeo* mGeometry; + + // Spans + gsl::span mTracksROFRecords; + gsl::span mTracks; + gsl::span mTracksMCLabels; + gsl::span mClusters; + gsl::span mInputITSidxs; + const o2::dataformats::MCLabelContainer* mClustersMCLCont; + + // Data + GTrackID::mask_t mTracksSrc{}; + std::shared_ptr mDataRequest; + std::vector>> mParticleInfo; // src/event/track + unsigned short mMask = 0x7f; + + // Utils + std::shared_ptr mGGCCDBRequest; + + // Histos + std::unique_ptr mGoodPt; + std::unique_ptr mGoodEta; + std::unique_ptr mGoodPtSec; + std::unique_ptr mGoodEtaSec; + std::unique_ptr mGoodChi2; + std::unique_ptr mFakePt; + std::unique_ptr mFakeEta; + std::unique_ptr mFakePtSec; + std::unique_ptr mFakeEtaSec; + std::unique_ptr mMultiFake; + std::unique_ptr mFakeChi2; + std::unique_ptr mClonePt; + std::unique_ptr mCloneEta; + + std::unique_ptr mDenominatorPt; + std::unique_ptr mDenominatorEta; + std::unique_ptr mDenominatorPtSec; + std::unique_ptr mDenominatorEtaSec; + + std::unique_ptr processvsZ; // TH2D with production process + std::unique_ptr processvsRad; + std::unique_ptr processvsRadOther; + std::unique_ptr processvsRadNotTracked; + std::unique_ptr processvsEtaNotTracked; + + std::unique_ptr mEffPt; // Eff vs Pt primary + std::unique_ptr mEffFakePt; + std::unique_ptr mEffClonesPt; + std::unique_ptr mEffEta; // Eff vs Eta primary + std::unique_ptr mEffFakeEta; + std::unique_ptr mEffClonesEta; + + std::unique_ptr mEffPtSec; // Eff vs Pt secondary + std::unique_ptr mEffFakePtSec; + std::unique_ptr mEffEtaSec; // Eff vs Eta secondary + std::unique_ptr mEffFakeEtaSec; + + std::unique_ptr mPtResolution; // Pt resolution for both primary and secondary + std::unique_ptr mPtResolution2D; + std::unique_ptr mPtResolutionSec; + std::unique_ptr mPtResolutionPrim; + std::unique_ptr g1; + + const char* ParticleName[7] = {"e^{-/+}", "#pi^{-/+}", "p", "^{2}H", "^{3}He", "_{#Lambda}^{3}H", "k^{+/-}"}; + const int PdgcodeClusterFake[7] = {11, 211, 2212, 1000010020, 100002030, 1010010030, 321}; + const char* name[3] = {"_{#Lambda}^{3}H", "#Lambda", "k^{0}_{s}"}; + const char* particleToanalize[4] = {"IperT", "Lambda", "k0s", "Tot"}; // [3]=Total of secondary particle + const int PDG[3] = {1010010030, 3122, 310}; + const char* ProcessName[50]; + int colorArr[4] = {kGreen, kRed, kBlue, kOrange}; + + std::vector> histLength, histLength1Fake, histLength2Fake, histLength3Fake, histLengthNoCl, histLength1FakeNoCl, histLength2FakeNoCl, histLength3FakeNoCl; // FakeCluster Study + std::vector stackLength, stackLength1Fake, stackLength2Fake, stackLength3Fake; + std::vector legends, legends1Fake, legends2Fake, legends3Fake; + std::vector> mClusterFake; + std::vector>> mGoodPts, mFakePts, mTotPts, mGoodEtas, mTotEtas, mFakeEtas; + std::vector>> mEffGoodPts, mEffFakePts, mEffGoodEtas, mEffFakeEtas; + std::vector> mGoodRad, mFakeRad, mTotRad, mGoodZ, mFakeZ, mTotZ; + std::vector> mEffGoodRad, mEffFakeRad, mEffGoodZ, mEffFakeZ; + // Canvas & decorations + std::unique_ptr mCanvasPt; + std::unique_ptr mCanvasPtSec; + std::unique_ptr mCanvasPt2; + std::unique_ptr mCanvasPt2fake; + std::unique_ptr mCanvasEta; + std::unique_ptr mCanvasEtaSec; + std::unique_ptr mCanvasRad; + std::unique_ptr mCanvasZ; + std::unique_ptr mCanvasRadD; + std::unique_ptr mCanvasZD; + std::unique_ptr mCanvasPtRes; + std::unique_ptr mCanvasPtRes2; + std::unique_ptr mCanvasPtRes3; + std::unique_ptr mCanvasPtRes4; + std::unique_ptr mLegendPt; + std::unique_ptr mLegendEta; + std::unique_ptr mLegendPtSec; + std::unique_ptr mLegendEtaSec; + std::unique_ptr mLegendPtRes; + std::unique_ptr mLegendPtRes2; + std::unique_ptr mLegendZ; + std::unique_ptr mLegendRad; + std::unique_ptr mLegendZD; + std::unique_ptr mLegendRadD; + std::vector Histo; + + float rLayer0 = 2.34; // middle radius + float rLayer1 = 3.15; + float rLayer2 = 3.93; + float rLayer3 = 19.605; + + double sigma[100]; + double sigmaerr[100]; + double meanPt[100]; + double aa[100]; + + // Debug output tree + std::unique_ptr mDBGOut; +}; + +void TrackCheckStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + + auto& pars = o2::its::study::ITSCheckTracksParamConfig::Instance(); + mOutFileName = pars.outFileName; + mMask = pars.trackLengthMask; + + std::vector xbins; + xbins.resize(pars.effHistBins + 1); + double a = std::log(pars.effPtCutHigh / pars.effPtCutLow) / pars.effHistBins; + for (int i{0}; i <= pars.effHistBins; i++) { + xbins[i] = pars.effPtCutLow * std::exp(i * a); + } + for (int yy = 0; yy < 50; yy++) { + ProcessName[yy] = " "; + } + mGoodPt = std::make_unique("goodPt", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mGoodEta = std::make_unique("goodEta", ";#eta;Number of tracks", 60, -3, 3); + mGoodPtSec = std::make_unique("goodPtSec", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mGoodEtaSec = std::make_unique("goodEtaSec", ";#eta;Number of tracks", 60, -3, 3); + mGoodChi2 = std::make_unique("goodChi2", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", 200, 0, 100); + + mFakePt = std::make_unique("fakePt", ";#it{p}_{T} (GeV/#it{c});Fak", pars.effHistBins, xbins.data()); + mFakeEta = std::make_unique("fakeEta", ";#eta;Number of tracks", 60, -3, 3); + mFakePtSec = std::make_unique("fakePtSec", ";#it{p}_{T} (GeV/#it{c});Fak", pars.effHistBins, xbins.data()); + mFakeEtaSec = std::make_unique("fakeEtaSec", ";#eta;Number of tracks", 60, -3, 3); + mFakeChi2 = std::make_unique("fakeChi2", ";#it{p}_{T} (GeV/#it{c});Fak", 200, 0, 100); + + mMultiFake = std::make_unique("multiFake", ";#it{p}_{T} (GeV/#it{c});Fak", pars.effHistBins, xbins.data()); + + mClonePt = std::make_unique("clonePt", ";#it{p}_{T} (GeV/#it{c});Clone", pars.effHistBins, xbins.data()); + mCloneEta = std::make_unique("cloneEta", ";#eta;Number of tracks", 60, -3, 3); + + mDenominatorPt = std::make_unique("denominatorPt", ";#it{p}_{T} (GeV/#it{c});Den", pars.effHistBins, xbins.data()); + mDenominatorEta = std::make_unique("denominatorEta", ";#eta;Number of tracks", 60, -3, 3); + mDenominatorPtSec = std::make_unique("denominatorPtSec", ";#it{p}_{T} (GeV/#it{c});Den", pars.effHistBins, xbins.data()); + mDenominatorEtaSec = std::make_unique("denominatorEtaSec", ";#eta;Number of tracks", 60, -3, 3); + + processvsZ = std::make_unique("Process", ";z_{SV} [cm]; production process", 100, -50, 50., 50, 0, 50); + processvsRad = std::make_unique("ProcessR", ";decay radius [cm]; production process", 100, 0, 25., 50, 0, 50); + processvsRadOther = std::make_unique("ProcessRO", ";decay radius [cm]; production process", 200, 0, 25., 50, 0, 50); + processvsRadNotTracked = std::make_unique("ProcessRNoT", ";decay radius [cm]; production process", 200, 0, 25., 50, 0, 50); + processvsEtaNotTracked = std::make_unique("ProcessENoT", ";#eta; production process", 60, -3, 3, 50, 0, 50); + + mGoodPts.resize(4); + mFakePts.resize(4); + mTotPts.resize(4); + mGoodEtas.resize(4); + mFakeEtas.resize(4); + mTotEtas.resize(4); + mGoodRad.resize(4); + mFakeRad.resize(4); + mTotRad.resize(4); + mGoodZ.resize(4); + mFakeZ.resize(4); + mTotZ.resize(4); + mClusterFake.resize(4); + for (int i = 0; i < 4; i++) { + mGoodPts[i].resize(4); + mFakePts[i].resize(4); + mTotPts[i].resize(4); + mGoodEtas[i].resize(4); + mFakeEtas[i].resize(4); + mTotEtas[i].resize(4); + } + for (int ii = 0; ii < 4; ii++) { + + mGoodRad[ii] = std::make_unique(Form("goodRad_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, 0., 20.); + mFakeRad[ii] = std::make_unique(Form("FakeRad_%s", particleToanalize[ii]), ";#eta;Number of tracks", 100, 0., 20.); + mTotRad[ii] = std::make_unique(Form("TotRad_%s", particleToanalize[ii]), ";#eta;Number of tracks", 100, 0., 20.); + + mGoodZ[ii] = std::make_unique(Form("goodZ_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, -50., 50.); + mFakeZ[ii] = std::make_unique(Form("FakeZ_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, -50., 50.); + mTotZ[ii] = std::make_unique(Form("TotZ_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, -50., 50.); + mClusterFake[ii] = std::make_unique(Form("Clusters_fake_%s", ParticleName[ii]), ";particle generating fake cluster; production process", 7, 0., 7., 50, 0, 50); + + mGoodRad[ii]->Sumw2(); + mFakeRad[ii]->Sumw2(); + mTotRad[ii]->Sumw2(); + mGoodZ[ii]->Sumw2(); + mFakeZ[ii]->Sumw2(); + mTotZ[ii]->Sumw2(); + + for (int yy = 0; yy < 4; yy++) { // divided by layer + mGoodPts[ii][yy] = std::make_unique(Form("goodPts_%s_%d", particleToanalize[ii], yy), ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mFakePts[ii][yy] = std::make_unique(Form("FakePts_%s_%d", particleToanalize[ii], yy), ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mTotPts[ii][yy] = std::make_unique(Form("TotPts_%s_%d", particleToanalize[ii], yy), ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + + mGoodEtas[ii][yy] = std::make_unique(Form("goodEtas_%s_%d", particleToanalize[ii], yy), ";#eta;Number of tracks", 60, -3, 3); + mFakeEtas[ii][yy] = std::make_unique(Form("FakeEtas_%s_%d", particleToanalize[ii], yy), ";#eta;Number of tracks", 60, -3, 3); + mTotEtas[ii][yy] = std::make_unique(Form("TotEtas_%s_%d", particleToanalize[ii], yy), ";#eta;Number of tracks", 60, -3, 3); + + mGoodPts[ii][yy]->Sumw2(); + mFakePts[ii][yy]->Sumw2(); + mTotPts[ii][yy]->Sumw2(); + mGoodEtas[ii][yy]->Sumw2(); + mFakeEtas[ii][yy]->Sumw2(); + mTotEtas[ii][yy]->Sumw2(); + } + } + + mPtResolution = std::make_unique("PtResolution", ";#it{p}_{T} ;Den", 100, -1, 1); + mPtResolutionSec = std::make_unique("PtResolutionSec", ";#it{p}_{T} ;Den", 100, -1, 1); + mPtResolutionPrim = std::make_unique("PtResolutionPrim", ";#it{p}_{T} ;Den", 100, -1, 1); + mPtResolution2D = std::make_unique("#it{p}_{T} Resolution vs #it{p}_{T}", ";#it{p}_{T} (GeV/#it{c});#Delta p_{T}/p_{T_{MC}", 100, 0, 10, 100, -1, 1); + + mPtResolution->Sumw2(); + mPtResolutionSec->Sumw2(); + mPtResolutionPrim->Sumw2(); + + mGoodPt->Sumw2(); + mGoodEta->Sumw2(); + mGoodPtSec->Sumw2(); + mGoodEtaSec->Sumw2(); + + mFakePt->Sumw2(); + mFakePtSec->Sumw2(); + mFakeEta->Sumw2(); + mMultiFake->Sumw2(); + mClonePt->Sumw2(); + mDenominatorPt->Sumw2(); + + histLength.resize(4); // fake clusters study + histLength1Fake.resize(4); + histLength2Fake.resize(4); + histLength3Fake.resize(4); + histLengthNoCl.resize(4); + histLength1FakeNoCl.resize(4); + histLength2FakeNoCl.resize(4); + histLength3FakeNoCl.resize(4); + stackLength.resize(4); + stackLength1Fake.resize(4); + stackLength2Fake.resize(4); + stackLength3Fake.resize(4); + for (int yy = 0; yy < 4; yy++) { + histLength[yy].resize(3); + histLength1Fake[yy].resize(3); + histLength2Fake[yy].resize(3); + histLength3Fake[yy].resize(3); + histLengthNoCl[yy].resize(3); + histLength1FakeNoCl[yy].resize(3); + histLength2FakeNoCl[yy].resize(3); + histLength3FakeNoCl[yy].resize(3); + } + legends.resize(4); + legends1Fake.resize(4); + legends2Fake.resize(4); + legends3Fake.resize(4); + + for (int iH{4}; iH < 8; ++iH) { + // check distributions on layers of fake clusters for tracks of different lengths. + // Different histograms if the correct cluster exist or not + for (int jj = 0; jj < 3; jj++) { + histLength[iH - 4][jj] = new TH1I(Form("trk_len_%d_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + histLengthNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLengthNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLengthNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength[iH - 4] = new THStack(Form("stack_trk_len_%d", iH), Form("trk_len=%d", iH)); + } + stackLength[iH - 4]->Add(histLength[iH - 4][jj]); + stackLength[iH - 4]->Add(histLengthNoCl[iH - 4][jj]); + + histLength1Fake[iH - 4][jj] = new TH1I(Form("trk_len_%d_1f_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength1Fake[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength1Fake[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + histLength1FakeNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_1f_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLength1FakeNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLength1FakeNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength1Fake[iH - 4] = new THStack(Form("stack_trk_len_%d_1f", iH), Form("trk_len=%d, 1 Fake", iH)); + } + stackLength1Fake[iH - 4]->Add(histLength1Fake[iH - 4][jj]); + stackLength1Fake[iH - 4]->Add(histLength1FakeNoCl[iH - 4][jj]); + + histLength2Fake[iH - 4][jj] = new TH1I(Form("trk_len_%d_2f_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength2Fake[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength2Fake[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + histLength2FakeNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_2f_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLength2FakeNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLength2FakeNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength2Fake[iH - 4] = new THStack(Form("stack_trk_len_%d_2f", iH), Form("trk_len=%d, 2 Fake", iH)); + } + stackLength2Fake[iH - 4]->Add(histLength2Fake[iH - 4][jj]); + stackLength2Fake[iH - 4]->Add(histLength2FakeNoCl[iH - 4][jj]); + + histLength3Fake[iH - 4][jj] = new TH1I(Form("trk_len_%d_3f_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength3Fake[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength3Fake[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + + histLength3FakeNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_3f_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLength3FakeNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLength3FakeNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength3Fake[iH - 4] = new THStack(Form("stack_trk_len_%d_3f", iH), Form("trk_len=%d, 3 Fake", iH)); + } + stackLength3Fake[iH - 4]->Add(histLength3Fake[iH - 4][jj]); + stackLength3Fake[iH - 4]->Add(histLength3FakeNoCl[iH - 4][jj]); + } + } +} + +void TrackCheckStudy::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + initialiseRun(recoData); + process(); +} + +void TrackCheckStudy::initialiseRun(o2::globaltracking::RecoContainer& recoData) +{ + mTracksROFRecords = recoData.getITSTracksROFRecords(); + mTracks = recoData.getITSTracks(); + mTracksMCLabels = recoData.getITSTracksMCLabels(); + mClusters = recoData.getITSClusters(); + mClustersMCLCont = recoData.getITSClustersMCLabels(); + mInputITSidxs = recoData.getITSTracksClusterRefs(); + + LOGP(info, "** Found in {} rofs:\n\t- {} clusters with {} labels\n\t- {} tracks with {} labels", + mTracksROFRecords.size(), mClusters.size(), mClustersMCLCont->getIndexedSize(), mTracks.size(), mTracksMCLabels.size()); + LOGP(info, "** Found {} sources from kinematic files", mKineReader->getNSources()); +} + +void TrackCheckStudy::process() +{ + LOGP(info, "** Filling particle table ... "); + mParticleInfo.resize(mKineReader->getNSources()); // sources + for (int iSource{0}; iSource < mKineReader->getNSources(); ++iSource) { + mParticleInfo[iSource].resize(mKineReader->getNEvents(iSource)); // events + for (int iEvent{0}; iEvent < mKineReader->getNEvents(iSource); ++iEvent) { + mParticleInfo[iSource][iEvent].resize(mKineReader->getTracks(iSource, iEvent).size()); // tracks + for (auto iPart{0}; iPart < mKineReader->getTracks(iEvent).size(); ++iPart) { + auto& part = mKineReader->getTracks(iSource, iEvent)[iPart]; + mParticleInfo[iSource][iEvent][iPart].event = iEvent; + mParticleInfo[iSource][iEvent][iPart].pdg = part.GetPdgCode(); + mParticleInfo[iSource][iEvent][iPart].pt = part.GetPt(); + mParticleInfo[iSource][iEvent][iPart].phi = part.GetPhi(); + mParticleInfo[iSource][iEvent][iPart].eta = part.GetEta(); + mParticleInfo[iSource][iEvent][iPart].vx = part.Vx(); + mParticleInfo[iSource][iEvent][iPart].vy = part.Vy(); + mParticleInfo[iSource][iEvent][iPart].vz = part.Vz(); + mParticleInfo[iSource][iEvent][iPart].isPrimary = part.isPrimary(); + mParticleInfo[iSource][iEvent][iPart].mother = part.getMotherTrackId(); + mParticleInfo[iSource][iEvent][iPart].prodProcessName = part.getProdProcessAsString(); + mParticleInfo[iSource][iEvent][iPart].prodProcess = part.getProcess(); + } + } + } + LOGP(info, "** Creating particle/clusters correspondance ... "); + for (auto iSource{0}; iSource < mParticleInfo.size(); ++iSource) { + for (auto iCluster{0}; iCluster < mClusters.size(); ++iCluster) { + auto labs = mClustersMCLCont->getLabels(iCluster); // ideally I can have more than one label per cluster + for (auto& lab : labs) { + if (!lab.isValid()) { + continue; // We want to skip channels related to noise, e.g. sID = 99: QED + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + auto& cluster = mClusters[iCluster]; + auto layer = mGeometry->getLayer(cluster.getSensorID()); + mParticleInfo[srcID][evID][trackID].clusters |= (1 << layer); + } + } + } + LOGP(info, "** Analysing tracks ... "); + int unaccounted{0}, good{0}, fakes{0}; + // ***secondary tracks*** + int nPartForSpec[4][4]; // total number [particle 0=IperT, 1=Lambda, 2=k, 3=Other][n layer] + int nPartGoodorFake[4][4][2]; // number of good or fake [particle 0=IperT, 1=Lambda, 2=k, 3=Other][n layer][good=1 fake=0] + for (int n = 0; n < 4; n++) { + for (int m = 0; m < 4; m++) { + nPartForSpec[n][m] = 0; + for (int h = 0; h < 2; h++) { + nPartGoodorFake[n][m][h] = 0; + } + } + } + int nlayer = 999; + int ngoodfake = 0; + int totsec = 0; + int totsecCont = 0; + + for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { + auto& lab = mTracksMCLabels[iTrack]; + if (!lab.isSet() || lab.isNoise()) { + unaccounted++; + continue; + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + + if (srcID == 99) { // skip QED + unaccounted++; + continue; + } + + mParticleInfo[srcID][evID][trackID].isReco += !fake; + mParticleInfo[srcID][evID][trackID].isFake += fake; + if (mTracks[iTrack].isBetter(mParticleInfo[srcID][evID][trackID].track, 1.e9)) { + mParticleInfo[srcID][evID][trackID].storedStatus = fake; + mParticleInfo[srcID][evID][trackID].track = mTracks[iTrack]; + } + fakes += fake; + good += !fake; + } + LOGP(info, "** Some statistics:"); + LOGP(info, "\t- Total number of tracks: {}", mTracks.size()); + LOGP(info, "\t- Total number of tracks not corresponding to particles: {} ({:.2f} %)", unaccounted, unaccounted * 100. / mTracks.size()); + LOGP(info, "\t- Total number of fakes: {} ({:.2f} %)", fakes, fakes * 100. / mTracks.size()); + LOGP(info, "\t- Total number of good: {} ({:.2f} %)", good, good * 100. / mTracks.size()); + + LOGP(info, "** Filling histograms ... "); + int evID = 0; + int trackID = 0; + int totP{0}, goodP{0}, fakeP{0}; + // Currently process only sourceID = 0, to be extended later if needed + for (auto& evInfo : mParticleInfo[0]) { + trackID = 0; + for (auto& part : evInfo) { + + if (strcmp(ProcessName[part.prodProcess], " ")) { + ProcessName[part.prodProcess] = part.prodProcessName; + } + if ((part.clusters & 0x7f) == mMask) { + // part.clusters != 0x3f && part.clusters != 0x3f << 1 && + // part.clusters != 0x1f && part.clusters != 0x1f << 1 && part.clusters != 0x1f << 2 && + // part.clusters != 0x0f && part.clusters != 0x0f << 1 && part.clusters != 0x0f << 2 && part.clusters != 0x0f << 3) { + // continue; + + if (part.isPrimary) { // **Primary particle** + totP++; + mDenominatorPt->Fill(part.pt); + mDenominatorEta->Fill(part.eta); + if (part.isReco) { + mGoodPt->Fill(part.pt); + mGoodEta->Fill(part.eta); + goodP++; + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + mClonePt->Fill(part.pt); + mCloneEta->Fill(part.eta); + } + } + } + if (part.isFake) { + mFakePt->Fill(part.pt); + mFakeEta->Fill(part.eta); + fakeP++; + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + mMultiFake->Fill(part.pt); + } + } + } + } + } + + // **Secondary particle** + nlayer = 999; + ngoodfake = 2; + if (!part.isPrimary) { + int TrackID, EvID, SrcID; + int pdgcode = mParticleInfo[0][evID][part.mother].pdg; + int idxPart = 999; + float rad = sqrt(pow(part.vx, 2) + pow(part.vy, 2)); + totsec++; + + if ((rad < rLayer0) && (part.clusters == 0x7f || part.clusters == 0x3f || part.clusters == 0x1f || part.clusters == 0x0f)) { // layer 0 + nlayer = 0; + } + if (rad < rLayer1 && rad > rLayer0 && (part.clusters == 0x1e || part.clusters == 0x3e || part.clusters == 0x7e)) { // layer 1 + nlayer = 1; + } + if (rad < rLayer2 && rad > rLayer1 && (part.clusters == 0x7c || part.clusters == 0x3c)) { // layer 2 + nlayer = 2; + } + if (rad < rLayer3 && rad > rLayer2 && part.clusters == 0x78) { // layer 3 + nlayer = 3; + } + if (nlayer == 0 || nlayer == 1 || nlayer == 2 || nlayer == 3) { // check if track is trackeable + + totsecCont++; + processvsZ->Fill(part.vz, part.prodProcess); + processvsRad->Fill(rad, part.prodProcess); + mDenominatorPtSec->Fill(part.pt); + mDenominatorEtaSec->Fill(part.eta); + mTotRad[3]->Fill(rad); + mTotZ[3]->Fill(part.vz); + mTotPts[nlayer][3]->Fill(part.pt); + mTotEtas[nlayer][3]->Fill(part.eta); + mTotPts[nlayer][3]->Fill(part.pt); + mTotEtas[nlayer][3]->Fill(part.eta); + if (pdgcode == PDG[0] || pdgcode == -1 * PDG[0]) { + idxPart = 0; // IperT + } + if (pdgcode == PDG[1] || pdgcode == -1 * PDG[1]) { + idxPart = 1; // Lambda + } + if (pdgcode == PDG[2] || pdgcode == -1 * PDG[2]) { + idxPart = 2; // K0s + } + if (part.isReco) { + ngoodfake = 1; + mGoodPts[3][nlayer]->Fill(part.pt); + mGoodEtas[3][nlayer]->Fill(part.eta); + mGoodPtSec->Fill(part.pt); + mGoodEtaSec->Fill(part.eta); + mGoodRad[3]->Fill(rad); + mGoodZ[3]->Fill(part.vz); + } + if (part.isFake) { + ngoodfake = 0; + mFakePts[3][nlayer]->Fill(part.pt); + mFakeEtas[3][nlayer]->Fill(part.eta); + mFakePtSec->Fill(part.pt); + mFakeEtaSec->Fill(part.eta); + mFakeRad[3]->Fill(rad); + mFakeZ[3]->Fill(part.vz); + } + if (idxPart < 3) // to change if the number of analysing particle changes + { + mTotRad[idxPart]->Fill(rad); + mTotZ[idxPart]->Fill(part.vz); + mTotPts[idxPart][nlayer]->Fill(part.pt); + mTotEtas[idxPart][nlayer]->Fill(part.eta); + if (part.isReco) { + mGoodRad[idxPart]->Fill(rad); + mGoodZ[idxPart]->Fill(part.vz); + mGoodPts[idxPart][nlayer]->Fill(part.pt); + mGoodEtas[idxPart][nlayer]->Fill(part.eta); + } + if (part.isFake) { + mFakeRad[idxPart]->Fill(rad); + mFakeZ[idxPart]->Fill(part.vz); + mFakePts[idxPart][nlayer]->Fill(part.pt); + mFakeEtas[idxPart][nlayer]->Fill(part.eta); + } + } + + if (pdgcode != 1010010030 && pdgcode != 3122 && pdgcode != 310 && pdgcode != -1010010030 && pdgcode != -310 && pdgcode != -3122) { + idxPart = 3; + processvsRadOther->Fill(rad, part.prodProcess); + } + + if (!part.isFake && !part.isReco) { + processvsEtaNotTracked->Fill(part.eta, part.prodProcess); + processvsRadNotTracked->Fill(rad, part.prodProcess); + } + if (ngoodfake == 1 || ngoodfake == 0) { + nPartGoodorFake[idxPart][nlayer][ngoodfake]++; + } + nPartForSpec[idxPart][nlayer]++; + + // Analysing fake clusters + int nCl{0}; + for (unsigned int bit{0}; bit < sizeof(part.clusters) * 8; ++bit) { + nCl += bool(part.clusters & (1 << bit)); + } + if (nCl < 3) { + continue; + } + if (idxPart < 3) { + auto& track = part.track; + auto len = track.getNClusters(); + int nclu = track.getNumberOfClusters(); + int firstclu = track.getFirstClusterEntry(); + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (track.hasHitOnLayer(iLayer)) { + if (track.isFakeOnLayer(iLayer)) { + // Reco track has fake cluster + if (part.clusters & (0x1 << iLayer)) { // Correct cluster exists + histLength[len - 4][idxPart]->Fill(iLayer); + if (track.getNFakeClusters() == 1) { + histLength1Fake[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 2) { + histLength2Fake[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 3) { + histLength3Fake[len - 4][idxPart]->Fill(iLayer); + } + } else { + + histLengthNoCl[len - 4][idxPart]->Fill(iLayer); + if (track.getNFakeClusters() == 1) { + histLength1FakeNoCl[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 2) { + histLength2FakeNoCl[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 3) { + histLength3FakeNoCl[len - 4][idxPart]->Fill(iLayer); + } + } + auto labs = mClustersMCLCont->getLabels(mInputITSidxs[firstclu - 1 - iLayer + track.getFirstClusterLayer() + nclu]); + + for (auto& lab : labs) { + if (!lab.isValid()) { + continue; // We want to skip channels related to noise, e.g. sID = 99: QED + } + + bool fakec; + lab.get(TrackID, EvID, SrcID, fakec); + double intHisto = 0; + for (int hg = 0; hg < 7; hg++) { + if (mParticleInfo[SrcID][EvID][TrackID].pdg == PdgcodeClusterFake[hg] || mParticleInfo[SrcID][EvID][TrackID].pdg == -1 * (PdgcodeClusterFake[hg])) { + intHisto = hg + 0.5; + } + } + if (idxPart < 3) { + mClusterFake[idxPart]->Fill(intHisto, mParticleInfo[SrcID][EvID][TrackID].prodProcess); + } + } + } + } + } + } + } + nlayer = 999; + } + trackID++; + } + evID++; + } + + int totgood{0}, totfake{0}, totI{0}, totL{0}, totK{0}, totO{0}; + for (int xx = 0; xx < 4; xx++) { + for (int yy = 0; yy < 4; yy++) { + totgood = totgood + nPartGoodorFake[xx][yy][1]; + totfake = totfake + nPartGoodorFake[xx][yy][0]; + if (xx == 0) { + totI = totI + nPartForSpec[0][yy]; + } + if (xx == 1) { + totL = totL + nPartForSpec[1][yy]; + } + if (xx == 2) { + totK = totK + nPartForSpec[2][yy]; + } + if (xx == 3) { + totO = totO + nPartForSpec[3][yy]; + } + } + } + LOGP(info, "number of primary tracks: {}, good:{}, fake:{}", totP, goodP, fakeP); + int goodI = nPartGoodorFake[0][0][1] + nPartGoodorFake[0][1][1] + nPartGoodorFake[0][2][1] + nPartGoodorFake[0][3][1]; + int goodL = nPartGoodorFake[1][0][1] + nPartGoodorFake[1][1][1] + nPartGoodorFake[1][2][1] + nPartGoodorFake[1][3][1]; + int goodK = nPartGoodorFake[2][0][1] + nPartGoodorFake[2][1][1] + nPartGoodorFake[2][2][1] + nPartGoodorFake[2][3][1]; + int fakeI = nPartGoodorFake[0][0][0] + nPartGoodorFake[0][1][0] + nPartGoodorFake[0][2][0] + nPartGoodorFake[0][3][0]; + int fakeL = nPartGoodorFake[1][0][0] + nPartGoodorFake[1][1][0] + nPartGoodorFake[1][2][0] + nPartGoodorFake[1][3][0]; + int fakeK = nPartGoodorFake[2][0][0] + nPartGoodorFake[2][1][0] + nPartGoodorFake[2][2][0] + nPartGoodorFake[2][3][0]; + LOGP(info, "** Some statistics on secondary tracks:"); + + LOGP(info, "\t- Total number of secondary tracks: {}", totsec); + LOGP(info, "\t- Total number of secondary trackeable tracks : {}", totsecCont); + LOGP(info, "\t- Total number of secondary trackeable tracks good: {}, fake: {}", totgood, totfake); + LOGP(info, "\t- Total number of secondary trackeable tracks from IperT: {} = {} %, Good={} % , fake={} %", totI, 100 * totI / totsecCont, 100 * goodI / totI, 100 * fakeI / totI); + LOGP(info, "\t- Total number of secondary trackeable tracks from Lam: {} = {} %, Good={} % , fake={} %", totL, 100 * totL / totsecCont, 100 * goodL / totL, 100 * fakeL / totL); + LOGP(info, "\t- Total number of secondary trackeable tracks from k: {} = {} %, Good={} % , fake={} %", totK, 100 * totK / totsecCont, 100 * goodK / totK, 100 * fakeK / totK); + LOGP(info, "\t- Total number of secondary trackeable tracks from Other: {} = {} %", totO, 100 * totO / totsecCont); + + LOGP(info, "** Computing efficiencies ..."); + + mEffPt = std::make_unique(*mGoodPt, *mDenominatorPt); + mEffFakePt = std::make_unique(*mFakePt, *mDenominatorPt); + mEffClonesPt = std::make_unique(*mClonePt, *mDenominatorPt); + + mEffEta = std::make_unique(*mGoodEta, *mDenominatorEta); + mEffFakeEta = std::make_unique(*mFakeEta, *mDenominatorEta); + mEffClonesEta = std::make_unique(*mCloneEta, *mDenominatorEta); + + mEffPtSec = std::make_unique(*mGoodPtSec, *mDenominatorPtSec); + mEffFakePtSec = std::make_unique(*mFakePtSec, *mDenominatorPtSec); + + mEffEtaSec = std::make_unique(*mGoodEtaSec, *mDenominatorEtaSec); + mEffFakeEtaSec = std::make_unique(*mFakeEtaSec, *mDenominatorEtaSec); + + for (int ii = 0; ii < 4; ii++) { + for (int yy = 0; yy < 4; yy++) { + mEffGoodPts[ii][yy] = std::make_unique(*mGoodPts[ii][yy], *mTotPts[ii][yy]); + mEffFakePts[ii][yy] = std::make_unique(*mFakePts[ii][yy], *mTotPts[ii][yy]); + mEffGoodEtas[ii][yy] = std::make_unique(*mGoodEtas[ii][yy], *mTotEtas[ii][yy]); + mEffFakeEtas[ii][yy] = std::make_unique(*mFakeEtas[ii][yy], *mTotEtas[ii][yy]); + } + mEffGoodRad[ii] = std::make_unique(*mGoodRad[ii], *mTotRad[ii]); + mEffFakeRad[ii] = std::make_unique(*mFakeRad[ii], *mTotRad[ii]); + mEffGoodZ[ii] = std::make_unique(*mGoodZ[ii], *mTotZ[ii]); + mEffFakeZ[ii] = std::make_unique(*mFakeZ[ii], *mTotZ[ii]); + } + + LOGP(info, "** Analysing pT resolution..."); + for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { + auto& lab = mTracksMCLabels[iTrack]; + if (!lab.isSet() || lab.isNoise()) { + continue; + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + if (srcID == 99) { + continue; // skip QED + } + mPtResolution->Fill((mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + mPtResolution2D->Fill(mParticleInfo[srcID][evID][trackID].pt, (mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + if (!mParticleInfo[srcID][evID][trackID].isPrimary) { + mPtResolutionSec->Fill((mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + } + mPtResolutionPrim->Fill((mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + } + + for (int yy = 0; yy < 100; yy++) { + aa[yy] = 0.; + sigma[yy] = 0.; + sigmaerr[yy] = 0.; + meanPt[yy] = 0.; + } + + for (int yy = 0; yy < 100; yy++) { + TH1D* projh2X = mPtResolution2D->ProjectionY("projh2X", yy, yy + 1, ""); + TF1* f1 = new TF1("f1", "gaus", -0.2, 0.2); + projh2X->Fit("f1"); + if (f1->GetParameter(2) > 0. && f1->GetParameter(2) < 1. && f1->GetParameter(1) < 1.) { + sigma[yy] = f1->GetParameter(2); + sigmaerr[yy] = f1->GetParError(2); + meanPt[yy] = ((8. / 100.) * yy + (8. / 100.) * (yy + 1)) / 2; + aa[yy] = 0.0125; + } + } +} + +void TrackCheckStudy::setEfficiencyGraph(std::unique_ptr& eff, const char* name, const char* title, const int color, const double alpha = 1, const double linew = 2, const int markerStyle = kFullCircle, const double markersize = 1.7) +{ + eff->SetName(name); + eff->SetTitle(title); + eff->SetLineColor(color); + eff->SetLineColorAlpha(color, alpha); + eff->SetMarkerColor(color); + eff->SetMarkerColorAlpha(color, alpha); + eff->SetLineWidth(linew); + eff->SetMarkerStyle(markerStyle); + eff->SetMarkerSize(markersize); + eff->SetDirectory(gDirectory); +} + +void TrackCheckStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + static bool initOnceDone = false; + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + mGeometry = GeometryTGeo::Instance(); + mGeometry->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void TrackCheckStudy::endOfStream(EndOfStreamContext& ec) +{ + TFile fout(mOutFileName.c_str(), "recreate"); + + setEfficiencyGraph(mEffPt, "Good_pt", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kAzure + 4, 0.65); + fout.WriteTObject(mEffPt.get()); + + setEfficiencyGraph(mEffFakePt, "Fake_pt", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kRed, 0.65); + fout.WriteTObject(mEffFakePt.get()); + + setEfficiencyGraph(mEffPtSec, "Good_ptSec", ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle", kOrange + 7); + fout.WriteTObject(mEffPtSec.get()); + + setEfficiencyGraph(mEffFakePtSec, "Fake_ptSec", ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle", kGray + 2); + fout.WriteTObject(mEffFakePtSec.get()); + + setEfficiencyGraph(mEffClonesPt, "Clone_pt", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kGreen + 2, 0.65); + fout.WriteTObject(mEffClonesPt.get()); + + setEfficiencyGraph(mEffEta, "Good_eta", ";#eta;efficiency primary particle", kAzure + 4, 0.65); + fout.WriteTObject(mEffEta.get()); + + setEfficiencyGraph(mEffFakeEta, "Fake_eta", ";#eta;efficiency primary particle", kRed + 1, 0.65); + fout.WriteTObject(mEffFakeEta.get()); + + setEfficiencyGraph(mEffEtaSec, "Good_etaSec", ";#eta;efficiency secondary particle", kOrange + 7); + fout.WriteTObject(mEffEtaSec.get()); + + setEfficiencyGraph(mEffFakeEtaSec, "Fake_etaSec", ";#eta;efficiency secondary particle", kGray + 2); + fout.WriteTObject(mEffFakeEtaSec.get()); + + setEfficiencyGraph(mEffClonesEta, "Clone_eta", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kGreen + 2, 0.65); + fout.WriteTObject(mEffClonesEta.get()); + + for (int aa = 0; aa < 4; aa++) { + setEfficiencyGraph(mEffGoodRad[aa], Form("Good_Rad_%s", particleToanalize[aa]), ";Radius [cm];efficiency secondary particle", colorArr[aa]); + fout.WriteTObject(mEffGoodRad[aa].get()); + + setEfficiencyGraph(mEffGoodRad[aa], Form("Fake_Rad_%s", particleToanalize[aa]), ";Radius [cm];efficiency secondary particle", colorArr[aa] - 9); + fout.WriteTObject(mEffGoodRad[aa].get()); + + setEfficiencyGraph(mEffGoodZ[aa], Form("Good_Z_%s", particleToanalize[aa]), ";Z_{sv} [cm];efficiency secondary particle", colorArr[aa]); + fout.WriteTObject(mEffGoodZ[aa].get()); + + setEfficiencyGraph(mEffGoodZ[aa], Form("Fake_Z_%s", particleToanalize[aa]), ";Z_{sv} [cm];efficiency secondary particle", colorArr[aa] - 9); + fout.WriteTObject(mEffGoodZ[aa].get()); + + for (int bb = 0; bb < 4; bb++) { + setEfficiencyGraph(mEffGoodPts[aa][bb], Form("EffPtGood_%sl%d", particleToanalize[aa], bb), Form("Good Sec Tracks_%s, L%d" + ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + setEfficiencyGraph(mEffFakePts[aa][bb], Form("EffPtFake_%sl%d", particleToanalize[aa], bb), Form("Fake Sec Tracks_%s, L%d" + ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + setEfficiencyGraph(mEffGoodEtas[aa][bb], Form("EffEtaGood_%sl%d", particleToanalize[aa], bb), Form("Good Sec Tracks_%s, L%d" + ";#eta ;efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + setEfficiencyGraph(mEffFakeEtas[aa][bb], Form("EffEtaFake_%sl%d", particleToanalize[aa], bb), Form("Fake Sec Tracks_%s, L%d" + ";#eta ;efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + + fout.WriteTObject(mEffGoodPts[aa][bb].get()); + fout.WriteTObject(mEffFakePts[aa][bb].get()); + fout.WriteTObject(mEffGoodEtas[aa][bb].get()); + fout.WriteTObject(mEffFakeEtas[aa][bb].get()); + } + for (int i = 0; i < 3; i++) { + fout.WriteTObject(histLength[aa][i], Form("trk_len_%d_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength1Fake[aa][i], Form("trk_len_%d_1f_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength2Fake[aa][i], Form("trk_len_%d_2f_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength3Fake[aa][i], Form("trk_len_%d_3f_%s", 4 + aa, name[i])); + fout.WriteTObject(histLengthNoCl[aa][i], Form("trk_len_%d_nocl_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength1FakeNoCl[aa][i], Form("trk_len_%d_1f_nocl_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength2FakeNoCl[aa][i], Form("trk_len_%d_2f_nocl_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength3FakeNoCl[aa][i], Form("trk_len_%d_3f_nocl_%s", 4 + aa, name[i])); + } + } + + for (int j = 0; j < 4; j++) { + for (int i = 1; i <= 7; i++) { + mClusterFake[j]->GetXaxis()->SetBinLabel(i, ParticleName[i - 1]); + + for (int i = 1; i <= 50; i++) { + mClusterFake[j]->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + if (j == 0) { + processvsZ->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsRad->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsRadOther->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsRadNotTracked->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsEtaNotTracked->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + } + } + fout.WriteTObject(mClusterFake[j].get()); + } + } + fout.WriteTObject(processvsZ.get()); + fout.WriteTObject(processvsRad.get()); + fout.WriteTObject(processvsRadOther.get()); + fout.WriteTObject(processvsRadNotTracked.get()); + fout.WriteTObject(processvsEtaNotTracked.get()); + + // Paint the histograms + // todo: delegate to a dedicated helper + gStyle->SetTitleSize(0.035, "xy"); + gStyle->SetLabelSize(0.035, "xy"); + gStyle->SetPadRightMargin(0.035); + gStyle->SetPadTopMargin(0.035); + gStyle->SetPadLeftMargin(0.19); + gStyle->SetPadBottomMargin(0.17); + gStyle->SetTitleOffset(1.4, "x"); + gStyle->SetTitleOffset(1.1, "y"); + gStyle->SetPadTickX(1); + gStyle->SetPadTickY(1); + gStyle->SetGridStyle(3); + gStyle->SetGridWidth(1); + + mCanvasPt = std::make_unique("cPt", "cPt", 1600, 1200); + mCanvasPt->cd(); + mCanvasPt->SetLogx(); + mCanvasPt->SetGrid(); + mEffPt->Draw("pz"); + mEffFakePt->Draw("pz same"); + mEffClonesPt->Draw("pz same"); + mLegendPt = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendPt->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendPt->AddEntry("Good_pt", "good (100% cluster purity)", "lep"); + mLegendPt->AddEntry("Fake_pt", "fake", "lep"); + mLegendPt->AddEntry("Clone_pt", "clone", "lep"); + mLegendPt->Draw(); + mCanvasPt->SaveAs("eff_pt.png"); + + mCanvasPtSec = std::make_unique("cPtSec", "cPtSec", 1600, 1200); + mCanvasPtSec->cd(); + mCanvasPtSec->SetLogx(); + mCanvasPtSec->SetGrid(); + mEffPtSec->Draw("pz"); + mEffFakePtSec->Draw("pz same"); + mLegendPtSec = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendPtSec->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendPtSec->AddEntry("Good_ptSec", "good (100% cluster purity)", "lep"); + mLegendPtSec->AddEntry("Fake_tSec", "fake", "lep"); + mLegendPtSec->Draw(); + mCanvasPtSec->SaveAs("eff_ptSec.png"); + + mCanvasEta = std::make_unique("cEta", "cEta", 1600, 1200); + mCanvasEta->cd(); + mCanvasEta->SetGrid(); + mEffEta->Draw("pz"); + mEffFakeEta->Draw("pz same"); + mEffClonesEta->Draw("pz same"); + mLegendEta = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendEta->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendEta->AddEntry("Good_eta", "good (100% cluster purity)", "lep"); + mLegendEta->AddEntry("Fake_eta", "fake", "lep"); + mLegendEta->AddEntry("Clone_eta", "clone", "lep"); + mLegendEta->Draw(); + mCanvasEta->SaveAs("eff_eta.png"); + + mCanvasEtaSec = std::make_unique("cEtaSec", "cEtaSec", 1600, 1200); + mCanvasEtaSec->cd(); + mCanvasEtaSec->SetGrid(); + mEffEtaSec->Draw("pz"); + mEffFakeEtaSec->Draw("pz same"); + mLegendEtaSec = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendEtaSec->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendEtaSec->AddEntry("Good_etaSec", "good (100% cluster purity)", "lep"); + mLegendEtaSec->AddEntry("Fake_etaSec", "fake", "lep"); + mLegendEtaSec->Draw(); + mCanvasEtaSec->SaveAs("eff_EtaSec.png"); + + mCanvasRad = std::make_unique("cRad", "cRad", 1600, 1200); + mCanvasRad->cd(); + mCanvasRad->SetGrid(); + mEffGoodRad[3]->Draw("pz"); + mEffFakeRad[3]->Draw("pz same"); + mCanvasRad->SetLogy(); + mLegendRad = std::make_unique(0.8, 0.4, 0.95, 0.6); + mLegendRad->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + mLegendRad->AddEntry(Form("Good_Rad_%s", particleToanalize[3]), "good", "lep"); + mLegendRad->AddEntry(Form("Fake_Rad_%s", particleToanalize[3]), "fake", "lep"); + mLegendRad->Draw(); + mCanvasRad->SaveAs("eff_rad_sec.png"); + + mCanvasZ = std::make_unique("cZ", "cZ", 1600, 1200); + mCanvasZ->cd(); + mCanvasZ->SetGrid(); + mCanvasZ->SetLogy(); + mEffGoodZ[3]->Draw("pz"); + mEffFakeZ[3]->Draw("pz same"); + mCanvasZ->SetLogy(); + mLegendZ = std::make_unique(0.8, 0.4, 0.95, 0.6); + mLegendZ->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + mLegendZ->AddEntry(Form("Good_Z_%s", particleToanalize[3]), "good", "lep"); + mLegendZ->AddEntry(Form("Fake_Z_%s", particleToanalize[3]), "fake", "lep"); + mLegendZ->Draw(); + mCanvasZ->SaveAs("eff_Z_sec.png"); + ; + + mCanvasRadD = std::make_unique("cRadD", "cRadD", 1600, 1200); + mCanvasRadD->cd(); + mCanvasRadD->SetGrid(); + mCanvasRadD->SetLogy(); + mLegendRadD = std::make_unique(0.8, 0.64, 0.95, 0.8); + mLegendRadD->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + for (int i = 0; i < 3; i++) { + if (i == 0) { + mEffGoodRad[i]->Draw("pz"); + } else { + mEffGoodRad[i]->Draw("pz same"); + mEffFakeRad[i]->Draw("pz same"); + mLegendRadD->AddEntry(Form("Good_Rad%s", particleToanalize[i]), Form("%s_good", name[i]), "lep"); + mLegendRadD->AddEntry(Form("Fake_Rad%s", particleToanalize[i]), Form("%s_fake", name[i]), "lep"); + } + } + mLegendRadD->Draw(); + mCanvasRadD->SaveAs("eff_RadD_sec.png"); + + mCanvasZD = std::make_unique("cZD", "cZD", 1600, 1200); + mCanvasZD->cd(); + mCanvasZD->SetGrid(); + mCanvasZD->SetLogy(); + mLegendZD = std::make_unique(0.8, 0.64, 0.95, 0.8); + mLegendZD->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + for (int i = 0; i < 3; i++) { + if (i == 0) { + mEffGoodZ[i]->Draw("pz"); + } else { + mEffGoodZ[i]->Draw("pz same"); + mEffFakeZ[i]->Draw("pz same"); + mLegendZD->AddEntry(Form("Good_Z%s", particleToanalize[i]), Form("%s_good", name[i]), "lep"); + mLegendZD->AddEntry(Form("Fake_Z%s", particleToanalize[i]), Form("%s_fake", name[i]), "lep"); + } + } + mLegendZD->Draw(); + mCanvasZD->SaveAs("eff_ZD_sec.png"); + + mPtResolution->SetName("#it{p}_{T} resolution"); + mPtResolution->SetTitle(";#Delta p_{T}/p_{T_{MC}} ;Entries"); + mPtResolution->SetFillColor(kAzure + 4); + mPtResolutionPrim->SetFillColor(kRed); + mPtResolutionSec->SetFillColor(kOrange); + mPtResolutionPrim->SetTitle(";#Delta p_{T}/p_{T_{MC}} ;Entries"); + mPtResolutionSec->SetTitle(";#Delta #it{p}_{T}/#it{p}_{T_{MC}} ;Entries"); + mPtResolution2D->SetTitle(";#it{p}_{T_{MC}} [GeV];#Delta #it{p}_{T}/#it{p}_{T_{MC}}"); + + fout.WriteTObject(mPtResolution.get()); + fout.WriteTObject(mPtResolutionPrim.get()); + fout.WriteTObject(mPtResolutionSec.get()); + fout.WriteTObject(mPtResolution2D.get()); + + mCanvasPtRes = std::make_unique("cPtr", "cPtr", 1600, 1200); + mCanvasPtRes->cd(); + mPtResolution->Draw("HIST"); + mLegendPtRes = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendPtRes->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendPtRes->AddEntry("mPtResolution", "All events", "lep"); + mLegendPtRes->Draw(); + mCanvasPtRes->SaveAs("ptRes.png"); + + mCanvasPtRes2 = std::make_unique("cPtr2", "cPtr2", 1600, 1200); + mCanvasPtRes2->cd(); + mPtResolution2D->Draw(); + mCanvasPtRes2->SaveAs("ptRes2.png"); + + mCanvasPtRes3 = std::make_unique("cPtr3", "cPtr3", 1600, 1200); + mCanvasPtRes3->cd(); + + auto* g1 = new TGraphErrors(100, meanPt, sigma, aa, sigmaerr); + g1->SetMarkerStyle(8); + g1->SetMarkerColor(kGreen); + g1->GetXaxis()->SetTitle(" #it{p}_{T} [GeV]"); + g1->GetYaxis()->SetTitle("#sigma #Delta #it{p}_{T}/#it{p}_{T_{MC}}"); + g1->GetYaxis()->SetLimits(0, 1); + g1->GetXaxis()->SetLimits(0, 10.); + g1->Draw("AP"); + g1->GetYaxis()->SetRangeUser(0, 1); + g1->GetXaxis()->SetRangeUser(0, 10.); + mCanvasPtRes3->SaveAs("ptRes3.png"); + + mCanvasPtRes4 = std::make_unique("cPt4", "cPt4", 1600, 1200); + mCanvasPtRes4->cd(); + mPtResolutionPrim->SetName("mPtResolutionPrim"); + mPtResolutionSec->SetName("mPtResolutionSec"); + mPtResolutionPrim->Draw("same hist"); + mPtResolutionSec->Draw("same hist"); + mLegendPtRes2 = std::make_unique(0.19, 0.8, 0.40, 0.96); + + mLegendPtRes2->SetHeader(Form("%zu events PP", mKineReader->getNEvents(0)), "C"); + mLegendPtRes2->AddEntry("mPtResolutionPrim", "Primary events", "f"); + mLegendPtRes2->AddEntry("mPtResolutionSec", "Secondary events", "f"); + mLegendPtRes2->Draw("same"); + mLegendPtRes2->SaveAs("ptRes4.png"); + + auto canvas = new TCanvas("fc_canvas", "Fake clusters", 1600, 1000); + canvas->Divide(4, 2); + for (int iH{0}; iH < 4; ++iH) { + canvas->cd(iH + 1); + stackLength[iH]->Draw(); + stackLength[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + for (int iH{0}; iH < 4; ++iH) { + canvas->cd(iH + 5); + stackLength1Fake[iH]->Draw(); + stackLength1Fake[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + + canvas->SaveAs("fakeClusters2.png", "recreate"); + + auto canvas2 = new TCanvas("fc_canvas2", "Fake clusters", 1600, 1000); + canvas2->Divide(4, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvas2->cd(iH + 1); + stackLength2Fake[iH]->Draw(); + stackLength2Fake[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + for (int iH{0}; iH < 4; ++iH) { + canvas2->cd(iH + 5); + stackLength3Fake[iH]->Draw(); + stackLength3Fake[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + canvas2->SaveAs("fakeClusters3.png", "recreate"); + + auto canvasPtfake = new TCanvas("canvasPtfake", "Fake pt", 1600, 1000); + canvasPtfake->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasPtfake->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasPtfake->cd(iH + 1); + } + if (v == 0) { + mEffFakePts[v][iH]->Draw(); + } else { + mEffFakePts[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#it{p}_{T}, Fake Tracks, layer %d", iH)); + gPad->SetName(Form("#it{p}_{T}, Fake Tracks, layer %d", iH)); + } + canvasPtfake->SaveAs("PtforPartFake.png", "recreate"); + + auto canvasPtGood = new TCanvas("canvasPtGood", "Good pt", 1600, 1000); + canvasPtGood->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasPtGood->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasPtGood->cd(iH + 1); + } + if (v == 0) { + mEffGoodPts[v][iH]->Draw(); + } else { + mEffGoodPts[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#it{p}_{T}, Good Tracks, layer %d", iH)); + gPad->SetName(Form("#it{p}_{T}, Good Tracks, layer %d", iH)); + } + + auto canvasEtafake = new TCanvas("canvasEtafake", "Fake Eta", 1600, 1000); + canvasEtafake->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasEtafake->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasEtafake->cd(iH + 1); + } + if (v == 0) { + mEffFakeEtas[v][iH]->Draw(); + } else { + mEffFakeEtas[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#eta, Fake Tracks, layer %d", iH)); + gPad->SetName(Form("#eta, Fake Tracks, layer %d", iH)); + } + auto canvasEtaGood = new TCanvas("canvasEtaGood", "Good Eta", 1600, 1000); + canvasEtaGood->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasEtaGood->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasEtaGood->cd(iH + 1); + } + if (v == 0) { + mEffGoodEtas[v][iH]->Draw(); + } else { + mEffGoodEtas[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#eta, Good Tracks, layer %d", iH)); + gPad->SetName(Form("#eta, Good Tracks, layer %d", iH)); + } + + auto canvasI = new TCanvas("canvasI", "canvasI", 1600, 1000); + canvasI->cd(); + mClusterFake[0]->Draw("COLZ"); + canvasI->SaveAs("Iper2D.png", "recreate"); + + auto canvasL = new TCanvas("canvasL", "canvasL", 1600, 1000); + canvasL->cd(); + mClusterFake[1]->Draw("COLZ"); + canvasL->SaveAs("Lam2D.png", "recreate"); + + auto canvasK = new TCanvas("canvasK", "canvasK", 1600, 1000); + canvasK->cd(); + mClusterFake[2]->Draw("COLZ"); + canvasK->SaveAs("K2D.png", "recreate"); + + auto canvasZProd = new TCanvas("canvasZProd", "canvasZProd", 1600, 1000); + canvasZProd->cd(); + processvsZ->Draw("COLZ"); + canvasZProd->SaveAs("prodvsZ.png", "recreate"); + auto canvasRadProd = new TCanvas("canvasRadProd", "canvasRadProd", 1600, 1000); + canvasRadProd->cd(); + processvsRad->Draw("COLZ"); + canvasRadProd->SaveAs("prodvsRad.png", "recreate"); + auto canvasRadProdO = new TCanvas("canvasRadProdO", "canvasRadProdO", 1600, 1000); + canvasRadProdO->cd(); + processvsRadOther->Draw("COLZ"); + canvasRadProdO->SaveAs("prodvsRadO.png", "recreate"); + auto canvasRadProNOTr = new TCanvas("canvasRadProNOTr", "canvasRadProNOTr", 1600, 1000); + canvasRadProNOTr->cd(); + processvsRadNotTracked->Draw("COLZ"); + canvasRadProNOTr->SaveAs("prodvsRadNoTr.png", "recreate"); + auto canvasEtaProNOTr = new TCanvas("canvasEtaProNOTr", "canvasEtaProNOTr", 1600, 1000); + canvasEtaProNOTr->cd(); + processvsEtaNotTracked->Draw("COLZ"); + canvasEtaProNOTr->SaveAs("prodvsEtaNoTr.png", "recreate"); + + fout.cd(); + mCanvasPt->Write(); + mCanvasEta->Write(); + mCanvasPtSec->Write(); + mCanvasEtaSec->Write(); + mCanvasPtRes->Write(); + mCanvasPtRes2->Write(); + mCanvasPtRes3->Write(); + mCanvasPtRes4->Write(); + mCanvasRad->Write(); + mCanvasZ->Write(); + mCanvasRadD->Write(); + mCanvasZD->Write(); + canvas->Write(); + canvas2->Write(); + canvasPtfake->Write(); + canvasI->Write(); + canvasL->Write(); + canvasK->Write(); + fout.Close(); +} + +void TrackCheckStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ +} + +DataProcessorSpec getTrackCheckStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestClusters(srcClustersMask, useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + + return DataProcessorSpec{ + "its-study-check-tracks", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, srcTracksMask, useMC, kineReader, ggRequest)}, + Options{}}; +} + +} // namespace o2::its::study diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackExtension.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackExtension.cxx new file mode 100644 index 0000000000000..6826d441cc815 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackExtension.cxx @@ -0,0 +1,655 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/TreeStreamRedirector.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DetectorsBase/Propagator.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "ITSStudies/Helpers.h" +#include "ITSStudies/TrackExtension.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCTrack.h" +#include "Steer/MCKinematicsReader.h" +#include "ReconstructionDataFormats/Vertex.h" +#include "ReconstructionDataFormats/DCA.h" + +#include + +#include "TFile.h" +#include "TH1D.h" +#include "TH2D.h" +#include "TEfficiency.h" + +namespace o2::its::study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; +using o2::steer::MCKinematicsReader; +class TrackExtensionStudy final : public Task +{ + struct ParticleInfo { + float eventX; + float eventY; + float eventZ; + int pdg; + float pt; + float eta; + float phi; + int mother; + int first; + float vx; + float vy; + float vz; + uint8_t clusters = 0u; + uint8_t fakeClusters = 0u; + uint8_t isReco = 0u; + uint8_t isFake = 0u; + bool isPrimary = false; + unsigned char storedStatus = 2; /// not stored = 2, fake = 1, good = 0 + int prodProcess; + o2::its::TrackITS track; + MCTrack mcTrack; + }; + + public: + TrackExtensionStudy(std::shared_ptr dr, + mask_t src, + std::shared_ptr kineReader, + std::shared_ptr gr) : mDataRequest(dr), mTracksSrc(src), mKineReader(kineReader), mGGCCDBRequest(gr) + { + LOGP(info, "Read MCKine reader with {} sources", mKineReader->getNSources()); + } + + ~TrackExtensionStudy() final = default; + void init(InitContext& /*ic*/) final; + void run(ProcessingContext& /*pc*/) final; + void endOfStream(EndOfStreamContext& /*ec*/) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(); + + private: + static constexpr std::array mBitPatternsBefore{15, 30, 31, 60, 62, 63, 120, 124, 126}; + static constexpr std::array mBitPatternsAfter{31, 47, 61, 62, 63, 79, 94, 95, 111, 121, 122, 123, 124, 125, 126, 127}; + const std::bitset<7> mTopMask{"1110000"}; + const std::bitset<7> mBotMask{"0000111"}; + + void updateTimeDependentParams(ProcessingContext& pc); + std::string mOutFileName = "TrackExtensionStudy.root"; + std::shared_ptr mKineReader; + GeometryTGeo* mGeometry{}; + + gsl::span mTracksROFRecords; + gsl::span mTracks; + gsl::span mTracksMCLabels; + gsl::span mClusters; + gsl::span mInputITSidxs; + const o2::dataformats::MCLabelContainer* mClustersMCLCont{}; + + GTrackID::mask_t mTracksSrc{}; + std::shared_ptr mDataRequest; + std::vector>> mParticleInfo; // src/event/track + unsigned short mMask = 0x7f; + + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mStream; + bool mWithTree{false}; + + std::unique_ptr mHTrackCounts; + std::unique_ptr mHLengthAny, mHLengthGood, mHLengthFake; + std::unique_ptr mHChi2Any, mHChi2Good, mHChi2Fake; + std::unique_ptr mHPtAny, mHPtGood, mHPtFake; + std::unique_ptr mHExtensionAny, mHExtensionGood, mHExtensionFake; + std::unique_ptr mHExtensionPatternsAny, mHExtensionPatternsGood, mHExtensionPatternsFake, mHExtensionPatternsGoodMissed, mHExtensionPatternsGoodEmpty; + std::unique_ptr mEExtensionNum, mEExtensionDen, mEExtensionPurityNum, mEExtensionPurityDen, mEExtensionFakeNum, mEExtensionFakeDen; + std::unique_ptr mEExtensionFakeBeforeNum, mEExtensionFakeAfterNum, mEExtensionFakeMixNum; + std::unique_ptr mEExtensionTopNum, mEExtensionTopPurityNum, mEExtensionTopFakeNum; + std::unique_ptr mEExtensionBotNum, mEExtensionBotPurityNum, mEExtensionBotFakeNum; + std::unique_ptr mEExtensionMixNum, mEExtensionMixPurityNum, mEExtensionMixFakeNum; + std::array, mBitPatternsBefore.size()> mEExtensionPatternGoodNum, mEExtensionPatternFakeNum; + std::array, mBitPatternsAfter.size()>, mBitPatternsBefore.size()> mEExtensionPatternIndGoodNum, mEExtensionPatternIndFakeNum; + // DCA + std::unique_ptr mDCAxyVsPtPionsNormal, mDCAxyVsPtPionsExtended; + std::unique_ptr mDCAzVsPtPionsNormal, mDCAzVsPtPionsExtended; + + template + std::unique_ptr createHistogram(C... n, F... b) + { + auto t = std::make_unique(n..., b...); + mHistograms.push_back(static_cast(t.get())); + return std::move(t); + } + std::vector mHistograms; +}; + +void TrackExtensionStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mWithTree = ic.options().get("with-tree"); + + constexpr size_t effHistBins = 40; + constexpr float effPtCutLow = 0.01; + constexpr float effPtCutHigh = 10.; + auto xbins = helpers::makeLogBinning(effHistBins, effPtCutLow, effPtCutHigh); + + // Track Counting + mHTrackCounts = createHistogram("hTrackCounts", "Track Stats", 10, 0, 10); + mHTrackCounts->GetXaxis()->SetBinLabel(1, "Total Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(2, "Normal ANY Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(3, "Normal GOOD Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(4, "Normal FAKE Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(5, "Extended ANY Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(6, "Extended GOOD Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(7, "Extended FAKE Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(8, "Extended FAKE BEFORE Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(9, "Extended FAKE AFTER Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(10, "Extended FAKE BEFORE&AFTER Tracks"); + + // Length + mHLengthAny = createHistogram("hLengthAny", "Extended Tracks Length (ANY);NCluster;Entries", 5, 3, 8); + mHLengthGood = createHistogram("hLengthGood", "Extended Tracks Length (GOOD);NCluster;Entries", 5, 3, 8); + mHLengthFake = createHistogram("hLengthFake", "Extended Tracks Length (FAKE);NCluster;Entries", 5, 3, 8); + + // Chi2 + mHChi2Any = createHistogram("hChi2Any", "Extended Tracks Length (ANY);#chi^{2};Entries", 50, 0, 100); + mHChi2Good = createHistogram("hChi2Good", "Extended Tracks Length (GOOD);#chi^{2};Entries", 50, 0, 100); + mHChi2Fake = createHistogram("hChi2Fake", "Extended Tracks Length (FAKE);#chi^{2};Entries", 50, 0, 100); + + // Pt + mHPtAny = createHistogram("hPtAny", "Extended Tracks Length (ANY);#it{p}_{T};Entries", effHistBins, xbins.data()); + mHPtGood = createHistogram("hPtGood", "Extended Tracks Length (GOOD);#it{p}_{T};Entries", effHistBins, xbins.data()); + mHPtFake = createHistogram("hPtFake", "Extended Tracks Length (FAKE);#it{p}_{T};Entries", effHistBins, xbins.data()); + + // Length + mHExtensionAny = createHistogram("hExtensionAny", "Extended Tracks Length (ANY);Extended Layer;Entries", 7, 0, 7); + mHExtensionGood = createHistogram("hExtensionGood", "Extended Tracks Length (GOOD);Extended Layer;Entries", 7, 0, 7); + mHExtensionFake = createHistogram("hExtensionFake", "Extended Tracks Length (FAKE);Extended Layer;Entries", 7, 0, 7); + + // Patterns + auto makePatternAxisLabels = [&](TH1* h, bool xBefore = true) { + for (int i{1}; i <= h->GetXaxis()->GetNbins(); ++i) { + if (xBefore) { + h->GetXaxis()->SetBinLabel(i, fmt::format("{:07b}", mBitPatternsBefore[i - 1]).c_str()); + } else { + h->GetXaxis()->SetBinLabel(i, fmt::format("{:07b}", mBitPatternsAfter[i - 1]).c_str()); + } + } + for (int i{1}; i <= h->GetYaxis()->GetNbins(); ++i) { + h->GetYaxis()->SetBinLabel(i, fmt::format("{:07b}", mBitPatternsAfter[i - 1]).c_str()); + } + }; + mHExtensionPatternsAny = createHistogram("hExtensionPatternsAny", "Extended Tracks Pattern (ANY);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsAny.get()); + mHExtensionPatternsGood = createHistogram("hExtensionPatternsGood", "Extended Tracks Pattern (GOOD);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsGood.get()); + mHExtensionPatternsFake = createHistogram("hExtensionPatternsFake", "Extended Tracks Pattern (FAKE);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsFake.get()); + mHExtensionPatternsGoodMissed = createHistogram("hExtensionPatternsGoodMissed", "Extended Tracks Pattern (GOOD) Missed Clusters;After;Missed;Entries", mBitPatternsAfter.size(), 0, mBitPatternsAfter.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsGoodMissed.get(), false); + mHExtensionPatternsGoodEmpty = createHistogram("hExtensionPatternsGoodEmpty", "Extended Tracks Pattern (GOOD) Empty Clusters;Before;After;Entries", mBitPatternsAfter.size(), 0, mBitPatternsAfter.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsGoodEmpty.get(), false); + + /// Effiencies + mEExtensionNum = createHistogram("hExtensionNum", "Extension Numerator", effHistBins, xbins.data()); + mEExtensionDen = createHistogram("hExtensionDen", "Extension Dennominator", effHistBins, xbins.data()); + // Purity + mEExtensionPurityNum = createHistogram("hExtensionPurityNum", "Extension Purity Numerator", effHistBins, xbins.data()); + mEExtensionPurityDen = createHistogram("hExtensionPurityDen", "Extension Purity Denominator", effHistBins, xbins.data()); + // Fake + mEExtensionFakeNum = createHistogram("hExtensionFakeNum", "Extension Fake Numerator", effHistBins, xbins.data()); + mEExtensionFakeDen = createHistogram("hExtensionFakeDen", "Extension Fake Denominator", effHistBins, xbins.data()); + mEExtensionFakeBeforeNum = createHistogram("hExtensionFakeBeforeNum", "Extension Fake Before Numerator", effHistBins, xbins.data()); + mEExtensionFakeAfterNum = createHistogram("hExtensionFakeAfterNum", "Extension Fake After Numerator", effHistBins, xbins.data()); + mEExtensionFakeMixNum = createHistogram("hExtensionFakeMixNum", "Extension Fake Mix Numerator", effHistBins, xbins.data()); + // Top + mEExtensionTopNum = createHistogram("hExtensionTopNum", "Extension Top Numerator", effHistBins, xbins.data()); + mEExtensionTopPurityNum = createHistogram("hExtensionTopPurityNum", "Extension Top Purity Numerator", effHistBins, xbins.data()); + mEExtensionTopFakeNum = createHistogram("hExtensionTopFakeNum", "Extension Top Fake Numerator", effHistBins, xbins.data()); + mEExtensionBotNum = createHistogram("hExtensionBotNum", "Extension Bot Numerator", effHistBins, xbins.data()); + mEExtensionBotPurityNum = createHistogram("hExtensionBotPurityNum", "Extension Bot Purity Numerator", effHistBins, xbins.data()); + mEExtensionBotFakeNum = createHistogram("hExtensionBotFakeNum", "Extension Bot Fake Numerator", effHistBins, xbins.data()); + mEExtensionMixNum = createHistogram("hExtensionMixNum", "Extension Mix Numerator", effHistBins, xbins.data()); + mEExtensionMixPurityNum = createHistogram("hExtensionMixPurityNum", "Extension Mix Purity Numerator", effHistBins, xbins.data()); + mEExtensionMixFakeNum = createHistogram("hExtensionMixFakeNum", "Extension Mix Fake Numerator", effHistBins, xbins.data()); + // Patterns + for (int i{0}; i < mBitPatternsBefore.size(); ++i) { + mEExtensionPatternGoodNum[i] = createHistogram(fmt::format("hExtensionPatternGood_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b}", mBitPatternsBefore[i]).c_str(), effHistBins, xbins.data()); + mEExtensionPatternFakeNum[i] = createHistogram(fmt::format("hExtensionPatternFake_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b}", mBitPatternsBefore[i]).c_str(), effHistBins, xbins.data()); + for (int j{0}; j < mBitPatternsAfter.size(); ++j) { + mEExtensionPatternIndGoodNum[i][j] = createHistogram(fmt::format("hExtensionPatternGood_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b} -> {:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), effHistBins, xbins.data()); + mEExtensionPatternIndFakeNum[i][j] = createHistogram(fmt::format("hExtensionPatternFake_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b} -> {:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), effHistBins, xbins.data()); + } + } + + /// DCA + mDCAxyVsPtPionsNormal = createHistogram("hDCAxyVsPtResNormal", "DCA_{#it{xy}} NORMAL Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + mDCAxyVsPtPionsExtended = createHistogram("hDCAxyVsPtResExtended", "DCA_{#it{xy}} EXTENDED Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + mDCAzVsPtPionsNormal = createHistogram("hDCAzVsPtResNormal", "DCA_{#it{z}} NORMAL Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + mDCAzVsPtPionsExtended = createHistogram("hDCAzVsPtResExtended", "DCA_{#it{z}} EXTENDED Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + + mStream = std::make_unique(mOutFileName.c_str(), "RECREATE"); +} + +void TrackExtensionStudy::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest); + updateTimeDependentParams(pc); + + mTracksROFRecords = recoData.getITSTracksROFRecords(); + mTracks = recoData.getITSTracks(); + mTracksMCLabels = recoData.getITSTracksMCLabels(); + mClusters = recoData.getITSClusters(); + mClustersMCLCont = recoData.getITSClustersMCLabels(); + mInputITSidxs = recoData.getITSTracksClusterRefs(); + + LOGP(info, "** Found in {} rofs:\n\t- {} clusters with {} labels\n\t- {} tracks with {} labels", + mTracksROFRecords.size(), mClusters.size(), mClustersMCLCont->getIndexedSize(), mTracks.size(), mTracksMCLabels.size()); + LOGP(info, "** Found {} sources from kinematic files", mKineReader->getNSources()); + + process(); +} + +void TrackExtensionStudy::process() +{ + LOGP(info, "** Filling particle table ... "); + mParticleInfo.resize(mKineReader->getNSources()); // sources + for (int iSource{0}; iSource < mKineReader->getNSources(); ++iSource) { + mParticleInfo[iSource].resize(mKineReader->getNEvents(iSource)); // events + for (int iEvent{0}; iEvent < mKineReader->getNEvents(iSource); ++iEvent) { + const auto& mcEvent = mKineReader->getMCEventHeader(iSource, iEvent); + mParticleInfo[iSource][iEvent].resize(mKineReader->getTracks(iSource, iEvent).size()); // tracks + for (auto iPart{0}; iPart < mKineReader->getTracks(iEvent).size(); ++iPart) { + const auto& part = mKineReader->getTracks(iSource, iEvent)[iPart]; + mParticleInfo[iSource][iEvent][iPart].eventX = mcEvent.GetX(); + mParticleInfo[iSource][iEvent][iPart].eventY = mcEvent.GetY(); + mParticleInfo[iSource][iEvent][iPart].eventZ = mcEvent.GetZ(); + mParticleInfo[iSource][iEvent][iPart].pdg = part.GetPdgCode(); + mParticleInfo[iSource][iEvent][iPart].pt = part.GetPt(); + mParticleInfo[iSource][iEvent][iPart].phi = part.GetPhi(); + mParticleInfo[iSource][iEvent][iPart].eta = part.GetEta(); + mParticleInfo[iSource][iEvent][iPart].vx = part.Vx(); + mParticleInfo[iSource][iEvent][iPart].vy = part.Vy(); + mParticleInfo[iSource][iEvent][iPart].vz = part.Vz(); + mParticleInfo[iSource][iEvent][iPart].isPrimary = part.isPrimary(); + mParticleInfo[iSource][iEvent][iPart].mother = part.getMotherTrackId(); + mParticleInfo[iSource][iEvent][iPart].prodProcess = part.getProcess(); + } + } + } + LOGP(info, "** Creating particle/clusters correspondance ... "); + for (auto iSource{0}; iSource < mParticleInfo.size(); ++iSource) { + for (auto iCluster{0}; iCluster < mClusters.size(); ++iCluster) { + auto labs = mClustersMCLCont->getLabels(iCluster); // ideally I can have more than one label per cluster + for (auto& lab : labs) { + if (!lab.isValid()) { + continue; // We want to skip channels related to noise, e.g. sID = 99: QED + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + auto& cluster = mClusters[iCluster]; + auto layer = mGeometry->getLayer(cluster.getSensorID()); + mParticleInfo[srcID][evID][trackID].clusters |= (1 << layer); + if (fake) { + mParticleInfo[srcID][evID][trackID].fakeClusters |= (1 << layer); + } + } + } + } + + LOGP(info, "** Analysing tracks ... "); + int unaccounted{0}, good{0}, fakes{0}, extended{0}; + for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { + const auto& lab = mTracksMCLabels[iTrack]; + if (!lab.isValid()) { + unaccounted++; + continue; + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + + if (srcID == 99) { // skip QED + unaccounted++; + continue; + } + + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (mTracks[iTrack].isExtendedOnLayer(iLayer)) { + ++extended; + break; + } + } + + mParticleInfo[srcID][evID][trackID].isReco += !fake; + mParticleInfo[srcID][evID][trackID].isFake += fake; + if (mTracks[iTrack].isBetter(mParticleInfo[srcID][evID][trackID].track, 1.e9)) { + mParticleInfo[srcID][evID][trackID].storedStatus = fake; + mParticleInfo[srcID][evID][trackID].track = mTracks[iTrack]; + mParticleInfo[srcID][evID][trackID].mcTrack = *mKineReader->getTrack(lab); + } + fakes += fake; + good += !fake; + } + LOGP(info, "** Some statistics:"); + LOGP(info, "\t- Total number of tracks: {}", mTracks.size()); + LOGP(info, "\t- Total number of tracks not corresponding to particles: {} ({:.2f} %)", unaccounted, unaccounted * 100. / mTracks.size()); + LOGP(info, "\t- Total number of fakes: {} ({:.2f} %)", fakes, fakes * 100. / mTracks.size()); + LOGP(info, "\t- Total number of good: {} ({:.2f} %)", good, good * 100. / mTracks.size()); + LOGP(info, "\t- Total number of extensions: {} ({:.2f} %)", extended, extended * 100. / mTracks.size()); + + o2::dataformats::VertexBase collision; + o2::dataformats::DCA impactParameter; + LOGP(info, "** Filling histograms ... "); + for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { + auto& lab = mTracksMCLabels[iTrack]; + if (!lab.isValid()) { + unaccounted++; + continue; + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + const auto& part = mParticleInfo[srcID][evID][trackID]; + if (!part.isPrimary) { + continue; + } + const auto& trk = part.track; + bool isGood = part.isReco && !part.isFake; + mHTrackCounts->Fill(0); + + std::bitset<7> extPattern{0}; + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (trk.isExtendedOnLayer(iLayer)) { + extPattern.set(iLayer); + } + } + + // Tree + while (mWithTree) { + constexpr float refRadius{70.f}; + constexpr float maxSnp{0.9f}; + auto cTrk = trk; + if (!o2::base::Propagator::Instance()->PropagateToXBxByBz(cTrk, refRadius, maxSnp, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrTGeo)) { + break; + } + std::array xyz{(float)part.mcTrack.GetStartVertexCoordinatesX(), (float)part.mcTrack.GetStartVertexCoordinatesY(), (float)part.mcTrack.GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)part.mcTrack.GetStartVertexMomentumX(), (float)part.mcTrack.GetStartVertexMomentumY(), (float)part.mcTrack.GetStartVertexMomentumZ()}; + auto pdg = O2DatabasePDG::Instance()->GetParticle(part.pdg); + if (pdg == nullptr) { + LOGP(error, "MC info not available"); + break; + } + auto mcTrk = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pdg->Charge() / 3.), true); + if (!mcTrk.rotate(cTrk.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(mcTrk, refRadius, maxSnp, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrTGeo)) { + break; + } + (*mStream) << "tree" + << "trk=" << cTrk + << "mcTrk=" << mcTrk + << "isGood=" << isGood + << "isExtended=" << extPattern.any() + << "\n"; + break; + } + + // impact parameter + while (isGood && std::abs(part.pdg) == 211) { + auto trkC = part.track; + collision.setXYZ(part.eventX, part.eventY, part.eventZ); + if (!o2::base::Propagator::Instance()->propagateToDCA(collision, trkC, o2::base::Propagator::Instance()->getNominalBz(), 2.0, o2::base::Propagator::MatCorrType::USEMatCorrTGeo, &impactParameter)) { + break; + } + + auto dcaXY = impactParameter.getY() * 1e4; + auto dcaZ = impactParameter.getZ() * 1e4; + if (!extPattern.any()) { + mDCAxyVsPtPionsNormal->Fill(part.pt, dcaXY); + mDCAzVsPtPionsNormal->Fill(part.pt, dcaZ); + } else { + mDCAxyVsPtPionsExtended->Fill(part.pt, dcaXY); + mDCAzVsPtPionsExtended->Fill(part.pt, dcaZ); + } + break; + } + + mEExtensionDen->Fill(trk.getPt()); + + if (!extPattern.any()) { + mHTrackCounts->Fill(1); + if (part.isReco || !part.isFake) { + mHTrackCounts->Fill(2); + } else { + mHTrackCounts->Fill(3); + } + continue; + } + + mHTrackCounts->Fill(4); + mHLengthAny->Fill(trk.getNClusters()); + mHChi2Any->Fill(trk.getChi2()); + mHPtAny->Fill(trk.getPt()); + mEExtensionNum->Fill(trk.getPt()); + mEExtensionPurityDen->Fill(trk.getPt()); + mEExtensionFakeDen->Fill(trk.getPt()); + if (isGood) { + mHTrackCounts->Fill(5); + mHLengthGood->Fill(trk.getNClusters()); + mHChi2Good->Fill(trk.getChi2()); + mHPtGood->Fill(trk.getPt()); + mEExtensionPurityNum->Fill(trk.getPt()); + } else { + mHTrackCounts->Fill(6); + mHLengthFake->Fill(trk.getNClusters()); + mHChi2Fake->Fill(trk.getChi2()); + mHPtFake->Fill(trk.getPt()); + mEExtensionFakeNum->Fill(trk.getPt()); + } + + std::bitset<7> clusPattern{static_cast(trk.getPattern())}; + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (extPattern.test(iLayer)) { + extPattern.set(iLayer); + mHExtensionAny->Fill(iLayer); + if (isGood) { + mHExtensionGood->Fill(iLayer); + } else { + mHExtensionFake->Fill(iLayer); + } + } + } + std::bitset<7> oldPattern{clusPattern & ~extPattern}, holePattern{clusPattern}; + holePattern.flip(); + auto clusN = clusPattern.to_ulong(); + auto clusIdx = std::distance(std::begin(mBitPatternsAfter), std::find(std::begin(mBitPatternsAfter), std::end(mBitPatternsAfter), clusN)); + auto oldN = oldPattern.to_ulong(); + auto oldIdx = std::distance(std::begin(mBitPatternsBefore), std::find(std::begin(mBitPatternsBefore), std::end(mBitPatternsBefore), oldN)); + mHExtensionPatternsAny->Fill(oldIdx, clusIdx); + if (isGood) { + mHExtensionPatternsGood->Fill(oldIdx, clusIdx); + mEExtensionPatternGoodNum[oldIdx]->Fill(trk.getPt()); + mEExtensionPatternIndGoodNum[oldIdx][clusIdx]->Fill(trk.getPt()); + } else { + mHExtensionPatternsFake->Fill(oldIdx, clusIdx); + mEExtensionPatternFakeNum[oldIdx]->Fill(trk.getPt()); + mEExtensionPatternIndFakeNum[oldIdx][clusIdx]->Fill(trk.getPt()); + } + + // old pattern + bool oldFake{false}, newFake{false}; + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (trk.isFakeOnLayer(iLayer)) { + if (oldPattern.test(iLayer)) { + oldFake = true; + } else if (extPattern.test(iLayer)) { + newFake = true; + } + } + } + if (oldFake && newFake) { + mHTrackCounts->Fill(9); + mEExtensionFakeMixNum->Fill(trk.getPt()); + } else if (oldFake) { + mHTrackCounts->Fill(7); + mEExtensionFakeBeforeNum->Fill(trk.getPt()); + } else if (newFake) { + mHTrackCounts->Fill(8); + mEExtensionFakeAfterNum->Fill(trk.getPt()); + } + + // Check if we missed some clusters + if (isGood && holePattern.any()) { + auto missPattern{clusPattern}, emptyPattern{clusPattern}; + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (!holePattern.test(iLayer)) { + continue; + } + + // Check if there was actually a cluster that we missed + if ((part.clusters & (1 << iLayer)) != 0) { + missPattern.set(iLayer); + } else { + emptyPattern.set(iLayer); + } + } + + if (missPattern != clusPattern) { + auto missN = missPattern.to_ulong(); + auto missIdx = std::distance(std::begin(mBitPatternsAfter), std::find(std::begin(mBitPatternsAfter), std::end(mBitPatternsAfter), missN)); + mHExtensionPatternsGoodMissed->Fill(clusIdx, missIdx); + } + if (emptyPattern != clusPattern) { + auto emptyN = emptyPattern.to_ulong(); + auto emptyIdx = std::distance(std::begin(mBitPatternsAfter), std::find(std::begin(mBitPatternsAfter), std::end(mBitPatternsAfter), emptyN)); + mHExtensionPatternsGoodEmpty->Fill(clusIdx, emptyIdx); + } + } + + // Top/Bot/Mixed Extension + bool isTop = (extPattern & mTopMask).any(); + bool isBot = (extPattern & mBotMask).any(); + if (isTop && isBot) { + mEExtensionMixNum->Fill(trk.getPt()); + if (isGood) { + mEExtensionMixPurityNum->Fill(trk.getPt()); + } else { + mEExtensionMixFakeNum->Fill(trk.getPt()); + } + } else if (isTop) { + mEExtensionTopNum->Fill(trk.getPt()); + if (isGood) { + mEExtensionTopPurityNum->Fill(trk.getPt()); + } else { + mEExtensionTopFakeNum->Fill(trk.getPt()); + } + } else { + mEExtensionBotNum->Fill(trk.getPt()); + if (isGood) { + mEExtensionBotPurityNum->Fill(trk.getPt()); + } else { + mEExtensionBotFakeNum->Fill(trk.getPt()); + } + } + } +} + +void TrackExtensionStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + static bool initOnceDone = false; + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + mGeometry = GeometryTGeo::Instance(); + mGeometry->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void TrackExtensionStudy::endOfStream(EndOfStreamContext& ec) +{ + LOGP(info, "Writing results to {}", mOutFileName); + mStream->GetFile()->cd(); + for (const auto h : mHistograms) { + h->Write(); + } + + LOGP(info, "Calculating efficiencies"); + auto makeEff = [](auto num, auto den, const char* name, const char* title) { + auto e = std::make_unique(*num, *den); + e->SetName(name); + e->SetTitle(title); + e->Write(); + }; + makeEff(mEExtensionNum.get(), mEExtensionDen.get(), "eExtension", "Track Extension EXT TRK/ALL"); + makeEff(mEExtensionPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionPurity", "Track Extension Purity GOOD/EXT TRK"); + makeEff(mEExtensionFakeNum.get(), mEExtensionFakeDen.get(), "eExtensionFake", "Track Extension Fake FAKE/EXT TRK"); + makeEff(mEExtensionFakeBeforeNum.get(), mEExtensionFakeNum.get(), "eExtensionFakeBefore", "Track Extension Fake FAKE BEF/FAKE EXT TRK"); + makeEff(mEExtensionFakeAfterNum.get(), mEExtensionFakeNum.get(), "eExtensionFakeAfter", "Track Extension Fake FAKE AFT/FAKE EXT TRK"); + makeEff(mEExtensionFakeMixNum.get(), mEExtensionFakeNum.get(), "eExtensionFakeMix", "Track Extension Fake FAKE MIX/FAKE EXT TRK"); + makeEff(mEExtensionTopNum.get(), mEExtensionDen.get(), "eExtensionTop", "Track Extension Top"); + makeEff(mEExtensionTopPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionTopPurity", "Track Extension Purity GOOD TOP/EXT TRK"); + makeEff(mEExtensionTopFakeNum.get(), mEExtensionFakeNum.get(), "eExtensionTopFake", "Track Extension FAKE TOP/EXT FAKE TRK"); + makeEff(mEExtensionBotNum.get(), mEExtensionDen.get(), "eExtensionBot", "Track Extension Bot"); + makeEff(mEExtensionBotPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionBotPurity", "Track Extension Purity GOOD BOT/EXT TRK"); + makeEff(mEExtensionBotFakeNum.get(), mEExtensionFakeNum.get(), "eExtensionBotFake", "Track Extension FAKE BOT/EXT FAKE TRK"); + makeEff(mEExtensionMixNum.get(), mEExtensionDen.get(), "eExtensionMix", "Track Extension Mix"); + makeEff(mEExtensionMixPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionMixPurity", "Track Extension Purity GOOD MIX/EXT TRK"); + makeEff(mEExtensionMixFakeNum.get(), mEExtensionFakeNum.get(), "eExtensionMixFake", "Track Extension FAKE MIX/EXT FAKE TRK"); + for (int i{0}; i < mBitPatternsBefore.size(); ++i) { + makeEff(mEExtensionPatternGoodNum[i].get(), mEExtensionPurityNum.get(), fmt::format("eExtensionPatternGood_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b} GOOD EXT TRK/EXT TRK", mBitPatternsBefore[i]).c_str()); + makeEff(mEExtensionPatternFakeNum[i].get(), mEExtensionFakeNum.get(), fmt::format("eExtensionPatternFake_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b} FAKE EXT TRK/EXT TRK", mBitPatternsBefore[i]).c_str()); + for (int j{0}; j < mBitPatternsAfter.size(); ++j) { + makeEff(mEExtensionPatternIndGoodNum[i][j].get(), mEExtensionPatternGoodNum[i].get(), fmt::format("eExtensionPatternGood_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b} -> {:07b} GOOD EXT TRK/EXT TRK", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str()); + makeEff(mEExtensionPatternIndFakeNum[i][j].get(), mEExtensionPatternFakeNum[i].get(), fmt::format("eExtensionPatternFake_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b} -> {:07b} FAKE EXT TRK/EXT TRK", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str()); + } + } + + mStream->Close(); +} + +void TrackExtensionStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } +} + +DataProcessorSpec getTrackExtensionStudy(mask_t srcTracksMask, mask_t srcClustersMask, std::shared_ptr kineReader) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, true); + dataRequest->requestClusters(srcClustersMask, true); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + + return DataProcessorSpec{ + "its-study-track-extension", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, srcTracksMask, kineReader, ggRequest)}, + Options{{"with-tree", o2::framework::VariantType::Bool, false, {"Produce in addition a tree"}}}}; +} + +} // namespace o2::its::study diff --git a/Detectors/ITSMFT/ITS/postprocessing/workflow/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/workflow/CMakeLists.txt new file mode 100644 index 0000000000000..8ec0d3b2ff325 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/workflow/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_executable(postprocessing-workflow + COMPONENT_NAME its-standalone + SOURCES standalone-postprocessing-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::ITSPostprocessing) \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx new file mode 100644 index 0000000000000..405e80475bd25 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx @@ -0,0 +1,158 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/CallbacksPolicy.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" + +// Include studies hereafter +#include "ITSStudies/ImpactParameter.h" +#include "ITSStudies/AvgClusSize.h" +#include "ITSStudies/PIDStudy.h" +#include "ITSStudies/AnomalyStudy.h" +#include "ITSStudies/ITSBeamBackgroundStudy.h" +#include "ITSStudies/Efficiency.h" +#include "ITSStudies/TrackCheck.h" +#include "ITSStudies/TrackExtension.h" +#include "Steer/MCKinematicsReader.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"input-from-upstream", VariantType::Bool, false, {"read clusters from the clusterer"}}, + {"track-sources", VariantType::String, std::string{"ITS,ITS-TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC,ITS-TPC-TRD,ZDC"}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, std::string{"ITS"}, {"comma-separated list of cluster sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"cluster-size-study", VariantType::Bool, false, {"Perform the average cluster size study"}}, + {"pid-study", VariantType::Bool, false, {"Perform the PID study"}}, + {"track-study", VariantType::Bool, false, {"Perform the track study"}}, + {"impact-parameter-study", VariantType::Bool, false, {"Perform the impact parameter study"}}, + {"anomaly-study", VariantType::Bool, false, {"Perform the anomaly study"}}, + {"its-beambkg-study", VariantType::Bool, false, {"Perform the ITS beam background study"}}, + {"track-extension-study", VariantType::Bool, false, {"Perform the track extension study"}}, + {"efficiency-study", VariantType::Bool, false, {"Perform the efficiency study"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); + // o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root"); + std::swap(workflowOptions, options); +} + +#include + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + GID::mask_t srcTrc, srcCls; + + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + auto useMC = !configcontext.options().get("disable-mc"); + + std::shared_ptr mcKinematicsReader; + if (useMC) { + mcKinematicsReader = std::make_shared("collisioncontext.root"); + } + bool anyStudy{false}; + // Declare specs related to studies hereafter + if (configcontext.options().get("impact-parameter-study")) { + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + specs.emplace_back(o2::its::study::getImpactParameterStudy(srcTrc, srcCls, useMC)); + } + if (configcontext.options().get("cluster-size-study")) { + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + specs.emplace_back(o2::its::study::getAvgClusSizeStudy(srcTrc, srcCls, useMC, mcKinematicsReader)); + } + if (configcontext.options().get("pid-study")) { + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + specs.emplace_back(o2::its::study::getPIDStudy(srcTrc, srcCls, useMC, mcKinematicsReader)); + } + if (configcontext.options().get("track-study")) { + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + if (!configcontext.options().get("input-from-upstream")) { + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + } + specs.emplace_back(o2::its::study::getTrackCheckStudy(GID::getSourcesMask("ITS"), GID::getSourcesMask("ITS"), useMC, mcKinematicsReader)); + } + if (configcontext.options().get("anomaly-study")) { + anyStudy = true; + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + if (!configcontext.options().get("input-from-upstream")) { + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + } + specs.emplace_back(o2::its::study::getAnomalyStudy(srcCls, useMC)); + } + if (configcontext.options().get("its-beambkg-study")) { + anyStudy = true; + + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + + if (!configcontext.options().get("input-from-upstream")) { + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + } + specs.emplace_back(o2::its::study::getITSBeamBackgroundStudy(srcTrc, srcCls, useMC)); + } + if (configcontext.options().get("track-extension-study")) { + if (!useMC) { + LOGP(fatal, "Track Extension Study needs MC!"); + } + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask("ITS"); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, true, srcCls, srcTrc); + specs.emplace_back(o2::its::study::getTrackExtensionStudy(srcTrc, srcCls, mcKinematicsReader)); + } + if (configcontext.options().get("efficiency-study")) { + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + if (!configcontext.options().get("input-from-upstream")) { + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + } + specs.emplace_back(o2::its::study::getEfficiencyStudy(GID::getSourcesMask("ITS"), GID::getSourcesMask("ITS"), useMC, mcKinematicsReader)); + } + if (!anyStudy) { + LOGP(info, "No study selected, dryrunning"); + } + + // o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + // write the configuration used for the studies workflow + o2::conf::ConfigurableParam::writeINI("o2_its_standalone_configuration.ini"); + + return std::move(specs); +} diff --git a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt index a625987bf59e5..be42015b95795 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt @@ -9,19 +9,13 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -o2_add_library( - ITSReconstruction - SOURCES src/ClustererTask.cxx src/CookedTracker.cxx src/CookedConfigParam.cxx - src/RecoGeomHelper.cxx src/FastMultEstConfig.cxx src/FastMultEst.cxx - PUBLIC_LINK_LIBRARIES O2::ITSBase O2::ITSMFTReconstruction O2::DataFormatsITS - O2::CommonUtils) +o2_add_library(ITSReconstruction + SOURCES src/RecoGeomHelper.cxx + PUBLIC_LINK_LIBRARIES O2::ITSBase + O2::ITSMFTReconstruction + O2::DataFormatsITS + O2::CommonUtils) o2_target_root_dictionary( ITSReconstruction - HEADERS include/ITSReconstruction/ClustererTask.h - include/ITSReconstruction/CookedTracker.h - include/ITSReconstruction/CookedConfigParam.h - include/ITSReconstruction/RecoGeomHelper.h - include/ITSReconstruction/FastMultEst.h - include/ITSReconstruction/FastMultEstConfig.h - LINKDEF src/CookedTrackerLinkDef.h) + HEADERS include/ITSReconstruction/RecoGeomHelper.h) diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/ClustererTask.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/ClustererTask.h deleted file mode 100644 index 16ac9dd63c631..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/ClustererTask.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file ClustererTask.h -/// \brief Definition of the ITS cluster finder task - -#ifndef ALICEO2_ITS_CLUSTERERTASK -#define ALICEO2_ITS_CLUSTERERTASK - -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "ITSMFTReconstruction/PixelReader.h" -#include "ITSMFTReconstruction/RawPixelReader.h" -#include "ITSMFTReconstruction/DigitPixelReader.h" -#include "ITSMFTReconstruction/Clusterer.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include -#include - -namespace o2 -{ -class MCCompLabel; -namespace dataformats -{ -template -class MCTruthContainer; -} - -namespace its -{ - -class ClustererTask -{ - using Clusterer = o2::itsmft::Clusterer; - using CompCluster = o2::itsmft::CompCluster; - using CompClusterExt = o2::itsmft::CompClusterExt; - using MCTruth = o2::dataformats::MCTruthContainer; - - public: - ClustererTask(bool useMC = true, bool raw = false); - ~ClustererTask(); - - void Init(); - Clusterer& getClusterer() { return mClusterer; } - void run(const std::string inpName, const std::string outName); - o2::itsmft::PixelReader* getReader() const { return (o2::itsmft::PixelReader*)mReader; } - - void writeTree(std::string basename, int i); - void setMaxROframe(int max) { maxROframe = max; } - int getMaxROframe() const { return maxROframe; } - - private: - int maxROframe = std::numeric_limits::max(); ///< maximal number of RO frames per a file - bool mRawDataMode = false; ///< input from raw data or MC digits - bool mUseMCTruth = true; ///< flag to use MCtruth if available - o2::itsmft::PixelReader* mReader = nullptr; ///< Pointer on the relevant Pixel reader - std::unique_ptr mReaderMC; ///< reader for MC data - std::unique_ptr> mReaderRaw; ///< reader for raw data - - Clusterer mClusterer; ///< Cluster finder - - std::vector mCompClus; //!< vector of compact clusters - - std::vector mROFRecVec; //!< vector of ROFRecord references - - MCTruth mClsLabels; //! MC labels - - std::vector mPatterns; - - ClassDefNV(ClustererTask, 2); -}; -} // namespace its -} // namespace o2 - -#endif /* ALICEO2_ITS_CLUSTERERTASK */ diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedConfigParam.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedConfigParam.h deleted file mode 100644 index bfc111d0a3803..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedConfigParam.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_COOKEDTRACKINGPARAM_H_ -#define ALICEO2_COOKEDTRACKINGPARAM_H_ - -#include "CommonUtils/ConfigurableParamHelper.h" - -namespace o2 -{ -namespace its -{ - -struct CookedConfigParam : public o2::conf::ConfigurableParamHelper { - // seed "windows" in z and phi: makeSeeds - float zWin = 0.33; - float minPt = 0.05; - // Maximal accepted impact parameters for the seeds - float maxDCAxy = 3.; - float maxDCAz = 3.; - // Space-point resolution - float sigma = 0.0005; - // Tracking "road" from layer to layer - float roadY = 0.2; - float roadZ = 0.3; - // Minimal number of attached clusters - int minNumberOfClusters = 4; - - O2ParamDef(CookedConfigParam, "ITSCookedTracker"); -}; - -} // namespace its -} // namespace o2 -#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h deleted file mode 100644 index 918f7f82cbff8..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CookedTracker.h -/// \brief Definition of the "Cooked Matrix" ITS tracker -/// \author iouri.belikov@cern.ch - -#ifndef ALICEO2_ITS_COOKEDTRACKER_H -#define ALICEO2_ITS_COOKEDTRACKER_H - -//------------------------------------------------------------------------- -// A stand-alone ITS tracker -// The pattern recongintion based on the "cooked covariance" approach -//------------------------------------------------------------------------- - -#include -#include -#include "ITSBase/GeometryTGeo.h" -#include "MathUtils/Cartesian.h" -#include "DataFormatsITSMFT/Cluster.h" -#include "DataFormatsITS/TrackITS.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "ReconstructionDataFormats/Vertex.h" -#include "ITSReconstruction/CookedConfigParam.h" - -using Point3Df = o2::math_utils::Point3D; - -namespace o2 -{ -class MCCompLabel; -namespace dataformats -{ -template -class MCTruthContainer; -} -namespace itsmft -{ -class TopologyDictionary; -class CompClusterExt; -} // namespace itsmft -namespace its -{ -class CookedTracker -{ - using Cluster = o2::itsmft::Cluster; - using CompClusterExt = o2::itsmft::CompClusterExt; - using Vertex = o2::dataformats::Vertex>; - - public: - CookedTracker(Int_t nThreads = 1); - CookedTracker(const CookedTracker&) = delete; - CookedTracker& operator=(const CookedTracker& tr) = delete; - ~CookedTracker() = default; - - void setConfigParams() - { - const auto& par = CookedConfigParam::Instance(); - LOG(info) << " Setting configurable parameters..."; - - gzWin = par.zWin; - gminPt = par.minPt; - gmaxDCAxy = par.maxDCAxy; - gmaxDCAz = par.maxDCAz; - gSigma2 = par.sigma * par.sigma; - gRoadY = par.roadY; - gRoadZ = par.roadZ; - gminNumberOfClusters = par.minNumberOfClusters; - } - void setParameters(const std::vector& par) - { - gzWin = par[0]; - gminPt = par[1]; - gmaxDCAxy = par[3]; - gmaxDCAz = par[4]; - gSeedingLayer1 = par[5]; - gSeedingLayer2 = par[6]; - gSeedingLayer3 = par[7]; - gSigma2 = par[8] * par[8]; - gmaxChi2PerCluster = par[9]; - gmaxChi2PerTrack = par[10]; - gRoadY = par[11]; - gRoadZ = par[12]; - gminNumberOfClusters = par[13]; - } - void setParametersCosmics() - { - // seed "windows" in z and phi: makeSeeds - gzWin = 84.; // length of the L3 - gminPt = 10.; - // Maximal accepted impact parameters for the seeds - gmaxDCAxy = 19.4; // radius of the L3 - gmaxDCAz = 42.; // half-lenght of the L3 - // Space point resolution - gSigma2 = 0.2 * 0.2; - // Tracking "road" from layer to layer - gRoadY = 1.5; // Chip size in Y - gRoadZ = 3.0; // Chip size in Z - } - - void setVertices(const std::vector& vertices) - { - mVertices = &vertices; - } - - Double_t getX() const { return mX; } - Double_t getY() const { return mY; } - Double_t getZ() const { return mZ; } - Double_t getSigmaX() const { return mSigmaX; } - Double_t getSigmaY() const { return mSigmaY; } - Double_t getSigmaZ() const { return mSigmaZ; } - o2::MCCompLabel cookLabel(TrackITSExt& t, Float_t wrong) const; - void setExternalIndices(TrackITSExt& t) const; - Double_t getBz() const; - void setBz(Double_t bz) { mBz = bz; } - - void setNumberOfThreads(Int_t n) { mNumOfThreads = n; } - Int_t getNumberOfThreads() const { return mNumOfThreads; } - - using TrackInserter = std::function; - // These functions must be implemented - template - void process(gsl::span clusters, gsl::span::iterator& it, const o2::itsmft::TopologyDictionary* dict, U& tracks, V& clusIdx, o2::itsmft::ROFRecord& rof) - { - TrackInserter inserter = [&tracks, &clusIdx, this](const TrackITSExt& t) -> int { - // convert internal track to output format - auto& trackNew = tracks.emplace_back(t); - int noc = t.getNumberOfClusters(); - int clEntry = clusIdx.size(); - for (int i = 0; i < noc; i++) { - const Cluster* c = this->getCluster(t.getClusterIndex(i)); - Int_t idx = c - &mClusterCache[0]; // Index of this cluster in event - clusIdx.emplace_back(this->mFirstInFrame + idx); - } - trackNew.setClusterRefs(clEntry, noc); - trackNew.setPattern(0x7f); // this tracker finds only complete tracks - return tracks.size(); - }; - process(clusters, it, dict, inserter, rof); - } - void process(gsl::span const& clusters, gsl::span::iterator& it, const o2::itsmft::TopologyDictionary* dict, TrackInserter& inserter, o2::itsmft::ROFRecord& rof); - const Cluster* getCluster(Int_t index) const; - - void setGeometry(o2::its::GeometryTGeo* geom); - void setMCTruthContainers(const o2::dataformats::MCTruthContainer* clsLabels, std::vector* trkLabels) - { - mClsLabels = clsLabels; - mTrkLabels = trkLabels; - } - - void setContinuousMode(bool mode) { mContinuousMode = mode; } - bool getContinuousMode() { return mContinuousMode; } - - static void setMostProbablePt(float pt) { mMostProbablePt = pt; } - static auto getMostProbablePt() { return mMostProbablePt; } - - // internal helper classes - class ThreadData; - class Layer; - - protected: - static constexpr int kNLayers = 7; - int loadClusters(); - void unloadClusters(); - std::tuple processLoadedClusters(TrackInserter& inserter); - - std::vector trackInThread(Int_t first, Int_t last); - o2::its::TrackITSExt cookSeed(const Point3Df& r1, Point3Df& r2, const Point3Df& tr3, float rad2, float rad3, float_t alpha, float_t bz); - void makeSeeds(std::vector& seeds, Int_t first, Int_t last); - void trackSeeds(std::vector& seeds); - - Bool_t attachCluster(Int_t& volID, Int_t nl, Int_t ci, TrackITSExt& t, const TrackITSExt& o) const; - - void makeBackPropParam(std::vector& seeds) const; - bool makeBackPropParam(TrackITSExt& track) const; - - private: - /*** Tracking parameters ***/ - // seed "windows" in z and phi: makeSeeds - static Float_t gzWin; - static Float_t gminPt; - static Float_t mMostProbablePt; ///< settable most probable pt - // Maximal accepted impact parameters for the seeds - static Float_t gmaxDCAxy; - static Float_t gmaxDCAz; - // Layers for the seeding - static Int_t gSeedingLayer1; - static Int_t gSeedingLayer2; - static Int_t gSeedingLayer3; - // Space point resolution - static Float_t gSigma2; - // Max accepted chi2 - static Float_t gmaxChi2PerCluster; - static Float_t gmaxChi2PerTrack; - // Tracking "road" from layer to layer - static Float_t gRoadY; - static Float_t gRoadZ; - // Minimal number of attached clusters - static Int_t gminNumberOfClusters; - - bool mContinuousMode = true; ///< triggered or cont. mode - const o2::its::GeometryTGeo* mGeom = nullptr; /// interface to geometry - const o2::dataformats::MCTruthContainer* mClsLabels = nullptr; /// Cluster MC labels - std::vector* mTrkLabels = nullptr; /// Track MC labels - std::uint32_t mFirstInFrame = 0; ///< Index of the 1st cluster of a frame (within the loaded vector of clusters) - - Int_t mNumOfThreads; ///< Number of tracking threads - - Double_t mBz; ///< Effective Z-component of the magnetic field (kG) - - const std::vector* mVertices = nullptr; - Double_t mX = 0.; ///< X-coordinate of the primary vertex - Double_t mY = 0.; ///< Y-coordinate of the primary vertex - Double_t mZ = 0.; ///< Z-coordinate of the primary vertex - - Double_t mSigmaX = 2.; ///< error of the primary vertex position in X - Double_t mSigmaY = 2.; ///< error of the primary vertex position in Y - Double_t mSigmaZ = 2.; ///< error of the primary vertex position in Z - - static Layer sLayers[kNLayers]; ///< Layers filled with clusters - std::vector mSeeds; ///< Track seeds - - std::vector mClusterCache; - - ClassDefNV(CookedTracker, 1); -}; - -class CookedTracker::Layer -{ - public: - Layer(); - Layer(const Layer&) = delete; - Layer& operator=(const Layer& tr) = delete; - - void init(); - Bool_t insertCluster(const Cluster* c); - void setR(Double_t r) { mR = r; } - void unloadClusters(); - void selectClusters(std::vector& s, Float_t phi, Float_t dy, Float_t z, Float_t dz); - Int_t findClusterIndex(Float_t z) const; - Float_t getR() const { return mR; } - const Cluster* getCluster(Int_t i) const { return mClusters[i]; } - Float_t getAlphaRef(Int_t i) const { return mAlphaRef[i]; } - Float_t getClusterPhi(Int_t i) const { return mPhi[i]; } - Int_t getNumberOfClusters() const { return mClusters.size(); } - void setGeometry(o2::its::GeometryTGeo* geom) { mGeom = geom; } - - protected: - enum { kNSectors = 21 }; - - Float_t mR; ///< mean radius of this layer - const o2::its::GeometryTGeo* mGeom = nullptr; ///< interface to geometry - std::vector mClusters; ///< All clusters - std::vector mAlphaRef; ///< alpha of the reference plane - std::vector mPhi; ///< cluster phi - std::vector> mSectors[kNSectors]; ///< Cluster indices sector-by-sector -}; -} // namespace its -} // namespace o2 -#endif /* ALICEO2_ITS_COOKEDTRACKER_H */ diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h deleted file mode 100644 index 457381862cc42..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file FastMultEst.h -/// \brief Fast multiplicity estimator for ITS -/// \author ruben.shahoyan@cern.ch - -#ifndef ALICEO2_ITS_FASTMULTEST_ -#define ALICEO2_ITS_FASTMULTEST_ - -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include -#include "ITSReconstruction/FastMultEstConfig.h" -#include -#include - -namespace o2 -{ -namespace its -{ - -struct FastMultEst { - - static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; - - float mult = 0.; /// estimated signal clusters multipliciy at reference (1st?) layer - float noisePerChip = 0.; /// estimated or imposed noise per chip - float cov[3] = {0.}; /// covariance matrix of estimation - float chi2 = 0.; /// chi2 - int nLayersUsed = 0; /// number of layers actually used - uint32_t lastRandomSeed = 0; /// state of the gRandom before - - std::array nClPerLayer{0}; // measured N Cl per layer selectROFs - FastMultEst(); - - static uint32_t getCurrentRandomSeed(); - int selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel); - - void fillNClPerLayer(const gsl::span& clusters); - float process(const std::array ncl) - { - return FastMultEstConfig::Instance().imposeNoisePerChip > 0 ? processNoiseImposed(ncl) : processNoiseFree(ncl); - } - float processNoiseFree(const std::array ncl); - float processNoiseImposed(const std::array ncl); - float process(const gsl::span& clusters) - { - fillNClPerLayer(clusters); - return process(nClPerLayer); - } - static bool sSeedSet; - - ClassDefNV(FastMultEst, 1); -}; - -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h deleted file mode 100644 index b488b29fd676e..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file FastMultEstConfig.h -/// \brief Configuration parameters for ITS fast multiplicity estimator -/// \author ruben.shahoyan@cern.ch - -#ifndef ALICEO2_ITS_FASTMULTESTCONF_H_ -#define ALICEO2_ITS_FASTMULTESTCONF_H_ - -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/ConfigurableParamHelper.h" -#include "ITSMFTReconstruction/ChipMappingITS.h" - -namespace o2 -{ -namespace its -{ -struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper { - static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; - - /// acceptance correction per layer (relative to 1st one) - float accCorr[NLayers] = {1.f, 0.895, 0.825, 0.803, 0.720, 0.962, 0.911}; - int firstLayer = 3; /// 1st layer to account - int lastLayer = 6; /// last layer to account - float imposeNoisePerChip = 1.e-7 * 1024 * 512; // assumed noise, free parameter if<0 - - // cuts to reject to low or too high mult events - float cutMultClusLow = 0; /// reject ROF with estimated cluster mult. below this value (no cut if <0) - float cutMultClusHigh = -1; /// reject ROF with estimated cluster mult. above this value (no cut if <0) - float cutMultVtxLow = -1; /// reject seed vertex if its multiplicity below this value (no cut if <0) - float cutMultVtxHigh = -1; /// reject seed vertex if its multiplicity above this value (no cut if <0) - float cutRandomFraction = -1.; /// apply random cut rejecting requested fraction - int randomSeed = 0; /// 0 - do not seet seed, >0 : set as is, <0 : use current time - bool preferTriggered = true; /// prefer ROFs with highest number of physics triggers - - bool isMultCutRequested() const { return cutMultClusLow >= 0.f && cutMultClusHigh > 0.f; }; - bool isVtxMultCutRequested() const { return cutMultVtxLow >= 0.f && cutMultVtxHigh > 0.f; }; - bool isPassingRandomRejection() const; - bool isPassingMultCut(float mult) const { return mult >= cutMultClusLow && (mult <= cutMultClusHigh || cutMultClusHigh <= 0.f); } - bool isPassingVtxMultCut(int mult) const { return mult >= cutMultVtxLow && (mult <= cutMultVtxHigh || cutMultVtxHigh <= 0.f); } - - O2ParamDef(FastMultEstConfig, "fastMultConfig"); -}; - -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h index f9d3f1ae46752..a7d814f02d011 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h +++ b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h @@ -103,7 +103,7 @@ struct RecoGeomHelper { static constexpr float ladderWidth() { return o2::itsmft::SegmentationAlpide::SensorSizeRows; } static constexpr float ladderWidthInv() { return 1. / ladderWidth(); } - void init(); + void init(int minLayer = 0, int maxLayer = getNLayers()); void print() const; ClassDefNV(RecoGeomHelper, 0); diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h deleted file mode 100644 index 3eb218dc973f6..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TrivialVertexer.h -/// \brief Definition of the ITS trivial vertex finder -#ifndef ALICEO2_ITS_TRIVIALVERTEXER_H -#define ALICEO2_ITS_TRIVIALVERTEXER_H - -#include - -#include "Rtypes.h" // for TrivialVertexer::Class, Double_t, ClassDef, etc - -class TFile; -class TTree; -class FairMCEventHeader; - -namespace o2 -{ -namespace itsmft -{ -class Cluster; -} -} // namespace o2 - -namespace o2 -{ -class MCCompLabel; -namespace dataformats -{ -template -class MCTruthContainer; -} -namespace its -{ -class TrivialVertexer -{ - using Cluster = o2::itsmft::Cluster; - using Label = o2::MCCompLabel; - - public: - TrivialVertexer(); - ~TrivialVertexer(); - - TrivialVertexer(const TrivialVertexer&) = delete; - TrivialVertexer& operator=(const TrivialVertexer&) = delete; - - Bool_t openInputFile(const Char_t*); - - void process(const std::vector& clusters, std::vector>& vertices); - void setMCTruthContainer(const o2::dataformats::MCTruthContainer* truth) { mClsLabels = truth; } - - private: - const o2::dataformats::MCTruthContainer* mClsLabels = nullptr; // Cluster MC labels - - TFile* mFile = nullptr; - TTree* mTree = nullptr; - FairMCEventHeader* mHeader = nullptr; -}; -} // namespace its -} // namespace o2 - -#endif /* ALICEO2_ITS_TRIVIALVERTEXER_H */ diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx deleted file mode 100644 index fb4e4ac7b6fa2..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file ClustererTask.cxx -/// \brief Implementation of the ITS cluster finder task - -#include "DetectorsCommonDataFormats/DetID.h" -#include "ITSReconstruction/ClustererTask.h" -#include "MathUtils/Cartesian.h" -#include "MathUtils/Utils.h" -#include -#include -#include - -using namespace o2::its; - -//_____________________________________________________________________ -ClustererTask::ClustererTask(bool useMC, bool raw) : mRawDataMode(raw), - mUseMCTruth(useMC && (!raw)) -{ - LOG(info) << Class()->GetName() << ": MC digits mode: " << (mRawDataMode ? "OFF" : "ON") - << " | Use MCtruth: " << (mUseMCTruth ? "ON" : "OFF"); - - mClusterer.setNChips(o2::itsmft::ChipMappingITS::getNChips()); -} - -//_____________________________________________________________________ -ClustererTask::~ClustererTask() -{ - mCompClus.clear(); - mClsLabels.clear(); -} - -//_____________________________________________________________________ -void ClustererTask::Init() -{ - /// Inititializes the clusterer and connects input and output container - - if (mReader) { - return; // already initialized - } - - // create reader according to requested raw of MC mode - if (mRawDataMode) { - mReaderRaw = std::make_unique>(); - mReader = mReaderRaw.get(); - } else { // clusterizer of digits - mReaderMC = std::make_unique(); - mReader = mReaderMC.get(); - } - - mClusterer.print(); - - return; -} - -//_____________________________________________________________________ -void ClustererTask::run(const std::string inpName, const std::string outName) -{ - // standalone execution - Init(); // create reader, clusterer - - if (mRawDataMode) { - - mReaderRaw->openInput(inpName); - mClusterer.process(1, *mReaderRaw.get(), &mCompClus, &mPatterns, &mROFRecVec, nullptr); - - auto basename = outName.substr(0, outName.size() - sizeof("root")); - auto nFiles = int(mROFRecVec.size() / maxROframe); - int i = 0; - for (; i < nFiles; i++) { - writeTree(basename, i); - } - writeTree(basename, i); // The remainder - - } else { - - mReaderMC->openInput(inpName, o2::detectors::DetID("ITS")); - - TFile outFile(outName.data(), "new"); - if (!outFile.IsOpen()) { - LOG(fatal) << "Failed to open output file " << outName; - } - - TTree outTree("o2sim", "ITS Clusters"); - - auto compClusPtr = &mCompClus; - outTree.Branch("ITSClusterComp", &compClusPtr); - - auto rofRecVecPtr = &mROFRecVec; - outTree.Branch("ITSClustersROF", &rofRecVecPtr); - - auto clsLabelsPtr = &mClsLabels; - if (mUseMCTruth && mReaderMC->getDigitsMCTruth()) { - // digit labels are provided directly to clusterer - outTree.Branch("ITSClusterMCTruth", &clsLabelsPtr); - } else { - mUseMCTruth = false; - } - LOG(info) << Class()->GetName() << " | MCTruth: " << (mUseMCTruth ? "ON" : "OFF"); - - outTree.Branch("ITSClusterPatt", &mPatterns); - - std::vector mc2rof, *mc2rofPtr = &mc2rof; - if (mUseMCTruth) { - auto mc2rofOrig = mReaderMC->getMC2ROFRecords(); - mc2rof.reserve(mc2rofOrig.size()); - for (const auto& m2r : mc2rofOrig) { // clone from the span - mc2rof.push_back(m2r); - } - outTree.Branch("ITSClustersMC2ROF", mc2rofPtr); - } - - // loop over entries of the input tree - while (mReaderMC->readNextEntry()) { - mClusterer.process(1, *mReaderMC.get(), &mCompClus, &mPatterns, &mROFRecVec, &mClsLabels); - } - - outTree.Fill(); - outTree.Write(); - } - - mClusterer.clear(); -} - -void ClustererTask::writeTree(std::string basename, int i) -{ - auto name = basename + std::to_string(i) + ".root"; - TFile outFile(name.data(), "new"); - if (!outFile.IsOpen()) { - LOG(fatal) << "Failed to open output file " << name; - } - TTree outTree("o2sim", "ITS Clusters"); - - size_t max = (i + 1) * maxROframe; - auto lastf = (max < mROFRecVec.size()) ? mROFRecVec.begin() + max : mROFRecVec.end(); - std::vector rofRecBuffer(mROFRecVec.begin() + i * maxROframe, lastf); - std::vector* rofRecPtr = &rofRecBuffer; - outTree.Branch("ITSClustersROF", rofRecPtr); - - auto first = rofRecBuffer[0].getFirstEntry(); - auto last = rofRecBuffer.back().getFirstEntry() + rofRecBuffer.back().getNEntries(); - - std::vector compClusBuffer, *compClusPtr = &compClusBuffer; - compClusBuffer.assign(&mCompClus[first], &mCompClus[last]); - outTree.Branch("ITSClusterComp", &compClusPtr); - outTree.Branch("ITSClusterPatt", &mPatterns); - - for (auto& rof : rofRecBuffer) { - rof.setFirstEntry(rof.getFirstEntry() - first); - } - - outTree.Fill(); - outTree.Write(); -} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx deleted file mode 100644 index 81087744b04a9..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITSReconstruction/CookedConfigParam.h" - -namespace o2 -{ -namespace its -{ -static auto& sITSCookedTrackerParam = o2::its::CookedConfigParam::Instance(); - -O2ParamImpl(o2::its::CookedConfigParam); -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx deleted file mode 100644 index e68d26a4d12d3..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx +++ /dev/null @@ -1,865 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CookedTracker.cxx -/// \brief Implementation of the "Cooked Matrix" ITS tracker -/// \author iouri.belikov@cern.ch - -//------------------------------------------------------------------------- -// A stand-alone ITS tracker -// The pattern recongintion based on the "cooked covariance" approach -//------------------------------------------------------------------------- -#include -#include -#include - -#include -#include - -#include - -#include "CommonConstants/MathConstants.h" -#include "DetectorsBase/Propagator.h" -#include "Field/MagneticField.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "ITSReconstruction/CookedTracker.h" -#include "MathUtils/Utils.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::its; -using namespace o2::itsmft; -using namespace o2::constants::math; -using o2::field::MagneticField; -using Label = o2::MCCompLabel; - -/*** Tracking parameters ***/ -// seed "windows" in z and phi: makeSeeds -Float_t CookedTracker::gzWin = 0.33; -Float_t CookedTracker::gminPt = 0.05; -Float_t CookedTracker::mMostProbablePt = o2::track::kMostProbablePt; -// Maximal accepted impact parameters for the seeds -Float_t CookedTracker::gmaxDCAxy = 3.; -Float_t CookedTracker::gmaxDCAz = 3.; -// Layers for the seeding -Int_t CookedTracker::gSeedingLayer1 = 6; -Int_t CookedTracker::gSeedingLayer2 = 4; -Int_t CookedTracker::gSeedingLayer3 = 5; -// Space point resolution -Float_t CookedTracker::gSigma2 = 0.0005 * 0.0005; -// Max accepted chi2 -Float_t CookedTracker::gmaxChi2PerCluster = 20.; -Float_t CookedTracker::gmaxChi2PerTrack = 30.; -// Tracking "road" from layer to layer -Float_t CookedTracker::gRoadY = 0.2; -Float_t CookedTracker::gRoadZ = 0.3; -// Minimal number of attached clusters -Int_t CookedTracker::gminNumberOfClusters = 4; - -const float kPI = 3.14159f; -const float k2PI = 2 * kPI; - -//************************************************ -// TODO: -//************************************************ -// Seeding: -// Precalculate cylidnrical (r,phi) for the clusters; -// use exact r's for the clusters - -CookedTracker::Layer CookedTracker::sLayers[CookedTracker::kNLayers]; - -CookedTracker::CookedTracker(Int_t n) : mNumOfThreads(n), mBz(0.) -{ - //-------------------------------------------------------------------- - // This default constructor needs to be provided - //-------------------------------------------------------------------- - const Double_t klRadius[7] = {2.34, 3.15, 3.93, 19.61, 24.55, 34.39, 39.34}; // tdr6 - - for (Int_t i = 0; i < kNLayers; i++) { - sLayers[i].setR(klRadius[i]); - } -} - -//__________________________________________________________________________ -Label CookedTracker::cookLabel(TrackITSExt& t, Float_t wrong) const -{ - //-------------------------------------------------------------------- - // This function "cooks" a track label. - // A label<0 indicates that some of the clusters are wrongly assigned. - //-------------------------------------------------------------------- - Int_t noc = t.getNumberOfClusters(); - std::map labelOccurence; - - for (int i = noc; i--;) { - const Cluster* c = getCluster(t.getClusterIndex(i)); - Int_t idx = c - &mClusterCache[0] + mFirstInFrame; // Index of this cluster in event - auto labels = mClsLabels->getLabels(idx); - - for (auto lab : labels) { // check all labels of the cluster - if (lab.isEmpty()) { - break; // all following labels will be empty also - } - // was this label already accounted for ? - labelOccurence[lab]++; - } - } - Label lab; - Int_t maxL = 0; // find most encountered label - for (auto [label, count] : labelOccurence) { - if (count <= maxL) { - continue; - } - maxL = count; - lab = label; - } - - if ((1. - Float_t(maxL) / noc) > wrong) { - // change the track ID to negative - lab.setFakeFlag(); - } - // t.SetFakeRatio((1.- Float_t(maxL)/noc)); - return lab; -} - -Double_t CookedTracker::getBz() const -{ - return mBz; -} - -static Double_t f1(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t x3, Double_t y3) -{ - //----------------------------------------------------------------- - // Initial approximation of the track curvature - //----------------------------------------------------------------- - Double_t d = (x2 - x1) * (y3 - y2) - (x3 - x2) * (y2 - y1); - Double_t a = - 0.5 * ((y3 - y2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1) - (y2 - y1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2)); - Double_t b = - 0.5 * ((x2 - x1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2) - (x3 - x2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1)); - - Double_t xr = TMath::Abs(d / (d * x1 - a)), yr = TMath::Abs(d / (d * y1 - b)); - - Double_t crv = xr * yr / sqrt(xr * xr + yr * yr); - if (d > 0) { - crv = -crv; - } - - return crv; -} - -static Double_t f2(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t x3, Double_t y3) -{ - //----------------------------------------------------------------- - // Initial approximation of the x-coordinate of the center of curvature - //----------------------------------------------------------------- - - Double_t k1 = (y2 - y1) / (x2 - x1), k2 = (y3 - y2) / (x3 - x2); - Double_t x0 = 0.5 * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1); - - return x0; -} - -static Double_t f3(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t z1, Double_t z2) -{ - //----------------------------------------------------------------- - // Initial approximation of the tangent of the track dip angle - //----------------------------------------------------------------- - return (z1 - z2) / sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); -} - -o2::its::TrackITSExt CookedTracker::cookSeed(const Point3Df& r1, Point3Df& r2, const Point3Df& tr3, float rad2, float rad3, float_t alpha, float_t bz) -// const Float_t r1[4], const Float_t r2[4], const Float_t tr3[4], Double_t alpha, Double_t bz) -{ - //-------------------------------------------------------------------- - // This is the main cooking function. - // Creates seed parameters out of provided clusters. - //-------------------------------------------------------------------- - - Double_t ca = TMath::Cos(alpha), sa = TMath::Sin(alpha); - Double_t x1 = r1.X() * ca + r1.Y() * sa, y1 = -r1.X() * sa + r1.Y() * ca, z1 = r1.Z(); - Double_t x2 = r2.X() * ca + r2.Y() * sa, y2 = -r2.X() * sa + r2.Y() * ca, z2 = r2.Z(); - Double_t x3 = tr3.X(), y3 = tr3.Y(), z3 = tr3.Z(); - - std::array par; - par[0] = y3; - par[1] = z3; - Double_t crv = f1(x1, y1, x2, y2, x3, y3); // curvature - Double_t x0 = f2(x1, y1, x2, y2, x3, y3); // x-coordinate of the center - Double_t tgl12 = f3(x1, y1, x2, y2, z1, z2); - Double_t tgl23 = f3(x2, y2, x3, y3, z2, z3); - - Double_t sf = crv * (x3 - x0); // FIXME: sf must never be >= kAlmost1 - par[2] = sf; - - par[3] = 0.5 * (tgl12 + tgl23); - par[4] = (TMath::Abs(bz) < Almost0) ? 1 / CookedTracker::getMostProbablePt() : crv / (bz * B2C); - - std::array cov; - /* - for (Int_t i=0; i<15; i++) cov[i]=0.; - cov[0] =gSigma2*10; - cov[2] =gSigma2*10; - cov[5] =0.007*0.007*10; //FIXME all these lines - cov[9] =0.007*0.007*10; - cov[14]=0.1*0.1*10; - */ - const Double_t dlt = 0.0005; - Double_t fy = 1. / (rad2 - rad3); - Double_t tz = fy; - const auto big = sqrt(o2::constants::math::VeryBig); - auto cy = big; - if (TMath::Abs(bz) >= Almost0) { - auto tmp = dlt * bz * B2C; - cy = (f1(x1, y1, x2, y2 + dlt, x3, y3) - crv) / tmp; - cy *= 20; // FIXME: MS contribution to the cov[14] - } - Double_t s2 = gSigma2; - - cov[0] = s2; - cov[1] = 0.; - cov[2] = s2; - cov[3] = s2 * fy; - cov[4] = 0.; - cov[5] = s2 * fy * fy; - cov[6] = 0.; - cov[7] = s2 * tz; - cov[8] = 0.; - cov[9] = s2 * tz * tz; - cov[10] = s2 * cy; - cov[11] = 0.; - cov[12] = s2 * fy * cy; - cov[13] = 0.; - cov[14] = s2 * cy * cy; - - return o2::its::TrackITSExt(x3, alpha, par, cov); -} - -void CookedTracker::makeSeeds(std::vector& seeds, Int_t first, Int_t last) -{ - //-------------------------------------------------------------------- - // This is the main pattern recongition function. - // Creates seeds out of two clusters and another point. - //-------------------------------------------------------------------- - const float zv = getZ(); - - Layer& layer1 = sLayers[gSeedingLayer1]; - Layer& layer2 = sLayers[gSeedingLayer2]; - Layer& layer3 = sLayers[gSeedingLayer3]; - - auto bz = getBz(); - const Double_t maxC = (TMath::Abs(bz) < Almost0) ? 0.03 : TMath::Abs(bz * B2C / gminPt); - const Double_t kpWinC = TMath::ASin(0.5 * maxC * layer1.getR()) - TMath::ASin(0.5 * maxC * layer2.getR()); - const Double_t kpWinD = 2 * (TMath::ASin(gmaxDCAxy / layer2.getR()) - TMath::ASin(gmaxDCAxy / layer1.getR())); - const Double_t kpWin = std::max(kpWinC, kpWinD); - - for (Int_t n1 = first; n1 < last; n1++) { - const Cluster* c1 = layer1.getCluster(n1); - // - //auto lab = (mClsLabels->getLabels(c1 - &mClusterCache[0] + mFirstInFrame))[0]; - // - auto xyz1 = c1->getXYZGloRot(*mGeom); - auto z1 = xyz1.Z(); - auto r1 = xyz1.rho(); - - auto phi1 = layer1.getClusterPhi(n1); - auto tgl = std::abs((z1 - zv) / r1); - - auto zr2 = zv + layer2.getR() / r1 * (z1 - zv); - auto phir2 = phi1; - auto dz2 = gzWin * (1 + 2 * tgl); - - std::vector selected2; - float dy2 = kpWin * layer2.getR(); - layer2.selectClusters(selected2, phir2, dy2, zr2, dz2); - for (auto n2 : selected2) { - const Cluster* c2 = layer2.getCluster(n2); - // - //if ((mClsLabels->getLabels(c2 - &mClusterCache[0] + mFirstInFrame))[0] != lab) continue; - // - auto xyz2 = c2->getXYZGloRot(*mGeom); - auto z2 = xyz2.Z(); - auto r2 = xyz2.rho(); - - auto dx = xyz2.X() - xyz1.X(), dy = xyz2.Y() - xyz1.Y(); - auto d = (dx * xyz1.Y() - dy * xyz1.X()) / TMath::Sqrt(dx * dx + dy * dy); - auto phir3 = phi1 + TMath::ASin(d / r1) - TMath::ASin(d / layer3.getR()); - - auto zr3 = z1 + (layer3.getR() - r1) / (r2 - r1) * (z2 - z1); - auto dz3 = 0.5f * dz2; - - std::vector selected3; - float dy3 = 0.1 * kpWin * layer3.getR(); //Fixme - layer3.selectClusters(selected3, phir3, dy3, zr3, dz3); - for (auto n3 : selected3) { - const Cluster* c3 = layer3.getCluster(n3); - // - //if ((mClsLabels->getLabels(c3 - &mClusterCache[0] + mFirstInFrame))[0] != lab) continue; - // - auto xyz3 = c3->getXYZGloRot(*mGeom); - auto z3 = xyz3.Z(); - auto r3 = xyz3.rho(); - - zr3 = z1 + (r3 - r1) / (r2 - r1) * (z2 - z1); - if (std::abs(z3 - zr3) > 0.2 * dz3) { - continue; - } - - const Point3Df& txyz2 = c2->getXYZ(); // tracking coordinates - - TrackITSExt seed = cookSeed(xyz1, xyz3, txyz2, layer2.getR(), layer3.getR(), layer2.getAlphaRef(n2), getBz()); - - float ip[2]; - seed.getImpactParams(getX(), getY(), getZ(), getBz(), ip); - if (TMath::Abs(ip[0]) > gmaxDCAxy) { - continue; - } - if (TMath::Abs(ip[1]) > gmaxDCAz) { - continue; - } - { - Double_t xx0 = 0.008; // Rough layer thickness - Double_t radl = 9.36; // Radiation length of Si [cm] - Double_t rho = 2.33; // Density of Si [g/cm^3] - if (!seed.correctForMaterial(xx0, xx0 * radl * rho, kTRUE)) { - continue; - } - } - seed.setClusterIndex(gSeedingLayer1, n1); - seed.setClusterIndex(gSeedingLayer3, n3); - seed.setClusterIndex(gSeedingLayer2, n2); - seeds.push_back(seed); - } - } - } - /* - for (Int_t n1 = 0; n1 < nClusters1; n1++) { - Cluster* c1 = layer1.getCluster(n1); - ((Cluster*)c1)->goToFrameTrk(); - } - for (Int_t n2 = 0; n2 < nClusters2; n2++) { - Cluster* c2 = layer2.getCluster(n2); - ((Cluster*)c2)->goToFrameTrk(); - } - for (Int_t n3 = 0; n3 < nClusters3; n3++) { - Cluster* c3 = layer3.getCluster(n3); - ((Cluster*)c3)->goToFrameTrk(); - } - */ -} - -void CookedTracker::trackSeeds(std::vector& seeds) -{ - //-------------------------------------------------------------------- - // Loop over a subset of track seeds - //-------------------------------------------------------------------- - std::vector used[gSeedingLayer2]; - std::vector selec[gSeedingLayer2]; - for (Int_t l = gSeedingLayer2 - 1; l >= 0; l--) { - Int_t n = sLayers[l].getNumberOfClusters(); - used[l].resize(n, false); - selec[l].reserve(n / 100); - } - - for (auto& track : seeds) { - auto x = track.getX(); - auto y = track.getY(); - Float_t phi = track.getAlpha() + TMath::ATan2(y, x); - o2::math_utils::bringTo02Pi(phi); - float ip[2]; - track.getImpactParams(getX(), getY(), getZ(), getBz(), ip); - - auto z = track.getZ(); - auto crv = track.getCurvature(getBz()); - auto tgl = track.getTgl(); - Float_t r1 = sLayers[gSeedingLayer2].getR(); - - for (Int_t l = gSeedingLayer2 - 1; l >= 0; l--) { - Float_t r2 = sLayers[l].getR(); - selec[l].clear(); - if (TMath::Abs(ip[0]) > r2) { - break; - } - if (TMath::Abs(crv) < gRoadY / (0.5 * r1 * 0.5 * r1)) { - phi += TMath::ASin(ip[0] / r2) - TMath::ASin(ip[0] / r1); - z += tgl * (TMath::Sqrt(r2 * r2 - ip[0] * ip[0]) - TMath::Sqrt(r1 * r1 - ip[0] * ip[0])); - } else { // Fixme - phi += 0.5 * crv * (r2 - r1); - z += tgl / (0.5 * crv) * (TMath::ASin(0.5 * crv * r2) - TMath::ASin(0.5 * crv * r1)); - } - sLayers[l].selectClusters(selec[l], phi, gRoadY, z, gRoadZ * (1 + 2 * std::abs(tgl))); - r1 = r2; - } - - TrackITSExt best(track); - - Int_t volID = -1; - for (auto& ci3 : selec[3]) { - TrackITSExt t3(track); - if (used[3][ci3]) { - continue; - } - if (!attachCluster(volID, 3, ci3, t3, track)) { - continue; - } - if (t3.isBetter(best, gmaxChi2PerTrack)) { - best = t3; - } - - for (auto& ci2 : selec[2]) { - TrackITSExt t2(t3); - if (used[2][ci2]) { - continue; - } - if (!attachCluster(volID, 2, ci2, t2, t3)) { - continue; - } - if (t2.isBetter(best, gmaxChi2PerTrack)) { - best = t2; - } - - for (auto& ci1 : selec[1]) { - TrackITSExt t1(t2); - if (used[1][ci1]) { - continue; - } - if (!attachCluster(volID, 1, ci1, t1, t2)) { - continue; - } - if (t1.isBetter(best, gmaxChi2PerTrack)) { - best = t1; - } - - for (auto& ci0 : selec[0]) { - TrackITSExt t0(t1); - if (used[0][ci0]) { - continue; - } - if (!attachCluster(volID, 0, ci0, t0, t1)) { - continue; - } - if (t0.isBetter(best, gmaxChi2PerTrack)) { - best = t0; - } - volID = -1; - } - } - } - } - - if (best.getNumberOfClusters() >= gminNumberOfClusters) { - Int_t noc = best.getNumberOfClusters(); - for (Int_t ic = 3; ic < noc; ic++) { - Int_t index = best.getClusterIndex(ic); - Int_t l = (index & 0xf0000000) >> 28, c = (index & 0x0fffffff); - used[l][c] = true; - } - } - track = best; - } -} - -std::vector CookedTracker::trackInThread(Int_t first, Int_t last) -{ - //-------------------------------------------------------------------- - // This function is passed to a tracking thread - //-------------------------------------------------------------------- - std::vector seeds; - seeds.reserve(last - first + 1); - - for (const auto& vtx : *mVertices) { - mX = vtx.getX(); - mY = vtx.getY(); - mZ = vtx.getZ(); - makeSeeds(seeds, first, last); - } - - std::sort(seeds.begin(), seeds.end()); - - trackSeeds(seeds); - - makeBackPropParam(seeds); - - return seeds; -} - -void CookedTracker::process(gsl::span const& clusters, - gsl::span::iterator& pattIt, - const o2::itsmft::TopologyDictionary* dict, - TrackInserter& inserter, - o2::itsmft::ROFRecord& rof) -{ - //-------------------------------------------------------------------- - // This is the main tracking function - //-------------------------------------------------------------------- - if (mVertices == nullptr || mVertices->empty()) { - LOG(info) << "Not a single primary vertex provided. Skipping...\n"; - return; - } - LOG(info) << "\n CookedTracker::process(), number of threads: " << mNumOfThreads; - - auto start = std::chrono::system_clock::now(); - - mFirstInFrame = rof.getFirstEntry(); - - mClusterCache.reserve(rof.getNEntries()); - auto clusters_in_frame = rof.getROFData(clusters); - for (const auto& comp : clusters_in_frame) { - - auto pattID = comp.getPatternID(); - o2::math_utils::Point3D locXYZ; - float sigmaY2 = gSigma2, sigmaZ2 = gSigma2; - if (pattID != itsmft::CompCluster::InvalidPatternID) { - sigmaY2 = gSigma2; //dict.getErr2X(pattID); - sigmaZ2 = gSigma2; //dict.getErr2Z(pattID); - if (!dict->isGroup(pattID)) { - locXYZ = dict->getClusterCoordinates(comp); - } else { - o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict->getClusterCoordinates(comp, patt); - } - } else { - o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict->getClusterCoordinates(comp, patt, false); - } - auto sensorID = comp.getSensorID(); - // Inverse transformation to the local --> tracking - auto trkXYZ = mGeom->getMatrixT2L(sensorID) ^ locXYZ; - - Cluster c; - c.setSensorID(sensorID); - c.setPos(trkXYZ); - c.setErrors(sigmaY2, sigmaZ2, 0.f); - mClusterCache.push_back(c); - } - - auto nClFrame = loadClusters(); - - auto end = std::chrono::system_clock::now(); - std::chrono::duration diff = end - start; - LOG(info) << "Loading clusters: " << nClFrame << " in a single frame : " << diff.count() << " s"; - - start = end; - - auto [first, number] = processLoadedClusters(inserter); - rof.setFirstEntry(first); - rof.setNEntries(number); - - unloadClusters(); - end = std::chrono::system_clock::now(); - diff = end - start; - LOG(info) << "Processing time/clusters for single frame : " << diff.count() << " / " << nClFrame << " s"; - - start = end; -} - -std::tuple CookedTracker::processLoadedClusters(TrackInserter& inserter) -{ - //-------------------------------------------------------------------- - // This is the main tracking function for single frame, it is assumed that only clusters - // which may contribute to this frame is loaded - //-------------------------------------------------------------------- - Int_t numOfClusters = sLayers[gSeedingLayer1].getNumberOfClusters(); - if (!numOfClusters) { - return {0, 0}; - } - - std::vector>> futures(mNumOfThreads); - std::vector> seedArray(mNumOfThreads); - - for (Int_t t = 0, first = 0; t < mNumOfThreads; t++) { - Int_t rem = t < (numOfClusters % mNumOfThreads) ? 1 : 0; - Int_t last = first + (numOfClusters / mNumOfThreads) + rem; - futures[t] = std::async(std::launch::async, &CookedTracker::trackInThread, this, first, last); - first = last; - } - Int_t nSeeds = 0, ngood = 0; - int nAllTracks = 0, nTracks = 0; - for (Int_t t = 0; t < mNumOfThreads; t++) { - seedArray[t] = futures[t].get(); - nSeeds += seedArray[t].size(); - for (auto& track : seedArray[t]) { - if (track.getNumberOfClusters() < gminNumberOfClusters) { - continue; - } - - o2::dataformats::VertexBase vtx; - track.propagateToDCA(vtx, getBz()); - - nAllTracks = inserter(track); - nTracks++; - if (mTrkLabels) { - Label label = cookLabel(track, 0.); // For comparison only - if (label.getTrackID() >= 0) { - ngood++; - } - // the inserter returns the size of the track vector, the index of the last - // inserted track is thus n - 1 - mTrkLabels->emplace_back(label); - } - } - } - - if (nSeeds) { - LOG(info) << "Found tracks: " << nTracks; - LOG(info) << "CookedTracker::processLoadedClusters(), good_tracks:/seeds: " << ngood << '/' << nSeeds << "-> " - << Float_t(ngood) / nSeeds << '\n'; - } - // returning index of the first track and the number of add tracks - // inserted in this call - return {nAllTracks - nTracks, nTracks}; -} - -//____________________________________________________________ -void CookedTracker::makeBackPropParam(std::vector& seeds) const -{ - // refit in backward direction - for (auto& track : seeds) { - if (track.getNumberOfClusters() < gminNumberOfClusters) { - continue; - } - makeBackPropParam(track); - } -} - -//____________________________________________________________ -bool CookedTracker::makeBackPropParam(TrackITSExt& track) const -{ - // refit in backward direction - auto backProp = track.getParamOut(); - backProp = track; - backProp.resetCovariance(); - auto propagator = o2::base::Propagator::Instance(); - - Int_t noc = track.getNumberOfClusters(); - for (int ic = noc; ic--;) { // cluster indices are stored in inward direction - Int_t index = track.getClusterIndex(ic); - const Cluster* c = getCluster(index); - float alpha = mGeom->getSensorRefAlpha(c->getSensorID()); - if (!backProp.rotate(alpha)) { - return false; - } - if (!propagator->PropagateToXBxByBz(backProp, c->getX())) { - return false; - } - if (!backProp.update(static_cast&>(*c))) { - return false; - } - } - track.getParamOut() = backProp; - return true; -} - -int CookedTracker::loadClusters() -{ - //-------------------------------------------------------------------- - // This function reads the ITSU clusters from the tree, - // sort them, distribute over the internal tracker arrays, etc - //-------------------------------------------------------------------- - - if (mClusterCache.empty()) { - return 0; - } - - for (const auto& c : mClusterCache) { - Int_t layer = mGeom->getLayer(c.getSensorID()); - sLayers[layer].insertCluster(&c); - } - - std::vector> fut; - for (Int_t l = 0; l < kNLayers; l += mNumOfThreads) { - for (Int_t t = 0; t < mNumOfThreads; t++) { - if (l + t >= kNLayers) { - break; - } - auto f = std::async(std::launch::async, &CookedTracker::Layer::init, sLayers + (l + t)); - fut.push_back(std::move(f)); - } - for (size_t t = 0; t < fut.size(); t++) { - fut[t].wait(); - } - } - - return mClusterCache.size(); -} - -void CookedTracker::unloadClusters() -{ - //-------------------------------------------------------------------- - // This function unloads ITSU clusters from the RAM - //-------------------------------------------------------------------- - mClusterCache.clear(); - for (Int_t i = 0; i < kNLayers; i++) { - sLayers[i].unloadClusters(); - } -} - -const Cluster* CookedTracker::getCluster(Int_t index) const -{ - //-------------------------------------------------------------------- - // Return pointer to a given cluster - //-------------------------------------------------------------------- - Int_t l = (index & 0xf0000000) >> 28; - Int_t c = (index & 0x0fffffff) >> 00; - return sLayers[l].getCluster(c); -} - -CookedTracker::Layer::Layer() : mR(0) -{ - //-------------------------------------------------------------------- - // This default constructor needs to be provided - //-------------------------------------------------------------------- -} - -void CookedTracker::Layer::init() -{ - //-------------------------------------------------------------------- - // Sort clusters and cache their reference plane info in a thread - //-------------------------------------------------------------------- - std::sort(std::begin(mClusters), std::end(mClusters), - [](const Cluster* c1, const Cluster* c2) { return (c1->getZ() < c2->getZ()); }); - - Double_t r = 0.; - Int_t m = mClusters.size(); - for (Int_t i = 0; i < m; i++) { - const Cluster* c = mClusters[i]; - // Float_t xRef, aRef; - // mGeom->getSensorXAlphaRefPlane(c->getSensorID(),xRef, aRef); - mAlphaRef.push_back(mGeom->getSensorRefAlpha(c->getSensorID())); - auto xyz = c->getXYZGloRot(*mGeom); - r += xyz.rho(); - Float_t phi = xyz.Phi(); - o2::math_utils::bringTo02Pi(phi); - mPhi.push_back(phi); - Int_t s = phi * kNSectors / k2PI; - mSectors[s < kNSectors ? s : kNSectors - 1].emplace_back(i, c->getZ()); - } - - if (m) { - mR = r / m; - } -} - -void CookedTracker::Layer::unloadClusters() -{ - //-------------------------------------------------------------------- - // Unload clusters from this layer - //-------------------------------------------------------------------- - mClusters.clear(); - mAlphaRef.clear(); - mPhi.clear(); - for (Int_t s = 0; s < kNSectors; s++) { - mSectors[s].clear(); - } -} - -Bool_t CookedTracker::Layer::insertCluster(const Cluster* c) -{ - //-------------------------------------------------------------------- - // This function inserts a cluster to this layer - //-------------------------------------------------------------------- - mClusters.push_back(c); - return kTRUE; -} - -Int_t CookedTracker::Layer::findClusterIndex(Float_t z) const -{ - //-------------------------------------------------------------------- - // This function returns the index of the first cluster with its fZ >= "z". - //-------------------------------------------------------------------- - auto found = std::upper_bound(std::begin(mClusters), std::end(mClusters), z, - [](Float_t zc, const Cluster* c) { return (zc < c->getZ()); }); - return found - std::begin(mClusters); -} - -void CookedTracker::Layer::selectClusters(std::vector& selec, Float_t phi, Float_t dy, Float_t z, Float_t dz) -{ - //-------------------------------------------------------------------- - // This function selects clusters within the "road" - //-------------------------------------------------------------------- - Float_t zMin = z - dz; - Float_t zMax = z + dz; - - o2::math_utils::bringTo02Pi(phi); - - Float_t dphi = dy / mR; - - int smin = (phi - dphi) / k2PI * kNSectors; - int ds = (phi + dphi) / k2PI * kNSectors - smin + 1; - - smin = (smin + kNSectors) % kNSectors; - - for (int is = 0; is < ds; is++) { - Int_t s = (smin + is) % kNSectors; - - auto cmp = [](Float_t zc, std::pair p) { return (zc < p.second); }; - auto imin = std::upper_bound(std::begin(mSectors[s]), std::end(mSectors[s]), zMin, cmp); - auto imax = std::upper_bound(imin, std::end(mSectors[s]), zMax, cmp); - for (; imin != imax; imin++) { - auto [i, zz] = *imin; - auto cdphi = std::abs(mPhi[i] - phi); - if (cdphi > dphi) { - if (cdphi > kPI) { - cdphi = k2PI - cdphi; - } - if (cdphi > dphi) { - continue; // check in Phi - } - } - selec.push_back(i); - } - } -} - -Bool_t CookedTracker::attachCluster(Int_t& volID, Int_t nl, Int_t ci, TrackITSExt& t, const TrackITSExt& o) const -{ - //-------------------------------------------------------------------- - // Try to attach a clusters with index ci to running track hypothesis - //-------------------------------------------------------------------- - Layer& layer = sLayers[nl]; - const Cluster* c = layer.getCluster(ci); - - Int_t vid = c->getSensorID(); - - if (vid != volID) { - volID = vid; - t = o; - Double_t alpha = layer.getAlphaRef(ci); - if (!t.propagate(alpha, c->getX(), getBz())) { - return kFALSE; - } - } - - Double_t chi2 = t.getPredictedChi2(*c); - if (chi2 > gmaxChi2PerCluster) { - return kFALSE; - } - - if (!t.update(*c, chi2)) { - return kFALSE; - } - t.setClusterIndex(nl, ci); - - Double_t xx0 = (nl > 2) ? 0.008 : 0.003; // Rough layer thickness - Double_t x0 = 9.36; // Radiation length of Si [cm] - Double_t rho = 2.33; // Density of Si [g/cm^3] - t.correctForMaterial(xx0, xx0 * x0 * rho, kTRUE); - return kTRUE; -} - -void CookedTracker::setGeometry(o2::its::GeometryTGeo* geom) -{ - /// attach geometry interface - mGeom = geom; - for (Int_t i = 0; i < kNLayers; i++) { - sLayers[i].setGeometry(geom); - } -} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h b/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h deleted file mode 100644 index 1e8596fbb224c..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifdef __CLING__ - -#pragma link off all globals; -#pragma link off all classes; -#pragma link off all functions; - -#pragma link C++ class o2::its::ClustererTask + ; -#pragma link C++ class o2::its::CookedTracker + ; -#pragma link C++ class o2::its::CookedConfigParam + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::CookedConfigParam> + ; -#pragma link C++ class o2::its::RecoGeomHelper + ; -#pragma link C++ class o2::its::FastMultEst + ; -#pragma link C++ class o2::its::FastMultEstConfig + ; - -#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx deleted file mode 100644 index 68caae8e072ac..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file FastMultEst.h -/// \brief Fast multiplicity estimator for ITS -/// \author ruben.shahoyan@cern.ch - -#include "ITSReconstruction/FastMultEst.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "Framework/Logger.h" -#include -#include -#include - -using namespace o2::its; - -bool FastMultEst::sSeedSet = false; - -///______________________________________________________ -FastMultEst::FastMultEst() -{ - if (!sSeedSet && FastMultEstConfig::Instance().cutRandomFraction > 0.f) { - sSeedSet = true; - if (FastMultEstConfig::Instance().randomSeed > 0) { - gRandom->SetSeed(FastMultEstConfig::Instance().randomSeed); - } else if (FastMultEstConfig::Instance().randomSeed < 0) { - gRandom->SetSeed(std::time(nullptr) % 0xffff); - } - } -} - -///______________________________________________________ -/// find multiplicity for given set of clusters -void FastMultEst::fillNClPerLayer(const gsl::span& clusters) -{ - int lr = FastMultEst::NLayers - 1, nchAcc = o2::itsmft::ChipMappingITS::getNChips() - o2::itsmft::ChipMappingITS::getNChipsPerLr(lr); - std::memset(&nClPerLayer[0], 0, sizeof(int) * FastMultEst::NLayers); - for (int i = clusters.size(); i--;) { // profit from clusters being ordered in chip increasing order - while (clusters[i].getSensorID() < nchAcc) { - assert(lr >= 0); - nchAcc -= o2::itsmft::ChipMappingITS::getNChipsPerLr(--lr); - } - nClPerLayer[lr]++; - } -} - -///______________________________________________________ -/// find multiplicity for given number of clusters per layer -float FastMultEst::processNoiseFree(const std::array ncl) -{ - // we assume that on the used layers the observed number of clusters is defined by the - // the noise ~ nu * Nchips and contribution from the signal tracks Ntr*mAccCorr - const auto& conf = FastMultEstConfig::Instance(); - - float mat[3] = {0}, b[2] = {0}; - nLayersUsed = 0; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - int nch = o2::itsmft::ChipMappingITS::getNChipsPerLr(il); - float err2i = 1. / ncl[il]; - float m2n = nch * err2i; - mat[0] += err2i * conf.accCorr[il] * conf.accCorr[il]; - mat[2] += nch * m2n; - mat[1] += conf.accCorr[il] * m2n; // non-diagonal element - b[0] += conf.accCorr[il]; - b[1] += nch; - nLayersUsed++; - } - } - mult = noisePerChip = chi2 = -1; - float det = mat[0] * mat[2] - mat[1] * mat[1]; - if (nLayersUsed < 2 || std::abs(det) < 1e-15) { - return -1; - } - float detI = 1. / det; - mult = detI * (b[0] * mat[2] - b[1] * mat[1]); - noisePerChip = detI * (b[1] * mat[0] - b[0] * mat[1]); - cov[0] = mat[2] * detI; - cov[2] = mat[0] * detI; - cov[1] = -mat[1] * detI; - chi2 = 0.; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - int nch = o2::itsmft::ChipMappingITS::getNChipsPerLr(il); - float diff = mult * conf.accCorr[il] + nch * noisePerChip - ncl[il]; - chi2 += diff * diff / ncl[il]; - } - } - chi2 = nLayersUsed > 2 ? chi2 / (nLayersUsed - 2) : 0.; - return mult > 0 ? mult : 0; -} - -///______________________________________________________ -/// find multiplicity for given number of clusters per layer with mean noise imposed -float FastMultEst::processNoiseImposed(const std::array ncl) -{ - // we assume that on the used layers the observed number of clusters is defined by the - // the noise ~ nu * Nchips and contribution from the signal tracks Ntr*conf.accCorr - // - // minimize the form sum_lr (noise_i - mu nchips_i)^2 / (mu nchips_i) + lambda_i * (noise_i + mult*acc_i - ncl_i) - // whith noise_i being estimate of the noise clusters in nchips_i of layer i, mu is the mean noise per chip, - // mult is the number of signal clusters on the ref. (1st) layer and the acc_i is the acceptance of layer i wrt 1st. - // The lambda_i is hust a Lagrange multiplier. - - const auto& conf = FastMultEstConfig::Instance(); - float w2sum = 0., wnsum = 0., wsum = 0.; - nLayersUsed = 0; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - float nchInv = 1. / o2::itsmft::ChipMappingITS::getNChipsPerLr(il); - w2sum += conf.accCorr[il] * conf.accCorr[il] * nchInv; - wnsum += ncl[il] * nchInv * conf.accCorr[il]; - wsum += conf.accCorr[il]; - nLayersUsed++; - } - } - mult = 0; - chi2 = -1; - noisePerChip = conf.imposeNoisePerChip; - if (nLayersUsed < 1) { - return -1; - } - auto w2sumI = 1. / w2sum; - mult = (wnsum - noisePerChip * wsum) * w2sumI; - cov[0] = wnsum * w2sumI; - cov[2] = 0.; - cov[1] = 0.; - - chi2 = 0.; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - float noise = ncl[il] - mult * conf.accCorr[il], estNoise = o2::itsmft::ChipMappingITS::getNChipsPerLr(il) * noisePerChip; - float diff = noise - estNoise; - chi2 += diff * diff / estNoise; - } - } - chi2 = nLayersUsed > 2 ? chi2 / (nLayersUsed - 2) : 0.; - return mult > 0 ? mult : 0; -} - -int FastMultEst::selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel) -{ - int nrof = rofs.size(), nsel = 0; - const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts - sel.clear(); - sel.resize(nrof, true); // by default select all - lastRandomSeed = gRandom->GetSeed(); - if (multEstConf.isMultCutRequested()) { - for (uint32_t irof = 0; irof < nrof; irof++) { - nsel += sel[irof] = multEstConf.isPassingMultCut(process(rofs[irof].getROFData(clus))); - } - } else { - nsel = nrof; - } - using IdNT = std::pair; - if (multEstConf.cutRandomFraction > 0.) { - int ntrig = trig.size(), currTrig = 0; - if (multEstConf.preferTriggered) { - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - std::vector nTrigROF; - nTrigROF.reserve(nrof); - for (uint32_t irof = 0; irof < nrof; irof++) { - if (sel[irof]) { - if (nsel && gRandom->Rndm() < multEstConf.cutRandomFraction) { - nsel--; - } - auto irROF = rofs[irof].getBCData(); - while (currTrig < ntrig && trig[currTrig].ir < irROF) { // triggers are sorted, jump to 1st one not less than current ROF - currTrig++; - } - auto& trof = nTrigROF.emplace_back(irof, 0); - irROF += alpParams.roFrameLengthInBC; - while (currTrig < ntrig && trig[currTrig].ir < irROF) { - trof.second++; - currTrig++; - } - } - } - if (nsel > 0) { - sort(nTrigROF.begin(), nTrigROF.end(), [](const IdNT& a, const IdNT& b) { return a.second > b.second; }); // order in number of triggers - auto last = nTrigROF.begin() + nsel; - sort(nTrigROF.begin(), last, [](const IdNT& a, const IdNT& b) { return a.first < b.first; }); // order in ROF ID first nsel ROFs - } - for (int i = nsel; i < int(nTrigROF.size()); i++) { // reject ROFs in the tail - sel[nTrigROF[i].first] = false; - } - } else { // dummy random rejection - for (int irof = 0; irof < nrof; irof++) { - if (sel[irof]) { - float sr = gRandom->Rndm(); - if (gRandom->Rndm() < multEstConf.cutRandomFraction) { - sel[irof] = false; - nsel--; - } - } - } - } - } - LOGP(debug, "NSel = {} of {} rofs Seeds: before {} after {}", nsel, nrof, lastRandomSeed, gRandom->GetSeed()); - - return nsel; -} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEstConfig.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEstConfig.cxx deleted file mode 100644 index 63c43cf26ba15..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEstConfig.cxx +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITSReconstruction/FastMultEstConfig.h" -#include "TRandom.h" - -O2ParamImpl(o2::its::FastMultEstConfig); - -using namespace o2::its; - -bool FastMultEstConfig::isPassingRandomRejection() const -{ - return (cutRandomFraction <= 0. || gRandom->Rndm() > cutRandomFraction) ? true : false; -} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h b/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h index c93cf03d0ed3d..3bc8ee0f5403b 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h +++ b/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h @@ -15,13 +15,6 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::its::ClustererTask + ; -#pragma link C++ class o2::its::CookedTracker + ; - #pragma link C++ class o2::its::RecoGeomHelper + ; -#pragma link C++ class o2::its::FastMultEst + ; -#pragma link C++ class o2::its::FastMultEstConfig + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::FastMultEstConfig> + ; - #endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx index 5d5467c6d4f4c..712ec6a022d16 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx @@ -16,6 +16,7 @@ #include "ITSReconstruction/RecoGeomHelper.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ITSBase/GeometryTGeo.h" +#include "Framework/Logger.h" using namespace o2::its; @@ -30,9 +31,9 @@ void RecoGeomHelper::RecoChip::updateLimits(const o2::math_utils::Point3D //_____________________________________________________________________ void RecoGeomHelper::RecoChip::print() const { - printf("Ch#%4d Alp: %+.3f X:%5.2f %+6.3f]<%.3f dPhiH:%.3f | XYEdges: {%+6.3f,%+6.3f}{%+6.3f,%+6.3f} | %3d chips | OvlNext: %s\n", - id, phiRange.getMin(), phiMean, phiRange.getMax(), dphiH, - xyEdges.getX0(), xyEdges.getY0(), xyEdges.getX1(), xyEdges.getY1(), (int)chips.size(), - overlapWithNext == Undefined ? "N/A" : ((overlapWithNext == NoOverlap ? "NO" : (overlapWithNext == Above ? "Above" : "Below")))); + LOGF(info, "Ladder %3d %.3f]<%.3f dPhiH:%.3f | XYEdges: {%+6.3f,%+6.3f}{%+6.3f,%+6.3f} | %3d chips | OvlNext: %s\n", + id, phiRange.getMin(), phiMean, phiRange.getMax(), dphiH, + xyEdges.getX0(), xyEdges.getY0(), xyEdges.getX1(), xyEdges.getY1(), (int)chips.size(), + overlapWithNext == Undefined ? "N/A" : ((overlapWithNext == NoOverlap ? "NO" : (overlapWithNext == Above ? "Above" : "Below")))); for (const auto& ch : chips) { ch.print(); } @@ -220,17 +221,17 @@ void RecoGeomHelper::RecoLayer::updateLimits(const o2::math_utils::Point3D= minLayer;) { auto& lr = layers[il]; lr.id = il; lr.init(); diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx deleted file mode 100644 index cb7f1eeacb02e..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TrivialVertexer.cxx -/// \brief Implementation of the ITS trivial vertex finder - -#include - -#include "TFile.h" -#include "TTree.h" - -#include "FairMCEventHeader.h" -#include - -#include "ITSReconstruction/TrivialVertexer.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::itsmft; -using namespace o2::its; - -using Point3Df = o2::math_utils::Point3D; - -TrivialVertexer::TrivialVertexer() = default; - -TrivialVertexer::~TrivialVertexer() -{ - if (mHeader) - delete mHeader; - if (mTree) - delete mTree; - if (mFile) - delete mFile; -} - -Bool_t TrivialVertexer::openInputFile(const Char_t* fname) -{ - mFile = TFile::Open(fname, "old"); - if (!mFile) { - LOG(error) << "TrivialVertexer::openInputFile() : " - << "Cannot open the input file !"; - return kFALSE; - } - mTree = (TTree*)mFile->Get("o2sim"); - if (!mTree) { - LOG(error) << "TrivialVertexer::openInputFile() : " - << "Cannot get the input tree !"; - return kFALSE; - } - Int_t rc = mTree->SetBranchAddress("MCEventHeader.", &mHeader); - if (rc != 0) { - LOG(error) << "TrivialVertexer::openInputFile() : " - << "Cannot get the input branch ! rc=" << rc; - return kFALSE; - } - return kTRUE; -} - -void TrivialVertexer::process(const std::vector& clusters, std::vector>& vertices) -{ - if (mClsLabels == nullptr) { - LOG(info) << "TrivialVertexer::process() : " - << "No cluster labels available ! Running with a default MC vertex..."; - vertices.emplace_back(std::array{0., 0., 0.}); - return; - } - - if (mTree == nullptr) { - LOG(info) << "TrivialVertexer::process() : " - << "No MC information available ! Running with a default MC vertex..."; - vertices.emplace_back(std::array{0., 0., 0.}); - return; - } - - Int_t lastEventID = 0; - Int_t firstEventID = std::numeric_limits::max(); - - // Find the first and last MC event within this TF - for (Int_t i = 0; i < clusters.size(); ++i) { - auto mclab = (mClsLabels->getLabels(i))[0]; - if (mclab.getTrackID() == -1) - continue; // noise - auto id = mclab.getEventID(); - if (id < firstEventID) - firstEventID = id; - if (id > lastEventID) - lastEventID = id; - } - - for (Int_t mcEv = firstEventID; mcEv <= lastEventID; ++mcEv) { - mTree->GetEvent(mcEv); - Double_t vx = mHeader->GetX(); - Double_t vy = mHeader->GetY(); - Double_t vz = mHeader->GetZ(); - vertices.emplace_back(std::array{vx, vy, vz}); - LOG(info) << "TrivialVertexer::process() : " - << "MC event #" << mcEv << " with vertex (" << vx << ',' << vy << ',' << vz << ')'; - } -} diff --git a/Detectors/ITSMFT/ITS/simulation/CMakeLists.txt b/Detectors/ITSMFT/ITS/simulation/CMakeLists.txt index 2406390944f06..51ade8bab6bcc 100644 --- a/Detectors/ITSMFT/ITS/simulation/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/simulation/CMakeLists.txt @@ -12,18 +12,21 @@ o2_add_library(ITSSimulation SOURCES src/V11Geometry.cxx src/V1Layer.cxx src/V3Layer.cxx src/Detector.cxx src/V3Services.cxx src/V3Cage.cxx - src/DescriptorInnerBarrelITS2.cxx + src/DescriptorInnerBarrelITS2.cxx src/ITSDataSimulator.cxx + src/ITSSimParam.cxx PUBLIC_LINK_LIBRARIES O2::ITSBase O2::ITSMFTSimulation ROOT::Physics $<$:O2::ITS3Base> $<$:O2::ITS3Simulation>) o2_target_root_dictionary(ITSSimulation HEADERS include/ITSSimulation/Detector.h + include/ITSSimulation/ITSDataSimulator.h include/ITSSimulation/V1Layer.h include/ITSSimulation/V3Layer.h include/ITSSimulation/V3Cage.h include/ITSSimulation/V11Geometry.h include/ITSSimulation/V3Services.h - include/ITSSimulation/DescriptorInnerBarrelITS2.h) + include/ITSSimulation/DescriptorInnerBarrelITS2.h + include/ITSSimulation/ITSSimParam.h) o2_data_file(COPY data DESTINATION Detectors/ITS/simulation) @@ -40,8 +43,23 @@ o2_add_executable(digi2raw O2::CommonUtils Boost::program_options) +o2_add_executable(sim-data + COMPONENT_NAME its + TARGETVARNAME itssimdata_exe + SOURCES src/ITSDataSimulator.cxx + PUBLIC_LINK_LIBRARIES O2::ITSMFTReconstruction + O2::DataFormatsITSMFT + O2::ITSMFTBase + O2::ITSMFTSimulation + O2::DetectorsRaw + O2::DetectorsCommonDataFormats + O2::CommonUtils + O2::ITSSimulation + Boost::program_options) + if(NOT APPLE) set_property(TARGET ${itsdigi2raw_exe} PROPERTY LINK_WHAT_YOU_USE ON) + set_property(TARGET ${itssimData_exe} PROPERTY LINK_WHAT_YOU_USE ON) endif() diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h index 8573053b06f66..57301ac4babd0 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h @@ -77,7 +77,13 @@ class Detector : public o2::base::DetImpl /// Name : Detector Name /// Active: kTRUE for active detectors (ProcessHits() will be called) /// kFALSE for inactive detectors - Detector(Bool_t active, TString name = "ITS", TString its3Version = ""); + Detector(Bool_t active, TString name = "ITS"); + + // Factory method + static o2::base::Detector* create(const char* name, bool active) + { + return new Detector(active, name); + } /// Default constructor Detector(); @@ -153,6 +159,9 @@ class Detector : public o2::base::DetImpl /// Add alignable top volumes void addAlignableVolumes() const override; + /// Add ITS chip volumes to parallel world geometry + void fillParallelWorld() const override; + /// Add alignable Layer volumes /// \param lr layer number /// \param parent path of the parent volume @@ -286,6 +295,10 @@ class Detector : public o2::base::DetImpl /// \param motherVolume the TGeoVolume owing the volume supports void createOuterBarrelSupports(TGeoVolume* motherVolume); + /// Creates the ITS Services (tubes, cables, and the like) + /// \param motherVolume the TGeoVolume owing the volume supports + void createITSServices(TGeoVolume* motherVolume); + Detector(const Detector&); Detector& operator=(const Detector&); diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSDataSimulator.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSDataSimulator.h new file mode 100644 index 0000000000000..c8bb9becbd2da --- /dev/null +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSDataSimulator.h @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ITSDataSimulator.h +/// \brief Infrastructure to simulate ALPIDE chip data. +/// \author knaumov@cern.ch + +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITSMFTReconstruction/PixelData.h" + +namespace o2 +{ +namespace itsmft +{ + +class ITSDataSimulator +{ + public: + const static uint32_t MaxChipID = 24119; + const static uint32_t MaxPixelsPerChip = + SegmentationAlpide::NRows * SegmentationAlpide::NCols; + + ITSDataSimulator(int32_t seed, uint32_t numberOfChips, + uint32_t maxPixelsPerChip, bool doDigits, bool doErrors) + : mSeed(seed), mNumberOfChips(numberOfChips), mMaxPixelsPerChip(maxPixelsPerChip), mDoDigits(doDigits), mDoErrors(doErrors) + { + srand(mSeed); + } + + ~ITSDataSimulator() = default; + + // Simulate fired pixels for a chip + std::vector generateChipData(); + + void simulate(); + + private: + int32_t mSeed; + uint32_t mMaxPixelsPerChip; + uint32_t mNumberOfChips; + bool mDoDigits; + bool mDoErrors; +}; + +} // namespace itsmft +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSSimParam.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSSimParam.h new file mode 100644 index 0000000000000..231b9294136b0 --- /dev/null +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSSimParam.h @@ -0,0 +1,34 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef DETECTORS_BASE_INCLUDE_ITSSIMPARAM_H_ +#define DETECTORS_BASE_INCLUDE_ITSSIMPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace its +{ + +struct ITSSimParam : public o2::conf::ConfigurableParamHelper { + bool addMetalToPW = true; + bool addSensorToPW = true; + bool addChipToPW = true; + + O2ParamDef(ITSSimParam, "ITSSimParam"); +}; + +} // namespace its +} // namespace o2 + +#endif /* DETECTORS_BASE_INCLUDE_ITSSIMPARAM_H_ */ diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Cage.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Cage.h index 0c38304d8370b..7844f42601a47 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Cage.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Cage.h @@ -85,6 +85,48 @@ class V3Cage : public V11Geometry /// \param mgr The GeoManager (used only to get the proper material) TGeoCompositeShape* createCageEndCapCableCross(const TGeoManager* mgr = gGeoManager); + /// Creates the Beam Pipe Support inside the Cage on the A side + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createBeamPipeSupport(const TGeoManager* mgr = gGeoManager); + + /// Creates the Titanium lower part of the collar supporting the beam pipe + /// \param mgr The GeoManager (used only to get the proper material) + TGeoCompositeShape* createBPSuppLowerCollar(); + + /// Creates the Titanium upper part of the collar supporting the beam pipe + /// \param mgr The GeoManager (used only to get the proper material) + TGeoCompositeShape* createBPSuppUpperCollar(); + + /// Creates the CF lateral bar of the beam pipe support (aka collar beam) + /// \param mgr The GeoManager (used only to get the proper material) + TGeoCompositeShape* createBPSuppCollarBeam(); + + /// Creates the Titanium lateral bracket of the beam pipe support + /// \param mgr The GeoManager (used only to get the proper material) + TGeoCompositeShape* createBPSuppBracket(); + + /// Creates the lateral clamps holding the beam pipe support to the Cage + /// \param mgr The GeoManager (used only to get the proper material) + TGeoCompositeShape* createBPSuppClamp(); + + /// Creates the Cage Closing Cross element + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createCageClosingCross(const TGeoManager* mgr = gGeoManager); + + /// Creates and places the MFT rails inside the Cage + /// \param mother The mother volume to place the rails into + /// \param mgr The GeoManager (used only to get the proper material) + void createAndPlaceMFTRailsInsideCage(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates a pair of MFT rails inside the Cage + /// \param motmed Medium material of the mother volume + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createMFTRailsPair(const TGeoMedium* motmed, const TGeoManager* mgr = gGeoManager); + + /// Creates a hinge holding a pair of MFT rails inside the Cage + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createMFTRailsHinge(const TGeoManager* mgr = gGeoManager); + // Parameters static const Double_t sCageYInBarrel; ///< Global Y translation @@ -107,6 +149,7 @@ class V3Cage : public V11Geometry static const Double_t sCageCoverRibYBaseHi; ///< Cover rib Base Height on Y static const Double_t sCageCoverRibFoldHi; ///< Cover rib Fold Height + // Cage Side Panels and Rails static const Double_t sCageSidePanelLength; ///< Side panel length along Z static const Double_t sCageSidePanelWidth; ///< Side panel width along Y static const Double_t sCageSidePanelFoilThick; ///< Side panel foil thickness @@ -124,11 +167,14 @@ class V3Cage : public V11Geometry static const Double_t sCageSidePanelGuideWide; ///< Side panel guide X width static const Double_t sCageSidePanelGuidThik1; ///< Side panel guide thickness static const Double_t sCageSidePanelGuidThik2; ///< Side panel guide thickness + static const Double_t sCageSidePanelMidBarWid; ///< Side panel middle bar width + static const Double_t sCageSidePanelSidBarWid; ///< Side panel side bar width static const Double_t sCageSidePanelRail1Ypos[2]; ///< Side panel rail 1 Y pos static const Double_t sCageSidePanelRail2Ypos; ///< Side panel rail 2 Y pos static const Double_t sCageSidePanelRail3Ypos[3]; ///< Side panel rail 3 Y pos + // Cage End Cap static const Double_t sCageEndCapDext; ///< End Cap ext diameter static const Double_t sCageEndCapDint; ///< End Cap int diameter static const Double_t sCageEndCapFoamThick; ///< End Cap foam thickness @@ -151,6 +197,90 @@ class V3Cage : public V11Geometry static const Double_t sCageECCableCrosInZLen; ///< EC Cable Cut inner length static const Double_t sCageECCableCrosSidWid; ///< EC Cable Cut Y side len + // Beam Pipe Support (A Side) + static const Double_t sBPSuppCollarIntD; ///< BP support collar int diam + static const Double_t sBPSuppCollarExtD; ///< BP support collar ext diam + static const Double_t sBPSuppCollarBushD; ///< BP support collar bushing diam + static const Double_t sBPSuppUpperCollarLen; ///< BP support upper collar len + static const Double_t sBPSuppUpperCollarHei; ///< BP support upper collar high + static const Double_t sBPSuppLowerCollarLen; ///< BP support lower collar len + static const Double_t sBPSuppLowerCollarTlX; ///< BP support lower collar tail X + static const Double_t sBPSuppLowCollHolDist; ///< BP support lower collar hole dist + static const Double_t sBPSuppLowCollTailHei; ///< BP support lower collar tail hei + static const Double_t sBPSuppCollarBeamLen; ///< BP support collar beam len + static const Double_t sBPSuppCollarBeamWid; ///< BP support collar beam wide + static const Double_t sBPSuppCollarBeamHei; ///< BP support collar beam high + static const Double_t sBPSuppBracketTotLen; ///< BP support bracket total len + static const Double_t sBPSuppBracketWidth; ///< BP support bracket width + static const Double_t sBPSuppBracketInLen; ///< BP support bracket internal len + static const Double_t sBPSuppBracketInHei; ///< BP support bracket internal height + static const Double_t sBPSuppBracketTailLen; ///< BP support bracket tail len + static const Double_t sBPSuppBracketTailHei; ///< BP support bracket tail hei + static const Double_t sBPSuppBrktCentHoleX; ///< BP support bracket central hole X pos + static const Double_t sBPSuppBrktCentHoleD; ///< BP support bracket central hole diameter + static const Double_t sBPSuppBrktLatHoleX; ///< BP support bracket lateral hole X pos + static const Double_t sBPSuppBrktLatHoleD; ///< BP support bracket lateral hole diameter + static const Double_t sBPSuppBrktLatHoleW; ///< BP support bracket lateral hole width + static const Double_t sBPSuppBrktLatHoleH; ///< BP support bracket lateral hole height + static const Double_t sBPSuppBrktHolesY; ///< BP support bracket holes Y pos + static const Double_t sBPSuppCollarM4High; ///< BP support collar screw head height + static const Double_t sBPSuppCollarM4Diam; ///< BP support collar screw head diameter + static const Double_t sBPSuppCollarM4XDist; ///< BP support collar screw X dist + static const Double_t sBPSuppCollarM4ZPos; ///< BP support collar screw Z pos + static const Double_t sBPSuppClampTotLen; ///< BP support clamp length + static const Double_t sBPSuppClampTotWid; ///< BP support clamp width + static const Double_t sBPSuppClampTotHei; ///< BP support clamp height + static const Double_t sBPSuppClampLatThick; ///< BP support clamp lateral thick + static const Double_t sBPSuppClampShelfLen; ///< BP support clamp shelf len + static const Double_t sBPSuppClampShelfHei; ///< BP support clamp shelf hei + static const Double_t sBPSuppClampsXDist; ///< BP support clamps X distance + static const Double_t sBPSuppClampInsDmin; ///< BP support clamp insert Dmin + static const Double_t sBPSuppClampInsDmax; ///< BP support clamp insert Dmax + static const Double_t sBPSuppClampInsH; ///< BP support clamp insert H + static const Double_t sBPSuppClampInsXPos; ///< BP support clamp insert X + static const Double_t sBPSuppClampInsZPos; ///< BP support clamp insert Z + static const Double_t sBPSuppClampShimLen; ///< BP support clamp shim length + static const Double_t sBPSuppClampShimWid; ///< BP support clamp shim width + static const Double_t sBPSuppClampShimThick; ///< BP support clamp shim thick + static const Double_t sBPSuppClampM5High; ///< BP support clamp screw head height + static const Double_t sBPSuppClampM5Diam; ///< BP support clamp screw head diameter + static const Double_t sBPSuppClampM5ZPos; ///< BP support clamp screw Z pos + static const Double_t sBPSuppZPos; ///< BP support global Z pos + + // Closing Cross + static const Double_t sCageCrossXWidthTot; ///< Closing cross total X wid + static const Double_t sCageCrossXWidthExt; ///< Closing cross external X wid + static const Double_t sCageCrossXWidthInt; ///< Closing cross internal X wid + static const Double_t sCageCrossYHeightTot; ///< Closing cross total Y h + static const Double_t sCageCrossYHeightInt; ///< Closing cross internal Y h + static const Double_t sCageCrossYMid; ///< Closing cross Y mid point + static const Double_t sCageCrossZLength; ///< Closing cross Z length + static const Double_t sCageCrossBarThick; ///< Closing cross bar thickness + static const Double_t sCageCrossBarPhi; ///< Closing cross bar angle + + // MFT Rails inside the Cage + static const Double_t sCageMFTRailZLen; ///< Total length of the rail + static const Double_t sCageMFTRailZPos; ///< Rail global Z position + static const Double_t sCageMFTRailTotWidth; ///< Total width of the rail + static const Double_t sCageMFTRailExtWidth; ///< Width of the external part + static const Double_t sCageMFTRailIntWidth; ///< Width of the internal part + static const Double_t sCageMFTRailBaseWidth; ///< Width of the rail base + static const Double_t sCageMFTRailTotHeight; ///< Total height of the rail + static const Double_t sCageMFTRailExtHeight; ///< Height of the external part + static const Double_t sCageMFTRailIntHeight; ///< Height of the internal part + static const Double_t sCageMFTRailsXDist; ///< X distance between rails + + // MFT Rail hinges + static const Double_t sCageMFTHingeTotWid; ///< Total width of a hinge + static const Double_t sCageMFTHingeIntWid; ///< Width of hinge inner part + static const Double_t sCageMFTHingeHeight; ///< Total height of the rail + static const Double_t sCageMFTHingeIntHei; ///< Height of hinge inner part + static const Double_t sCageMFTHingeTotLen; ///< Total length of a hinge + static const Double_t sCageMFTHingeIntLen; ///< Length of hinge inner part + static const Double_t sCageMFTHingeBulgeWid; ///< Width of a hinge bulge + static const Double_t sCageMFTHingeBulgeHei; ///< Height of a hinge bulge + static const Double_t sCageMFTHingeBulgePos; ///< X position of a hinge bulge + ClassDefOverride(V3Cage, 0); // ITS v3 support geometry }; } // namespace its diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Layer.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Layer.h index 15683feac4613..bc4fd6b0dadd4 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Layer.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Layer.h @@ -353,43 +353,49 @@ class V3Layer : public V11Geometry static const Int_t sIBNChipRows; ///< IB chip rows in module static const Double_t sIBChipZGap; ///< Gap between IB chips on Z - static const Double_t sIBModuleZLength; ///< IB Module Length along Z - static const Double_t sIBFPCWiderXPlus; ///< FPC protrusion at X>0 - static const Double_t sIBFPCWiderXNeg; ///< FPC protrusion at X<0 - static const Double_t sIBFlexCableAlThick; ///< Thickness of FPC Aluminum - static const Double_t sIBFPCAlGNDWidth; ///< Width of total FPC Al Gnd - static const Double_t sIBFPCAlAnodeWidth1; ///< Width of FPC Al Anode - static const Double_t sIBFPCAlAnodeWidth2; ///< Width of FPC Al Anode - static const Double_t sIBFlexCableKapThick; ///< Thickness of FPC Kapton - static const Double_t sIBFlexCablePolyThick; ///< Thickness of FPC Coverlay - static const Double_t sIBFlexCapacitorXWid; ///< IB capaictor X width - static const Double_t sIBFlexCapacitorYHi; ///< IB capaictor Y height - static const Double_t sIBFlexCapacitorZLen; ///< IB capaictor Z length - static const Double_t sIBColdPlateWidth; ///< IB cold plate X width - static const Double_t sIBColdPlateZLen; ///< IB cold plate Z length - static const Double_t sIBGlueThick; ///< IB glue thickness - static const Double_t sIBCarbonFleeceThick; ///< IB carbon fleece thickness - static const Double_t sIBCarbonPaperThick; ///< IB Carbon Paper Thickness - static const Double_t sIBCarbonPaperWidth; ///< IB Carbon Paper X Width - static const Double_t sIBCarbonPaperZLen; ///< IB Carbon Paper Z Length - static const Double_t sIBK13D2UThick; ///< IB k13d2u prepreg thickness - static const Double_t sIBCoolPipeInnerD; ///< IB cooling inner diameter - static const Double_t sIBCoolPipeThick; ///< IB cooling pipe thickness - static const Double_t sIBCoolPipeXDist; ///< IB cooling pipe separation - static const Double_t sIBCoolPipeZLen; ///< IB cooling pipe length - static const Double_t sIBTopVertexWidth1; ///< IB TopVertex width - static const Double_t sIBTopVertexWidth2; ///< IB TopVertex width - static const Double_t sIBTopVertexHeight; ///< IB TopVertex height - static const Double_t sIBTopVertexAngle; ///< IB TopVertex aperture angle - static const Double_t sIBSideVertexWidth; ///< IB SideVertex width - static const Double_t sIBSideVertexHeight; ///< IB SideVertex height - static const Double_t sIBTopFilamentSide; ///< IB TopFilament side - static const Double_t sIBTopFilamentAlpha; ///< IB TopFilament angle - static const Double_t sIBTopFilamentInterZ; ///< IB TopFilament Z interdist - static const Double_t sIBEndSupportThick; ///< IB end support thickness - static const Double_t sIBEndSupportZLen; ///< IB end support length - static const Double_t sIBEndSupportXUp; ///< IB end support X up wide - static const Double_t sIBEndSupportOpenPhi; ///< IB end support opening phi + static const Double_t sIBModuleZLength; ///< IB Module Length along Z + static const Double_t sIBFPCWiderXPlus; ///< FPC protrusion at X>0 + static const Double_t sIBFPCWiderXNeg; ///< FPC protrusion at X<0 + static const Double_t sIBFlexCableAlThick; ///< Thickness of FPC Aluminum + static const Double_t sIBFPCAlGNDWidth; ///< Width of total FPC Al Gnd + static const Double_t sIBFPCAlAnodeWidth1; ///< Width of FPC Al Anode + static const Double_t sIBFPCAlAnodeWidth2; ///< Width of FPC Al Anode + static const Double_t sIBFlexCableKapThick; ///< Thickness of FPC Kapton + static const Double_t sIBFlexCablePolyThick; ///< Thickness of FPC Coverlay + static const Double_t sIBFlexCapacitor1XWid; ///< IB small capacitor X width + static const Double_t sIBFlexCapacitor1YHi; ///< IB small capacitor Y height + static const Double_t sIBFlexCapacitor1ZLen; ///< IB small capacitor Z length + static const Double_t sIBFlexCapacitor22XWid; ///< IB large capacitor X width + static const Double_t sIBFlexCapacitor22YHi; ///< IB large capacitor Y height + static const Double_t sIBFlexCapacitor22ZLen; ///< IB large capacitor Z length + static const Double_t sIBFlexResistorXWid; ///< IB FPC resistor X width + static const Double_t sIBFlexResistorYHi; ///< IB FPC resistor Y height + static const Double_t sIBFlexResistorZLen; ///< IB FPC resistor Z length + static const Double_t sIBColdPlateWidth; ///< IB cold plate X width + static const Double_t sIBColdPlateZLen; ///< IB cold plate Z length + static const Double_t sIBGlueThick; ///< IB glue thickness + static const Double_t sIBCarbonFleeceThick; ///< IB carbon fleece thickness + static const Double_t sIBCarbonPaperThick; ///< IB Carbon Paper Thickness + static const Double_t sIBCarbonPaperWidth; ///< IB Carbon Paper X Width + static const Double_t sIBCarbonPaperZLen; ///< IB Carbon Paper Z Length + static const Double_t sIBK13D2UThick; ///< IB k13d2u prepreg thickness + static const Double_t sIBCoolPipeInnerD; ///< IB cooling inner diameter + static const Double_t sIBCoolPipeThick; ///< IB cooling pipe thickness + static const Double_t sIBCoolPipeXDist; ///< IB cooling pipe separation + static const Double_t sIBCoolPipeZLen; ///< IB cooling pipe length + static const Double_t sIBTopVertexWidth1; ///< IB TopVertex width + static const Double_t sIBTopVertexWidth2; ///< IB TopVertex width + static const Double_t sIBTopVertexHeight; ///< IB TopVertex height + static const Double_t sIBTopVertexAngle; ///< IB TopVertex aperture angle + static const Double_t sIBSideVertexWidth; ///< IB SideVertex width + static const Double_t sIBSideVertexHeight; ///< IB SideVertex height + static const Double_t sIBTopFilamentSide; ///< IB TopFilament side + static const Double_t sIBTopFilamentAlpha; ///< IB TopFilament angle + static const Double_t sIBTopFilamentInterZ; ///< IB TopFilament Z interdist + static const Double_t sIBEndSupportThick; ///< IB end support thickness + static const Double_t sIBEndSupportZLen; ///< IB end support length + static const Double_t sIBEndSupportXUp; ///< IB end support X up wide + static const Double_t sIBEndSupportOpenPhi; ///< IB end support opening phi static const Double_t sIBConnectorXWidth; ///< IB Connectors Width static const Double_t sIBConnectorYTot; ///< IB Connectors total height diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Services.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Services.h index b281051ecb9f4..352a6ce335c7e 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Services.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Services.h @@ -106,6 +106,11 @@ class V3Services : public V11Geometry /// \param mgr The GeoManager (used only to get the proper material) void createIBGammaConvWire(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + /// Steering method to creates the ITS Services (tubes, cables, and the like) + /// \param motherVolume the TGeoVolume owing the volume supports + /// \param mgr The GeoManager (used only to get the proper material) + void createAllITSServices(TGeoVolume* motherVolume, const TGeoManager* mgr = gGeoManager); + /// Creates the Outer Barrel Gamma Conversion Wire /// \param mother the TGeoVolume owing the volume structure /// \param mgr The GeoManager (used only to get the proper material) diff --git a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx index cc4deac97dff4..63d7a8ad8dfa2 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -20,17 +20,18 @@ #include "ITSSimulation/V3Layer.h" #include "ITSSimulation/V3Services.h" #include "ITSSimulation/V3Cage.h" +#include "ITSSimulation/ITSSimParam.h" #include "DetectorsBase/Stack.h" #include "SimulationDataFormat/TrackReference.h" +#include "fairlogger/Logger.h" // for LOG, LOG_IF // FairRoot includes -#include "FairDetector.h" // for FairDetector -#include // for LOG, LOG_IF -#include "FairRootManager.h" // for FairRootManager -#include "FairRun.h" // for FairRun -#include "FairRuntimeDb.h" // for FairRuntimeDb -#include "FairVolume.h" // for FairVolume +#include "FairDetector.h" // for FairDetector +#include "FairRootManager.h" // for FairRootManager +#include "FairRun.h" // for FairRun +#include "FairRuntimeDb.h" // for FairRuntimeDb +#include "FairVolume.h" // for FairVolume #include "FairRootManager.h" #include "TGeoManager.h" // for TGeoManager, gGeoManager @@ -41,8 +42,10 @@ #include "TVirtualMC.h" // for gMC, TVirtualMC #include "TVirtualMCStack.h" // for TVirtualMCStack #include "TFile.h" // for TVirtualMCStack +#include "TGeoParallelWorld.h" #include // for NULL, snprintf +#include class FairModule; @@ -58,6 +61,7 @@ using Segmentation = o2::itsmft::SegmentationAlpide; using namespace o2::its; #ifdef ENABLE_UPGRADES +#include "ITS3Simulation/DescriptorInnerBarrelITS3.h" using namespace o2::its3; #endif @@ -126,7 +130,7 @@ void Detector::configOuterBarrelITS(int nInnerBarrelLayers, int buildLevel) } } -Detector::Detector(Bool_t active, TString name, TString its3Version) +Detector::Detector(Bool_t active, TString name) : o2::base::DetImpl(name, active), mTrackData(), /* @@ -142,13 +146,10 @@ Detector::Detector(Bool_t active, TString name, TString its3Version) mHits(o2::utils::createSimVector()) { if (name == "ITS") { - mDescriptorIB.reset(new DescriptorInnerBarrelITS2(3)); + mDescriptorIB = std::make_shared(3); } else if (name == "IT3") { #ifdef ENABLE_UPGRADES - mDescriptorIB.reset(new DescriptorInnerBarrelITS3()); - if (its3Version != "") { - ((DescriptorInnerBarrelITS3*)mDescriptorIB.get())->setVersion(its3Version.Data()); - } + mDescriptorIB = std::make_shared(); #endif } else { LOG(fatal) << "Detector name not supported (options ITS and ITS3)"; @@ -159,14 +160,14 @@ Detector::Detector(Bool_t active, TString name, TString its3Version) TString detName = GetName(); if (detName == "ITS") { - ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->configure(buildLevelITS); + dynamic_cast(mDescriptorIB.get())->configure(buildLevelITS); } else if (detName == "IT3") { #ifdef ENABLE_UPGRADES - ((DescriptorInnerBarrelITS3*)mDescriptorIB.get())->configure(); + dynamic_cast(mDescriptorIB.get())->configure(); #endif } - mNumberInnerLayers = mDescriptorIB.get()->getNumberOfLayers(); + mNumberInnerLayers = mDescriptorIB->getNumberOfLayers(); mNumberLayers = mNumberInnerLayers + sNumberOuterLayers; mLayerName.resize(mNumberLayers); @@ -186,10 +187,11 @@ Detector::Detector(Bool_t active, TString name, TString its3Version) for (int j{0}; j < mNumberLayers; j++) { if (detName == "IT3" && j < mNumberInnerLayers) { - mLayerName[j].Form("%s%d", GeometryTGeo::getITS3SensorPattern(), j); // See V3Layer + mLayerName[j].Form("%s%d", GeometryTGeo::getITS3SensorPattern(), j); } else { mLayerName[j].Form("%s%d", GeometryTGeo::getITSSensorPattern(), j); // See V3Layer } + LOGP(debug, "{}: mLayerName={}", j, mLayerName[j].Data()); } if (mNumberLayers > 0) { // if not, we'll Fatal-ize in CreateGeometry @@ -387,7 +389,7 @@ Bool_t Detector::ProcessHits(FairVolume* vol) TLorentzVector positionStop; fMC->TrackPosition(positionStop); // Retrieve the indices with the volume path - int halfbarrel(0), stave(0), halfstave(0), chipinmodule(0), module; + int halfbarrel(0), stave(0), halfstave(0), chipinmodule(0), module(0); fMC->CurrentVolOffID(1, chipinmodule); fMC->CurrentVolOffID(2, module); fMC->CurrentVolOffID(3, halfstave); @@ -476,10 +478,11 @@ void Detector::createMaterials() Float_t dInox304 = 7.85; // Ceramic (for IB capacitors) (BaTiO3) + // Density includes soldering Float_t aCeramic[3] = {137.327, 47.867, 15.999}; Float_t zCeramic[3] = {56, 22, 8}; // Ba, Ti, O Float_t wCeramic[3] = {1, 1, 3}; // Molecular composition - Float_t dCeramic = 6.02; + Float_t dCeramic = 8.28; // Rohacell (C9 H13 N1 O2) Float_t aRohac[4] = {12.01, 1.01, 14.010, 16.}; @@ -502,6 +505,18 @@ void Detector::createMaterials() Float_t wBrass[3] = {0.58, 0.39, 0.03}; Float_t dBrass = 8.46; + // Polymer for ITS services + Float_t aPoly[2] = {12.01, 1.}; + Float_t zPoly[2] = {6., 1.}; + Float_t wPoly[2] = {0.857, .143}; + Float_t dPoly = 0.9; + + // Vespel for Beam Pipe Support (same definition of Polyimide in Pipe.cxx) + Float_t aVesp[4] = {16., 14., 12., 1.}; + Float_t zVesp[4] = {8., 7., 6., 1.}; + Float_t wVesp[4] = {5., 2., 22., 10.}; + Float_t dVesp = 1.42; + o2::base::Detector::Mixture(1, "AIR$", aAir, zAir, dAir, 4, wAir); o2::base::Detector::Medium(1, "AIR$", 1, 0, ifield, fieldm, tmaxfdAir, stemaxAir, deemaxAir, epsilAir, stminAir); @@ -577,7 +592,7 @@ void Detector::createMaterials() o2::base::Detector::Medium(38, "RIST110$", 38, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); // Carbon prepreg (Cage) - o2::base::Detector::Material(33, "M46J6K$", 12.0107, 6, 1.84, 999, 999); + o2::base::Detector::Material(33, "M46J6K$", 12.0107, 6, 1.48, 999, 999); o2::base::Detector::Medium(33, "M46J6K$", 33, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); // PEEK CF30 @@ -627,6 +642,22 @@ void Detector::createMaterials() o2::base::Detector::Material(35, "TITANIUM$", 47.867, 22, 4.506, 999, 999); o2::base::Detector::Medium(35, "TITANIUM$", 35, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Carbon-Fiber-Reinforced Polymer + o2::base::Detector::Material(43, "CFRP$", 12.01, 6, 1.55, 999, 999); + o2::base::Detector::Medium(43, "CFRP$", 43, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + // Vespel for Beam Pipe Support + o2::base::Detector::Mixture(44, "VESPEL$", aVesp, zVesp, dVesp, -4, wVesp); + o2::base::Detector::Medium(44, "VESPEL$", 44, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + // Carbon for ITS services + o2::base::Detector::Material(41, "C4SERVICES$", 12.01, 6, 1.75, 999, 999); + o2::base::Detector::Medium(41, "C4SERVICES$", 41, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + // Polymer for ITS services + o2::base::Detector::Mixture(42, "POLY4SERVICES$", aPoly, zPoly, dPoly, 2, wPoly); + o2::base::Detector::Medium(42, "POLY4SERVICES$", 42, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // For ITS3 // Araldite 2011 @@ -634,12 +665,12 @@ void Detector::createMaterials() // ERG Duocel o2::base::Detector::Material(39, "ERGDUOCEL$", 12.0107, 6, 0.06, 999, 999); - o2::base::Detector::Medium(39, "ERGDUOCEL$", 33, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + o2::base::Detector::Medium(39, "ERGDUOCEL$", 39, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); // Impregnated carbon fleece // (as educated guess we assume 50% carbon fleece 50% Araldite glue) o2::base::Detector::Material(40, "IMPREG_FLEECE$", 12.0107, 6, 0.5 * (dAraldite + 0.4), 999, 999); - o2::base::Detector::Medium(40, "IMPREG_FLEECE$", 34, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + o2::base::Detector::Medium(40, "IMPREG_FLEECE$", 40, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); } void Detector::EndOfEvent() { Reset(); } @@ -694,8 +725,8 @@ void Detector::defineLayer(Int_t nlay, Double_t phi0, Double_t r, Int_t nstav, I // Return: // none. - LOG(info) << "L# " << nlay << " Phi:" << phi0 << " R:" << r << " Nst:" << nstav << " Nunit:" << nunit - << " Lthick:" << lthick << " Dthick:" << dthick << " DetID:" << dettypeID << " B:" << buildLevel; + LOG(debug) << "L# " << nlay << " Phi:" << phi0 << " R:" << r << " Nst:" << nstav << " Nunit:" << nunit + << " Lthick:" << lthick << " Dthick:" << dthick << " DetID:" << dettypeID << " B:" << buildLevel; if (nlay >= mNumberLayers || nlay < 0) { LOG(error) << "Wrong layer number " << nlay; @@ -771,7 +802,7 @@ TGeoVolume* Detector::createWrapperVolume(Int_t id) switch (id) { case 0: // IB Layer 0,1,2: simple cylinder { - tube = (TGeoShape*)mDescriptorIB.get()->defineWrapperVolume(); + tube = (TGeoShape*)mDescriptorIB->defineWrapperVolume(); } break; case 1: // MB Layer 3,4: complex Pcon to avoid MFT overlaps { @@ -948,7 +979,7 @@ void Detector::constructDetectorGeometry() TString detName = GetName(); if (detName == "ITS") { ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->createServices(wrapVols[0]); - } else if (detName == "IT3" && mNumberInnerLayers == 3) { + } else if (detName == "IT3") { #ifdef ENABLE_UPGRADES ((DescriptorInnerBarrelITS3*)mDescriptorIB.get())->createServices(wrapVols[0]); #endif @@ -959,6 +990,8 @@ void Detector::constructDetectorGeometry() createOuterBarrelServices(wrapVols[2]); createOuterBarrelSupports(vITSV); + createITSServices(vALIC); + mServicesGeometry->createOBGammaConvWire(vITSV); // Finally create and place the cage @@ -1038,6 +1071,24 @@ void Detector::createOuterBarrelSupports(TGeoVolume* motherVolume) mServicesGeometry->createOBCYSSCylinder(motherVolume); } +void Detector::createITSServices(TGeoVolume* motherVolume) +{ + // + // Creates the ITS services: tubes, cables and the like + // + // Input: + // motherVolume : the volume hosting the supports + // + // Output: + // + // Return: + // + // Created: 12 Apr 2023 Mario Sitta + // + + mServicesGeometry->createAllITSServices(motherVolume); +} + void Detector::addAlignableVolumes() const { // @@ -1056,7 +1107,7 @@ void Detector::addAlignableVolumes() const TString detName = GetName(); TString path = Form("/cave_1/barrel_1/%s_2", GeometryTGeo::getITSVolPattern()); - TString sname = GeometryTGeo::composeSymNameITS((detName == "IT3")); + TString sname = GeometryTGeo::composeSymNameITS(); LOG(debug) << sname << " <-> " << path; @@ -1067,15 +1118,19 @@ void Detector::addAlignableVolumes() const Int_t lastUID = 0; for (Int_t lr = 0; lr < mNumberLayers; lr++) { if (lr < mNumberInnerLayers) { +#ifdef ENABLE_UPGRADES if (detName == "ITS") { ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); + } else { + ((DescriptorInnerBarrelITS3*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); } +#else + ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); +#endif } else { addAlignableVolumesLayer(lr, path, lastUID); } } - - return; } void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) const @@ -1098,8 +1153,6 @@ void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) for (Int_t hb = start; hb < nhbarrel; hb++) { addAlignableVolumesHalfBarrel(lr, hb, path, lastUID); } - - return; } void Detector::addAlignableVolumesHalfBarrel(Int_t lr, Int_t hb, TString& parent, Int_t& lastUID) const @@ -1127,8 +1180,6 @@ void Detector::addAlignableVolumesHalfBarrel(Int_t lr, Int_t hb, TString& parent for (int st = 0; st < nstaves; st++) { addAlignableVolumesStave(lr, hb, st, path, lastUID); } - - return; } void Detector::addAlignableVolumesStave(Int_t lr, Int_t hb, Int_t st, TString& parent, Int_t& lastUID) const @@ -1155,8 +1206,6 @@ void Detector::addAlignableVolumesStave(Int_t lr, Int_t hb, Int_t st, TString& p for (Int_t sst = start; sst < nhstave; sst++) { addAlignableVolumesHalfStave(lr, hb, st, sst, path, lastUID); } - - return; } void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t hb, Int_t st, Int_t hst, TString& parent, Int_t& lastUID) const @@ -1186,8 +1235,6 @@ void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t hb, Int_t st, Int_t for (Int_t md = start; md < nmodules; md++) { addAlignableVolumesModule(lr, hb, st, hst, md, path, lastUID); } - - return; } void Detector::addAlignableVolumesModule(Int_t lr, Int_t hb, Int_t st, Int_t hst, Int_t md, TString& parent, Int_t& lastUID) const @@ -1216,8 +1263,6 @@ void Detector::addAlignableVolumesModule(Int_t lr, Int_t hb, Int_t st, Int_t hst for (Int_t ic = 0; ic < nchips; ic++) { addAlignableVolumesChip(lr, hb, st, hst, md, ic, path, lastUID); } - - return; } void Detector::addAlignableVolumesChip(Int_t lr, Int_t hb, Int_t st, Int_t hst, Int_t md, Int_t ch, TString& parent, @@ -1236,17 +1281,15 @@ void Detector::addAlignableVolumesChip(Int_t lr, Int_t hb, Int_t st, Int_t hst, LOG(debug) << "Add " << sname << " <-> " << path; - if (!gGeoManager->SetAlignableEntry(sname, path.Data(), modUID)) { + if (gGeoManager->SetAlignableEntry(sname, path.Data(), modUID) == nullptr) { LOG(fatal) << "Unable to set alignable entry ! " << sname << " : " << path; } - - return; } void Detector::defineSensitiveVolumes() { TGeoManager* geoManager = gGeoManager; - TGeoVolume* v; + TGeoVolume* v = nullptr; TString volumeName; @@ -1263,6 +1306,53 @@ void Detector::defineSensitiveVolumes() } } +void Detector::fillParallelWorld() const +{ + TGeoParallelWorld* pw = gGeoManager->GetParallelWorld(); + if (pw == nullptr) { + LOG(error) << "Parallel world was not created"; + return; + } + auto& param = ITSSimParam::Instance(); + + for (int iL{0}; iL < mNumberLayers; ++iL) { + auto const layer = mGeometry[iL]; + int nhbarrels = layer->getNumberOfHalfBarrelsPerParent(); + int nstaves = layer->getNumberOfStavesPerParent(); + int nhstaves = layer->getNumberOfHalfStavesPerParent(); + int nmodules = layer->getNumberOfModulesPerParent(); + int nchips = layer->getNumberOfChipsPerParent(); + + for (int iHB{0}; iHB < nhbarrels; ++iHB) { + for (int iS{0}; iS < nstaves; ++iS) { + for (int iHS{nhstaves > 0 ? 0 : -1}; iHS < nhstaves; ++iHS) { + for (int iM{nmodules > 0 ? 0 : -1}; iM < nmodules; ++iM) { + for (int iC{0}; iC < nchips; ++iC) { + TString sname = GeometryTGeo::composeSymNameChip(iL, iHB, iS, iHS, iM, iC); + TGeoPNEntry* pne = gGeoManager->GetAlignableEntry(sname); + auto path = pne->GetTitle(); + + if (param.addMetalToPW) { + TString metalPath = Form("%s/MetalStack_1", path); + gGeoManager->MakePhysicalNode(metalPath); + pw->AddNode(metalPath); + } + if (param.addSensorToPW) { + TString sensorPath = Form("%s/ITSUSensor%d_1", path, iL); + gGeoManager->MakePhysicalNode(sensorPath); + pw->AddNode(sensorPath); + } + if (param.addChipToPW) { + pw->AddNode(path); + } + } + } + } + } + } + } +} + Hit* Detector::addHit(int trackID, int detID, const TVector3& startPos, const TVector3& endPos, const TVector3& startMom, double startE, double endTime, double eLoss, unsigned char startStatus, unsigned char endStatus) @@ -1272,3 +1362,11 @@ Hit* Detector::addHit(int trackID, int detID, const TVector3& startPos, const TV } ClassImp(o2::its::Detector); + +// Define Factory method for calling from the outside +extern "C" { +o2::base::Detector* create_detector_its(const char* name, bool active) +{ + return o2::its::Detector::create(name, active); +} +} diff --git a/Detectors/ITSMFT/ITS/simulation/src/ITSDataSimulator.cxx b/Detectors/ITSMFT/ITS/simulation/src/ITSDataSimulator.cxx new file mode 100644 index 0000000000000..9f7ba12a3c677 --- /dev/null +++ b/Detectors/ITSMFT/ITS/simulation/src/ITSDataSimulator.cxx @@ -0,0 +1,147 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ITSDataSimulator.cxx +/// \author knaumov@cern.ch + +#include "DataFormatsITSMFT/Digit.h" +#include "ITSSimulation/ITSDataSimulator.h" + +#include +#include +#include + +namespace bpo = boost::program_options; +using namespace o2::itsmft; + +std::vector ITSDataSimulator::generateChipData() +{ + std::vector vec; + uint32_t numOfPixels = rand() % mMaxPixelsPerChip; + while (vec.size() < numOfPixels) { + int row = rand() % SegmentationAlpide::NRows; + int col = rand() % SegmentationAlpide::NCols; + PixelData pixel(row, col); + vec.push_back(pixel); + } + std::sort(vec.begin(), vec.end()); + if (!mDoErrors) { + // If errors are disabled, chips should not contain + // pixels fired multiple times + auto iter = std::unique(vec.begin(), vec.end()); + vec.erase(iter, vec.end()); + } + return vec; +} + +void ITSDataSimulator::simulate() +{ + // Generate the chip data + std::map> chipData; + while (chipData.size() < mNumberOfChips) { + uint32_t chipID = rand() % MaxChipID; + if (!chipData.contains(chipID)) { + chipData.emplace(chipID, generateChipData()); + } + } + + if (mDoDigits) { + std::vector digVec; + for (auto const& chip : chipData) { + uint32_t chipID = chip.first; + const std::vector& pixels = chip.second; + for (auto pixel : pixels) { + Digit dig(chipID, pixel.getRow(), pixel.getCol()); + digVec.push_back(dig); + } + } + // TODO: Save the digits to a file + } +} + +int main(int argc, char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + "Simulates ALPIDE data\n"); + bpo::options_description opt_hidden(""); + bpo::options_description opt_all; + bpo::positional_options_description opt_pos; + + try { + auto add_option = opt_general.add_options(); + add_option("help,h", "Print this help message"); + add_option("verbosity,v", bpo::value()->default_value(0), + "verbosity level [0 = no output]"); + add_option("digits", + bpo::value()->default_value(false)->implicit_value(true), + "generate the data in the digits format"); + add_option("enable-errors", + bpo::value()->default_value(false)->implicit_value(true), + "enable additon of errors to the raw data"); + add_option("seed", bpo::value()->default_value(0), + "random seed for data generation"); + add_option( + "max-pixels-per-chip", bpo::value()->default_value(100), + ("maximum number of fired pixels per chip (0 - " + + std::to_string(ITSDataSimulator::MaxPixelsPerChip) + + ")") + .c_str()); + add_option("number-of-chip", bpo::value()->default_value(10), + ("number of chips to be present in the data (0 - " + + std::to_string(ITSDataSimulator::MaxChipID) + ")") + .c_str()); + add_option("configKeyValues", bpo::value()->default_value(""), + "comma-separated configKeyValues"); + + opt_all.add(opt_general).add(opt_hidden); + bpo::store(bpo::command_line_parser(argc, argv) + .options(opt_all) + .positional(opt_pos) + .run(), + vm); + + if (vm.count("help")) { + std::cout << opt_general << std::endl; + exit(0); + } + + if (vm["max-pixels-per-chip"].as() > + ITSDataSimulator::MaxPixelsPerChip) { + std::cerr << "Invalid max pixels per chip, valid range (0, " + << ITSDataSimulator::MaxPixelsPerChip << ")" << std::endl; + exit(1); + } + + if (vm["number-of-chip"].as() > ITSDataSimulator::MaxChipID) { + std::cerr << "Invalid number of chips, valid range (0, " + << ITSDataSimulator::MaxChipID << ")" << std::endl; + exit(1); + } + + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << "ERROR: " << e.what() << std::endl + << std::endl; + std::cerr << opt_general << std::endl; + exit(1); + } catch (std::exception& e) { + std::cerr << e.what() << ", application will now exit" << std::endl; + exit(2); + } + + ITSDataSimulator simulator( + vm["seed"].as(), vm["number-of-chip"].as(), + vm["max-pixels-per-chip"].as(), vm["digits"].as(), + vm["enable-errors"].as()); + + simulator.simulate(); +} diff --git a/Detectors/ITSMFT/ITS/simulation/src/ITSSimParam.cxx b/Detectors/ITSMFT/ITS/simulation/src/ITSSimParam.cxx new file mode 100644 index 0000000000000..564e9942a26d5 --- /dev/null +++ b/Detectors/ITSMFT/ITS/simulation/src/ITSSimParam.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITSSimulation/ITSSimParam.h" +O2ParamImpl(o2::its::ITSSimParam); diff --git a/Detectors/ITSMFT/ITS/simulation/src/ITSSimulationLinkDef.h b/Detectors/ITSMFT/ITS/simulation/src/ITSSimulationLinkDef.h index 92e10577b6abf..bd4ce228deb1b 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/ITSSimulationLinkDef.h +++ b/Detectors/ITSMFT/ITS/simulation/src/ITSSimulationLinkDef.h @@ -23,5 +23,6 @@ #pragma link C++ class o2::its::Detector + ; #pragma link C++ class o2::its::DescriptorInnerBarrelITS2 + ; #pragma link C++ class o2::base::DetImpl < o2::its::Detector> + ; +#pragma link C++ class o2::its::ITSSimParam + ; #endif diff --git a/Detectors/ITSMFT/ITS/simulation/src/V3Cage.cxx b/Detectors/ITSMFT/ITS/simulation/src/V3Cage.cxx index 1b09c1cad6e24..bd9ce1cd333a2 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/V3Cage.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/V3Cage.cxx @@ -19,13 +19,13 @@ #include // for LOG -//#include // for TGeoArb8 -//#include // for TGeoBBox -//#include // for TGeoConeSeg, TGeoCone +#include // for TGeoArb8 +#include // for TGeoBBox +#include // for TGeoConeSeg, TGeoCone #include // for TGeoPcon #include // for TGeoManager, gGeoManager #include // for TGeoCombiTrans, TGeoRotation, etc -//#include // for TGeoTrd1 +// #include // for TGeoTrd1 #include // for TGeoTube, TGeoTubeSeg #include // for TGeoVolume, TGeoVolumeAssembly #include // for TGeoXtru @@ -79,6 +79,8 @@ const Double_t V3Cage::sCageSidePanelGuideInHi = 204.0 * sMm; const Double_t V3Cage::sCageSidePanelGuideWide = 44.0 * sMm; const Double_t V3Cage::sCageSidePanelGuidThik1 = 6.0 * sMm; const Double_t V3Cage::sCageSidePanelGuidThik2 = 8.0 * sMm; +const Double_t V3Cage::sCageSidePanelMidBarWid = 15.0 * sMm; +const Double_t V3Cage::sCageSidePanelSidBarWid = 15.0 * sMm; const Double_t V3Cage::sCageSidePanelRail1Ypos[2] = {226.5 * sMm, 147.5 * sMm}; const Double_t V3Cage::sCageSidePanelRail2Ypos = 74.5 * sMm; @@ -106,6 +108,87 @@ const Double_t V3Cage::sCageECCableCrosInThik = 4 * sMm; const Double_t V3Cage::sCageECCableCrosInZLen = 10.2 * sMm; const Double_t V3Cage::sCageECCableCrosSidWid = 8 * sMm; +const Double_t V3Cage::sBPSuppCollarIntD = 53 * sMm; +const Double_t V3Cage::sBPSuppCollarExtD = 57 * sMm; +const Double_t V3Cage::sBPSuppCollarBushD = 52 * sMm; +const Double_t V3Cage::sBPSuppUpperCollarLen = 78 * sMm; +const Double_t V3Cage::sBPSuppUpperCollarHei = 4 * sMm; +const Double_t V3Cage::sBPSuppLowerCollarLen = 151 * sMm; +const Double_t V3Cage::sBPSuppLowerCollarTlX = 40.5 * sMm; +const Double_t V3Cage::sBPSuppLowCollHolDist = 100 * sMm; +const Double_t V3Cage::sBPSuppLowCollTailHei = 6 * sMm; +const Double_t V3Cage::sBPSuppCollarBeamLen = 370 * sMm; +const Double_t V3Cage::sBPSuppCollarBeamWid = 40 * sMm; +const Double_t V3Cage::sBPSuppCollarBeamHei = 12 * sMm; +const Double_t V3Cage::sBPSuppBracketTotLen = 57 * sMm; +const Double_t V3Cage::sBPSuppBracketWidth = 25 * sMm; +const Double_t V3Cage::sBPSuppBracketInLen = 20 * sMm; +const Double_t V3Cage::sBPSuppBracketInHei = 8 * sMm; +const Double_t V3Cage::sBPSuppBracketTailLen = 18.5 * sMm; +const Double_t V3Cage::sBPSuppBracketTailHei = 3 * sMm; +const Double_t V3Cage::sBPSuppBrktCentHoleX = 31.5 * sMm; +const Double_t V3Cage::sBPSuppBrktCentHoleD = 6 * sMm; +const Double_t V3Cage::sBPSuppBrktLatHoleX = 24.5 * sMm; +const Double_t V3Cage::sBPSuppBrktLatHoleD = 3.2 * sMm; +const Double_t V3Cage::sBPSuppBrktLatHoleW = 4 * sMm; +const Double_t V3Cage::sBPSuppBrktLatHoleH = 2.5 * sMm; +const Double_t V3Cage::sBPSuppBrktHolesY = 0.5 * sMm; +const Double_t V3Cage::sBPSuppCollarM4High = 2.2 * sMm; +const Double_t V3Cage::sBPSuppCollarM4Diam = 7.5 * sMm; +const Double_t V3Cage::sBPSuppCollarM4XDist = 68 * sMm; +const Double_t V3Cage::sBPSuppCollarM4ZPos = 7 * sMm; +const Double_t V3Cage::sBPSuppClampTotLen = 55 * sMm; +const Double_t V3Cage::sBPSuppClampTotWid = 23 * sMm; +const Double_t V3Cage::sBPSuppClampTotHei = 13 * sMm; +const Double_t V3Cage::sBPSuppClampLatThick = 5 * sMm; +const Double_t V3Cage::sBPSuppClampShelfLen = 25 * sMm; +const Double_t V3Cage::sBPSuppClampShelfHei = 4.5 * sMm; +const Double_t V3Cage::sBPSuppClampsXDist = 944 * sMm; +const Double_t V3Cage::sBPSuppClampInsDmin = 7 * sMm; +const Double_t V3Cage::sBPSuppClampInsDmax = 11 * sMm; +const Double_t V3Cage::sBPSuppClampInsH = 2.9 * sMm; +const Double_t V3Cage::sBPSuppClampInsXPos = 15 * sMm; +const Double_t V3Cage::sBPSuppClampInsZPos = 7 * sMm; +const Double_t V3Cage::sBPSuppClampShimLen = 26 * sMm; +const Double_t V3Cage::sBPSuppClampShimWid = 15 * sMm; +const Double_t V3Cage::sBPSuppClampShimThick = 2.5 * sMm; +const Double_t V3Cage::sBPSuppClampM5High = 2.7 * sMm; +const Double_t V3Cage::sBPSuppClampM5Diam = 8.5 * sMm; +const Double_t V3Cage::sBPSuppClampM5ZPos = 20 * sMm; +const Double_t V3Cage::sBPSuppZPos = 1801 * sMm; + +const Double_t V3Cage::sCageCrossXWidthTot = 973 * sMm; +const Double_t V3Cage::sCageCrossXWidthExt = 944 * sMm; +const Double_t V3Cage::sCageCrossXWidthInt = 904 * sMm; +const Double_t V3Cage::sCageCrossYHeightTot = 244 * sMm; +const Double_t V3Cage::sCageCrossYHeightInt = 220 * sMm; +const Double_t V3Cage::sCageCrossYMid = (126 + 5.5) * sMm; +const Double_t V3Cage::sCageCrossZLength = 8 * sMm; +const Double_t V3Cage::sCageCrossBarThick = 20 * sMm; +const Double_t V3Cage::sCageCrossBarPhi = 25; // Deg + +// MFT rails inside Cage +const Double_t V3Cage::sCageMFTRailZLen = 1874 * sMm; +const Double_t V3Cage::sCageMFTRailZPos = 6.3 * sMm; +const Double_t V3Cage::sCageMFTRailTotWidth = 27 * sMm; +const Double_t V3Cage::sCageMFTRailExtWidth = 24 * sMm; +const Double_t V3Cage::sCageMFTRailIntWidth = 17.5 * sMm; +const Double_t V3Cage::sCageMFTRailBaseWidth = 22 * sMm; +const Double_t V3Cage::sCageMFTRailTotHeight = 8.9 * sMm; +const Double_t V3Cage::sCageMFTRailExtHeight = 5.9 * sMm; +const Double_t V3Cage::sCageMFTRailIntHeight = 3.5 * sMm; +const Double_t V3Cage::sCageMFTRailsXDist = 44 * sMm; + +const Double_t V3Cage::sCageMFTHingeTotWid = 164 * sMm; +const Double_t V3Cage::sCageMFTHingeIntWid = 141.3 * sMm; +const Double_t V3Cage::sCageMFTHingeHeight = 8 * sMm; +const Double_t V3Cage::sCageMFTHingeIntHei = 6 * sMm; +const Double_t V3Cage::sCageMFTHingeTotLen = 41 * sMm; +const Double_t V3Cage::sCageMFTHingeIntLen = 28 * sMm; +const Double_t V3Cage::sCageMFTHingeBulgeWid = 10 * sMm; +const Double_t V3Cage::sCageMFTHingeBulgeHei = 10 * sMm; +const Double_t V3Cage::sCageMFTHingeBulgePos = 7 * sMm; + ClassImp(V3Cage); V3Cage::V3Cage() @@ -137,13 +220,15 @@ void V3Cage::createAndPlaceCage(TGeoVolume* mother, const TGeoManager* mgr) // // Local variables - Double_t zunit, xpos, zpos; + Double_t zunit, xpos, ypos, zpos; // Create the cover elements TGeoVolume* cageCover = createCageCover(mgr); TGeoVolume* cageCoverRib = createCageCoverRib(mgr); TGeoVolume* cageEndCap = createCageEndCap(mgr); TGeoVolume* cageSidePanel = createCageSidePanel(mgr); + TGeoVolume* cageBPSupport = createBeamPipeSupport(mgr); + TGeoVolume* cageClosingCross = createCageClosingCross(mgr); // Now place all elements mother->AddNode(cageCover, 1, new TGeoTranslation(0, sCageYInBarrel, 0)); @@ -170,6 +255,10 @@ void V3Cage::createAndPlaceCage(TGeoVolume* mother, const TGeoManager* mgr) mother->AddNode(cageSidePanel, 1, new TGeoTranslation(xpos, sCageYInBarrel, zposSP)); mother->AddNode(cageSidePanel, 2, new TGeoCombiTrans(-xpos, sCageYInBarrel, zposSP, new TGeoRotation("", 180, 0, 0))); + Double_t zposCC = -zpos + sCageSidePanelLength + sCageCrossZLength / 2; + mother->AddNode(cageClosingCross, 1, new TGeoTranslation(0, sCageYInBarrel, zposCC)); + mother->AddNode(cageClosingCross, 2, new TGeoCombiTrans(0, sCageYInBarrel, zposCC, new TGeoRotation("", 0, 180, 0))); + // The end cap is only on C side zpos += sCageECCableCrosTotZ / 2; mother->AddNode(cageEndCap, 1, new TGeoTranslation(0, sCageYInBarrel, -zpos)); @@ -179,6 +268,14 @@ void V3Cage::createAndPlaceCage(TGeoVolume* mother, const TGeoManager* mgr) mother->AddNode(cageCoverRib, 5, new TGeoTranslation(0, sCageYInBarrel, zpos)); mother->AddNode(cageCoverRib, 6, new TGeoCombiTrans(0, sCageYInBarrel, zpos, new TGeoRotation("", 180, 0, 0))); + // The Beam Pipe Support on A side + ypos = sCageYInBarrel - sBPSuppLowCollTailHei / 2; + zpos = sBPSuppZPos + sBPSuppCollarBeamWid / 2; + mother->AddNode(cageBPSupport, 1, new TGeoTranslation(0, ypos, zpos)); + + // The MFT Rails inside the Cage + createAndPlaceMFTRailsInsideCage(mother, mgr); + return; } @@ -347,6 +444,7 @@ TGeoVolume* V3Cage::createCageSidePanel(const TGeoManager* mgr) // The side panel as a TGeoVolumeAssembly // // Created: 30 Sep 2022 Mario Sitta + // Updated: 20 May 2023 Mario Sitta Mid and side bars added // // Local variables @@ -377,6 +475,18 @@ TGeoVolume* V3Cage::createCageSidePanel(const TGeoManager* mgr) // The shortest rails TGeoCompositeShape* rail3Sh = createCageSidePanelRail(sCageSidePanelRail3Len, 3); + // The middle bar: a BBox + xlen = sCageSidePanelCoreThick / 2; + ylen = sCageSidePanelMidBarWid / 2; + zlen = (sCageSidePanelLength - sCageSidePanelRail3Len - sCageSidePanelSidBarWid) / 2; + TGeoBBox* midBarSh = new TGeoBBox(xlen, ylen, zlen); + + // The side bar: a BBox + xlen = sCageSidePanelCoreThick / 2; + ylen = sCageSidePanelWidth / 2; + zlen = sCageSidePanelSidBarWid / 2; + TGeoBBox* sidBarSh = new TGeoBBox(xlen, ylen, zlen); + // The elements of the guide: // - the vertical part: a BBox xlen = sCageSidePanelGuidThik2 / 2; @@ -433,6 +543,14 @@ TGeoVolume* V3Cage::createCageSidePanel(const TGeoManager* mgr) rail3Vol->SetFillColor(kGray); rail3Vol->SetLineColor(kGray); + TGeoVolume* midBarVol = new TGeoVolume("CageSidePanelMiddleBar", midBarSh, medAlAlloy); + midBarVol->SetFillColor(kGray); + midBarVol->SetLineColor(kGray); + + TGeoVolume* sidBarVol = new TGeoVolume("CageSidePanelSideBar", sidBarSh, medAlAlloy); + sidBarVol->SetFillColor(kGray); + sidBarVol->SetLineColor(kGray); + TGeoVolume* guideVol = new TGeoVolume("CageSidePanelGuide", guideSh, medFabric); guideVol->SetFillColor(kViolet); guideVol->SetLineColor(kViolet); @@ -464,6 +582,12 @@ TGeoVolume* V3Cage::createCageSidePanel(const TGeoManager* mgr) sidePanelVol->AddNode(rail3Vol, j + 4, new TGeoTranslation(xpos, -ypos, zpos)); } + zpos = sCageSidePanelLength / 2 - midBarSh->GetDZ() - sCageSidePanelSidBarWid; + sidePanelVol->AddNode(midBarVol, 1, new TGeoTranslation(0, 0, -zpos)); + + zpos = sCageSidePanelLength / 2 - sidBarSh->GetDZ(); + sidePanelVol->AddNode(sidBarVol, 1, new TGeoTranslation(0, 0, -zpos)); + xpos = sCageSidePanelCoreThick / 2 + sCageSidePanelFoilThick + sCageSidePanelGuidThik2 / 2; zpos = (sCageSidePanelLength - sCageSidePanelGuideLen) / 2; sidePanelVol->AddNode(guideVol, 1, new TGeoTranslation(xpos, 0, -zpos)); @@ -488,6 +612,7 @@ TGeoCompositeShape* V3Cage::createCageSidePanelCoreFoil(const Double_t xthick, c // The side panel core or foil as a TGeoCompositeShape // // Created: 07 Oct 2022 Mario Sitta + // Updated: 20 May 2023 Mario Sitta Mid and side bars added // // Local variables @@ -550,6 +675,30 @@ TGeoCompositeShape* V3Cage::createCageSidePanelCoreFoil(const Double_t xthick, c rail3Mat[j + 3]->RegisterYourself(); } + // The hole for the middle bar: a BBox + xlen = 1.1 * sCageSidePanelCoreThick / 2; + ylen = sCageSidePanelMidBarWid / 2; + zlen = (sCageSidePanelLength - sCageSidePanelRail3Len) / 2; + TGeoBBox* midBarHol = new TGeoBBox(xlen, ylen, zlen); + midBarHol->SetName("midbar"); + + zpos = sCageSidePanelRail3Len / 2; + TGeoTranslation* midBarMat = new TGeoTranslation(0, 0, -zpos); + midBarMat->SetName("midbarmat"); + midBarMat->RegisterYourself(); + + // The hole for the side bar: a BBox + xlen = 1.1 * sCageSidePanelCoreThick / 2; + ylen = 1.1 * sCageSidePanelWidth / 2; + zlen = sCageSidePanelSidBarWid; + TGeoBBox* sidBarHol = new TGeoBBox(xlen, ylen, zlen); + sidBarHol->SetName("sidebar"); + + zpos = sCageSidePanelLength / 2; + TGeoTranslation* sidBarMat = new TGeoTranslation(0, 0, -zpos); + sidBarMat->SetName("sidebarmat"); + sidBarMat->RegisterYourself(); + // The actual shape: a CompositeShape TString compoShape = Form("%sbodyshape", shpref); for (Int_t j = 0; j < 4; j++) { @@ -562,6 +711,11 @@ TGeoCompositeShape* V3Cage::createCageSidePanelCoreFoil(const Double_t xthick, c compoShape += Form("-%sshortrail:shortrailmat%d", shpref, j); } + // The mid and side bar holes are present only in the core shape + if (strcmp(shpref, "core") == 0) { + compoShape += "-midbar:midbarmat-sidebar:sidebarmat"; + } + TGeoCompositeShape* corefoilSh = new TGeoCompositeShape(compoShape); // Now return the shape @@ -898,3 +1052,861 @@ TGeoCompositeShape* V3Cage::createCageEndCapCableCross(const TGeoManager* mgr) return cableCross; } + +TGeoVolume* V3Cage::createBeamPipeSupport(const TGeoManager* mgr) +{ + // + // Creates the Beam Pipe Support inside the Cage on the A side + // (from drawings ALIITSUP1064, ALIITSUP1059, ALIITSUP1057, ALIITSUP1058, + // ALIITSUP1056, ALIITSUP0823, ALIITSUP0273, ALIITSUP1060, ALIITSUP1062) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // The beam pipe support as a TGeoVolumeAssembly + // + // Created: 03 Jun 2023 Mario Sitta + // + + // Local variables + const Int_t nv = 8; + Double_t xv[nv], yv[nv]; + Double_t xpos, ypos, zpos; + + // The TGeoVolumeAssembly holding all elements + TGeoVolumeAssembly* bpSuppVol = new TGeoVolumeAssembly("CageBeamPipeSupport"); + + // The lower collar + TGeoCompositeShape* lowCollarSh = createBPSuppLowerCollar(); + + // The upper collar + TGeoCompositeShape* upCollarSh = createBPSuppUpperCollar(); + + // Each one of the collar beams + TGeoCompositeShape* collarBeamSh = createBPSuppCollarBeam(); + + // Each one of the lateral brackets + TGeoCompositeShape* bracketSh = createBPSuppBracket(); + + // Each one of the lateral clamps + TGeoCompositeShape* clampSh = createBPSuppClamp(); + + // The Vespel bushing: a Tube + TGeoTube* bushSh = new TGeoTube(0.5 * sBPSuppCollarBushD, 0.5 * sBPSuppCollarIntD, 0.5 * sBPSuppBracketWidth); + + // The clamp shim: a BBox + TGeoBBox* shimSh = new TGeoBBox(0.5 * sBPSuppClampShimWid, 0.5 * sBPSuppClampShimThick, 0.5 * sBPSuppClampShimLen); + + // The M4 screw head: a Tube + TGeoTube* m4ScrewSh = new TGeoTube(0, 0.5 * sBPSuppCollarM4Diam, 0.5 * sBPSuppCollarM4High); + + // The M5 screw head: a Tube + TGeoTube* m5ScrewSh = new TGeoTube(0, 0.5 * sBPSuppClampM5Diam, 0.5 * sBPSuppClampM5High); + + // The threaded insert head: a Cone + TGeoCone* insHeadSh = new TGeoCone(0.5 * sBPSuppClampInsH, 0, 0.5 * sBPSuppClampInsDmin, 0, 0.5 * sBPSuppClampInsDmax); + + // We have all the shapes: now create the real volumes + TGeoMedium* medCFRP = mgr->GetMedium(Form("%s_CFRP$", GetDetName())); + TGeoMedium* medTitanium = mgr->GetMedium(Form("%s_TITANIUM$", GetDetName())); + TGeoMedium* medSteel = mgr->GetMedium(Form("%s_INOX304$", GetDetName())); + TGeoMedium* medBrass = mgr->GetMedium(Form("%s_BRASS$", GetDetName())); + TGeoMedium* medVespel = mgr->GetMedium(Form("%s_VESPEL$", GetDetName())); + + Color_t kTitanium = kGray + 1; // Darker gray + + TGeoVolume* lowCollarVol = new TGeoVolume("BPSupportLowerCollar", lowCollarSh, medTitanium); + lowCollarVol->SetFillColor(kTitanium); + lowCollarVol->SetLineColor(kTitanium); + + TGeoVolume* upCollarVol = new TGeoVolume("BPSupportUpperCollar", upCollarSh, medTitanium); + upCollarVol->SetFillColor(kTitanium); + upCollarVol->SetLineColor(kTitanium); + + TGeoVolume* bushVol = new TGeoVolume("BPSupportCollarBushing", bushSh, medVespel); + bushVol->SetFillColor(kGreen); + bushVol->SetLineColor(kGreen); + + TGeoVolume* collarBeamVol = new TGeoVolume("BPSupportCollarBeam", collarBeamSh, medCFRP); + collarBeamVol->SetFillColor(kBlue); + collarBeamVol->SetLineColor(kBlue); + + TGeoVolume* bracketVol = new TGeoVolume("BPSupportBracket", bracketSh, medTitanium); + bracketVol->SetFillColor(kTitanium); + bracketVol->SetLineColor(kTitanium); + + TGeoVolume* clampVol = new TGeoVolume("BPSupportClamp", clampSh, medTitanium); + clampVol->SetFillColor(kTitanium); + clampVol->SetLineColor(kTitanium); + + TGeoVolume* shimVol = new TGeoVolume("BPSupportClampShim", shimSh, medBrass); + shimVol->SetFillColor(kOrange - 4); // Brownish + shimVol->SetLineColor(kOrange - 4); + + TGeoVolume* m4ScrewVol = new TGeoVolume("BPSupportCollarScrew", m4ScrewSh, medTitanium); + m4ScrewVol->SetFillColor(kTitanium); + m4ScrewVol->SetLineColor(kTitanium); + + TGeoVolume* m5ScrewVol = new TGeoVolume("BPSupportClampScrew", m5ScrewSh, medSteel); + m5ScrewVol->SetFillColor(kGray); + m5ScrewVol->SetLineColor(kGray); + + TGeoVolume* insHeadVol = new TGeoVolume("BPSupportClampInsert", insHeadSh, medSteel); + insHeadVol->SetFillColor(kGray); + insHeadVol->SetLineColor(kGray); + + // Then build up the beam support + bpSuppVol->AddNode(lowCollarVol, 1, nullptr); + + ypos = sBPSuppLowCollTailHei / 2; + bpSuppVol->AddNode(upCollarVol, 1, new TGeoTranslation(0, ypos, 0)); + + bpSuppVol->AddNode(bushVol, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = sBPSuppCollarM4XDist / 2; + ypos += (sBPSuppUpperCollarHei + m4ScrewSh->GetDz()); + zpos = sBPSuppCollarM4ZPos; + bpSuppVol->AddNode(m4ScrewVol, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(m4ScrewVol, 2, new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(m4ScrewVol, 3, new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(m4ScrewVol, 4, new TGeoCombiTrans(-xpos, ypos, -zpos, new TGeoRotation("", 0, 90, 0))); + + xpos = sBPSuppLowerCollarLen / 2 - sBPSuppBracketInLen + sBPSuppCollarBeamLen / 2; + bpSuppVol->AddNode(collarBeamVol, 1, new TGeoCombiTrans(xpos, 0, 0, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(collarBeamVol, 2, new TGeoCombiTrans(-xpos, 0, 0, new TGeoRotation("", 0, 90, 0))); + + xpos += (sBPSuppCollarBeamLen / 2 - sBPSuppBracketInLen); + bpSuppVol->AddNode(bracketVol, 1, new TGeoTranslation(xpos, 0, 0)); + bpSuppVol->AddNode(bracketVol, 2, new TGeoCombiTrans(-xpos, 0, 0, new TGeoRotation("", 90, 180, -90))); + + xpos = 0.5 * sBPSuppClampsXDist - sBPSuppClampTotWid + shimSh->GetDX(); + ypos = -shimSh->GetDY(); + bpSuppVol->AddNode(shimVol, 1, new TGeoTranslation(xpos, ypos, 0)); + bpSuppVol->AddNode(shimVol, 2, new TGeoTranslation(-xpos, ypos, 0)); + + xpos = 0.5 * sBPSuppClampsXDist - sBPSuppClampLatThick; + ypos -= shimSh->GetDY(); + bpSuppVol->AddNode(clampVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + bpSuppVol->AddNode(clampVol, 2, new TGeoCombiTrans(xpos, ypos, 0, new TGeoRotation("", 90, 180, -90))); + + xpos -= m5ScrewSh->GetDz(); + ypos += (0.5 * sBPSuppClampTotHei - sBPSuppClampShelfHei); + zpos = sBPSuppClampM5ZPos; + bpSuppVol->AddNode(m5ScrewVol, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90, 90, -90))); + bpSuppVol->AddNode(m5ScrewVol, 2, new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", 90, 90, -90))); + bpSuppVol->AddNode(m5ScrewVol, 3, new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 90, 90, -90))); + bpSuppVol->AddNode(m5ScrewVol, 4, new TGeoCombiTrans(-xpos, ypos, -zpos, new TGeoRotation("", 90, 90, -90))); + + xpos = 0.5 * sBPSuppClampsXDist - sBPSuppClampInsXPos; + ypos = sBPSuppBracketTailHei + insHeadSh->GetDz(); + zpos = sBPSuppClampInsZPos; + bpSuppVol->AddNode(insHeadVol, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(insHeadVol, 2, new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(insHeadVol, 3, new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(insHeadVol, 4, new TGeoCombiTrans(-xpos, ypos, -zpos, new TGeoRotation("", 0, 90, 0))); + + // Finally return the beam pipe support volume + return bpSuppVol; +} + +TGeoCompositeShape* V3Cage::createBPSuppLowerCollar() +{ + // + // Creates the lower collar which actually supports the Beam Pipe + // (ALIITSUP1056) + // + // Input: + // + // Output: + // + // Return: + // The lower collar as a TGeoCompositeShape + // + // Created: 06 Jun 2023 Mario Sitta + // + + // Local variables + const Int_t nv = 12; + Double_t xv[nv], yv[nv], xy8[16]; + Double_t zlen; + Double_t xpos, ypos; + + // The lateral bracket: a Xtru + Double_t totlen = (sBPSuppLowerCollarLen - sBPSuppCollarIntD) / 2; + Double_t xtail = (sBPSuppLowCollHolDist - sBPSuppCollarIntD) / 2; + Double_t taillen = sBPSuppLowerCollarTlX - sBPSuppCollarIntD / 2; + + xv[0] = 0; + yv[0] = -sBPSuppCollarBeamHei / 2; + xv[1] = totlen - xtail; + yv[1] = yv[0]; + xv[2] = totlen - taillen; + yv[2] = -sBPSuppLowCollTailHei / 2; + xv[3] = totlen; + yv[3] = yv[2]; + xv[4] = xv[3]; + yv[4] = sBPSuppLowCollTailHei / 2; + xv[5] = xv[2]; + yv[5] = yv[4]; + xv[6] = xv[1]; + yv[6] = -yv[1]; + xv[7] = xv[0]; + yv[7] = yv[6]; + xv[8] = xv[7]; + yv[8] = sBPSuppBracketInHei / 2; + xv[9] = sBPSuppBracketInLen; + yv[9] = yv[8]; + xv[10] = xv[9]; + yv[10] = -yv[9]; + xv[11] = xv[0]; + yv[11] = yv[10]; + + zlen = sBPSuppBracketWidth / 2; + TGeoXtru* brktlat = new TGeoXtru(2); + brktlat->DefinePolygon(nv, xv, yv); + brktlat->DefineSection(0, -zlen); + brktlat->DefineSection(1, zlen); + brktlat->SetName("latBrackBody"); + + // The central hole in lateral bracket: a Tube + zlen = sBPSuppBracketWidth / 2 + 0.001; + TGeoTube* brktcenthole = new TGeoTube(0, sBPSuppBrktCentHoleD / 2, zlen); + brktcenthole->SetName("latBrackCentHole"); + + xpos = totlen - xtail; + TGeoTranslation* brktcenthmat = new TGeoTranslation(xpos, 0, 0); + brktcenthmat->SetName("latCentHoleMat"); + brktcenthmat->RegisterYourself(); + + // The lateral hole in lateral bracket: an Arb8 + // (array of vertices is in the form (x0, y0, x1, y1, ..., x7, y7) ) + xy8[0] = 0; + xy8[1] = 0; + xy8[2] = -sBPSuppBrktLatHoleW; + xy8[3] = -sBPSuppBrktLatHoleH / 2; + xy8[4] = xy8[2]; + xy8[5] = -xy8[3]; + xy8[6] = xy8[0]; + xy8[7] = xy8[1]; + for (Int_t i = 0; i < 8; i++) { // The opposite face + xy8[8 + i] = xy8[i]; + } + TGeoArb8* brktlathole = new TGeoArb8(zlen, xy8); + brktlathole->SetName("latBrackLatHole"); + + xpos = totlen - taillen; + TGeoTranslation* brktlathmat = new TGeoTranslation(xpos, 0, 0); + brktlathmat->SetName("latLatHoleMat"); + brktlathmat->RegisterYourself(); + + // The lateral bracket: a CompositeShape + TGeoCompositeShape* latbrkt = new TGeoCompositeShape("latBrackBody-latBrackCentHole:latCentHoleMat-latBrackLatHole:latLatHoleMat"); + latbrkt->SetName("lateralBracket"); + + // The lateral bracket matrices + xpos = sBPSuppLowerCollarLen / 2; + TGeoTranslation* latmat1 = new TGeoTranslation(-xpos, 0, 0); + latmat1->SetName("latBrackMat1"); + latmat1->RegisterYourself(); + + TGeoCombiTrans* latmat2 = new TGeoCombiTrans(xpos, 0, 0, new TGeoRotation("", 90, 180, -90)); + latmat2->SetName("latBrackMat2"); + latmat2->RegisterYourself(); + + // The collar: a TubeSeg + TGeoTubeSeg* collar = new TGeoTubeSeg(0.5 * sBPSuppCollarIntD, 0.5 * sBPSuppCollarExtD, 0.5 * sBPSuppBracketWidth, 180, 360); + collar->SetName("lowerCollar"); + + ypos = brktlat->GetY(4); // The upper face of the tail + TGeoTranslation* collmat = new TGeoTranslation(0, ypos, 0); + collmat->SetName("lowerCollMat"); + collmat->RegisterYourself(); + + // Finally create and return the lower collar + // (the origin of its reference system is at its center) + TGeoCompositeShape* collarShape = new TGeoCompositeShape("lowerCollar:lowerCollMat+lateralBracket:latBrackMat1+lateralBracket:latBrackMat2"); + + return collarShape; +} + +TGeoCompositeShape* V3Cage::createBPSuppUpperCollar() +{ + // + // Creates the upper collar of the Beam Pipe Support (ALIITSUP0823) + // + // Input: + // + // Output: + // + // Return: + // The upper collar as a TGeoCompositeShape + // + // Created: 07 Jun 2023 Mario Sitta + // + + // Local variables + Double_t xlen; + Double_t xpos, ypos; + + // The lateral plate: a BBox + xlen = (sBPSuppUpperCollarLen - sBPSuppCollarIntD) / 2; + TGeoBBox* plate = new TGeoBBox(0.5 * xlen, 0.5 * sBPSuppUpperCollarHei, 0.5 * sBPSuppBracketWidth); + plate->SetName("lateralPlate"); + + xpos = sBPSuppUpperCollarLen / 2 - plate->GetDX(); + ypos = plate->GetDY(); + TGeoTranslation* latplmat1 = new TGeoTranslation(xpos, ypos, 0); + latplmat1->SetName("lateralPlateMat1"); + latplmat1->RegisterYourself(); + + TGeoTranslation* latplmat2 = new TGeoTranslation(-xpos, ypos, 0); + latplmat2->SetName("lateralPlateMat2"); + latplmat2->RegisterYourself(); + + // The collar: a TubeSeg + TGeoTubeSeg* collar = new TGeoTubeSeg(0.5 * sBPSuppCollarIntD, 0.5 * sBPSuppCollarExtD, 0.5 * sBPSuppBracketWidth, 0, 180); + collar->SetName("upperCollar"); + + // Finally create and return the upper collar + // (the origin of its reference system is at its center) + TGeoCompositeShape* collarShape = new TGeoCompositeShape("upperCollar+lateralPlate:lateralPlateMat1+lateralPlate:lateralPlateMat2"); + + return collarShape; +} + +TGeoCompositeShape* V3Cage::createBPSuppCollarBeam() +{ + // + // Creates the collar beam (i.e. the lateral support bar) of the + // Beam Pipe Support (ALIITSUP1057) + // + // Input: + // + // Output: + // + // Return: + // The collar beam as a TGeoCompositeShape + // + // Created: 03 Jun 2023 Mario Sitta + // + + // Local variables + const Int_t nv = 8; + Double_t xv[nv], yv[nv]; + Double_t xlen, xwid, ylen, zlen; + Double_t xpos; + + // The central part: a Xtru + xlen = (sBPSuppCollarBeamLen - 2 * sBPSuppBracketInLen) / 2; + xwid = (sBPSuppCollarBeamWid - sBPSuppBracketWidth) / 2; + xv[0] = -xlen; + yv[0] = -sBPSuppBracketWidth / 2; + xv[1] = xv[0] + xwid; + yv[1] = -sBPSuppCollarBeamWid / 2; + xv[2] = -xv[1]; + yv[2] = yv[1]; + xv[3] = -xv[0]; + yv[3] = yv[0]; + for (Int_t i = 0; i < 4; i++) { // Reflect the lower half to the upper half + xv[4 + i] = xv[3 - i]; + yv[4 + i] = -yv[3 - i]; + } + + zlen = sBPSuppCollarBeamHei / 2; + TGeoXtru* colcent = new TGeoXtru(2); + colcent->SetName("collarCentral"); + colcent->DefinePolygon(nv, xv, yv); + colcent->DefineSection(0, -zlen); + colcent->DefineSection(1, zlen); + + // Each bracket insert: a BBox + xlen = sBPSuppBracketInLen / 2; + ylen = sBPSuppBracketWidth / 2; + zlen = sBPSuppBracketInHei / 2; + TGeoBBox* colins = new TGeoBBox("collarInsert", xlen, ylen, zlen); + + xpos = colcent->GetX(0) - colins->GetDX(); + TGeoTranslation* insmat1 = new TGeoTranslation(-xpos, 0, 0); + insmat1->SetName("colInsMat1"); + insmat1->RegisterYourself(); + + TGeoTranslation* insmat2 = new TGeoTranslation(xpos, 0, 0); + insmat2->SetName("colInsMat2"); + insmat2->RegisterYourself(); + + // Finally create and return the collar beam + // (the origin of its reference system is at its center) + TGeoCompositeShape* beamShape = new TGeoCompositeShape("collarCentral+collarInsert:colInsMat1+collarInsert:colInsMat2"); + + return beamShape; +} + +TGeoCompositeShape* V3Cage::createBPSuppBracket() +{ + // + // Creates the lateral Titanium bracket of the Beam Pipe Support + // (ALIITSUP1058) + // + // Input: + // + // Output: + // + // Return: + // The bracket as a TGeoCompositeShape + // + // Created: 04 Jun 2023 Mario Sitta + // + + // Local variables + const Int_t nv = 12; + Double_t xv[nv], yv[nv]; + Double_t zlen; + Double_t xpos; + + // The main body: a Xtru + xv[0] = 0; + yv[0] = -sBPSuppCollarBeamHei / 2; + xv[1] = sBPSuppBracketTotLen - sBPSuppBrktCentHoleX; + yv[1] = yv[0]; + xv[2] = sBPSuppBracketTotLen - sBPSuppBracketTailLen; + yv[2] = 0; + xv[3] = sBPSuppBracketTotLen; + yv[3] = yv[2]; + xv[4] = xv[3]; + yv[4] = sBPSuppBracketTailHei; + xv[5] = xv[2]; + yv[5] = yv[4]; + xv[6] = xv[1]; + yv[6] = -yv[1]; + xv[7] = xv[0]; + yv[7] = yv[6]; + xv[8] = xv[7]; + yv[8] = sBPSuppBracketInHei / 2; + xv[9] = sBPSuppBracketInLen; + yv[9] = yv[8]; + xv[10] = xv[9]; + yv[10] = -yv[9]; + xv[11] = xv[0]; + yv[11] = yv[10]; + + zlen = sBPSuppBracketWidth / 2; + TGeoXtru* brktbody = new TGeoXtru(2); + brktbody->DefinePolygon(nv, xv, yv); + brktbody->DefineSection(0, -zlen); + brktbody->DefineSection(1, zlen); + brktbody->SetName("bracketBody"); + + // The central hole: a Tube + zlen = sBPSuppBracketWidth / 2 + 0.001; + TGeoTube* brktcenthole = new TGeoTube(0, sBPSuppBrktCentHoleD / 2, zlen); + brktcenthole->SetName("bracketCentHole"); + + xpos = sBPSuppBracketTotLen - sBPSuppBrktCentHoleX; + TGeoTranslation* brktcenthmat = new TGeoTranslation(xpos, -sBPSuppBrktHolesY, 0); + brktcenthmat->SetName("bracketCentHMat"); + brktcenthmat->RegisterYourself(); + + // The lateral hole: a Tube + TGeoTube* brktlathole = new TGeoTube(0, sBPSuppBrktLatHoleD / 2, zlen); + brktlathole->SetName("bracketLatHole"); + + xpos = sBPSuppBracketTotLen - sBPSuppBrktLatHoleX; + TGeoTranslation* brktlathmat = new TGeoTranslation(xpos, sBPSuppBrktHolesY, 0); + brktlathmat->SetName("bracketLatHMat"); + brktlathmat->RegisterYourself(); + + // Finally create and return the bracket + // (the origin of its reference system is opposite to its tail) + TGeoCompositeShape* bracketShape = new TGeoCompositeShape("bracketBody-bracketCentHole:bracketCentHMat-bracketLatHole:bracketLatHMat"); + + return bracketShape; +} + +TGeoCompositeShape* V3Cage::createBPSuppClamp() +{ + // + // Creates the lateral Titanium clamp holding the Beam Pipe Support + // to the ITS Cage (ALIITSUP1060) + // + // Input: + // + // Output: + // + // Return: + // The clamp as a TGeoCompositeShape + // + // Created: 08 Jun 2023 Mario Sitta + // + + // Local variables + Double_t xlen, ylen, zlen; + Double_t xpos, ypos; + + // The vertical wall: a BBox + xlen = sBPSuppClampLatThick / 2; + ylen = sBPSuppClampTotHei / 2; + zlen = sBPSuppClampTotLen / 2; + TGeoBBox* clampwall = new TGeoBBox(xlen, ylen, zlen); + clampwall->SetName("clampWall"); + + xpos = -clampwall->GetDX(); + ypos = clampwall->GetDY() - sBPSuppClampShelfHei; + TGeoTranslation* clampwallmat = new TGeoTranslation(xpos, ypos, 0); + clampwallmat->SetName("clampWallMat"); + clampwallmat->RegisterYourself(); + + // The horizontal shelf: a BBox + xlen = (sBPSuppClampTotWid - sBPSuppClampLatThick) / 2; + ylen = sBPSuppClampShelfHei / 2; + zlen = sBPSuppClampShelfLen / 2; + TGeoBBox* clampshelf = new TGeoBBox(xlen, ylen, zlen); + clampshelf->SetName("clampShelf"); + + xpos = clampshelf->GetDX(); + ypos = -clampshelf->GetDY(); + TGeoTranslation* clampshelfmat = new TGeoTranslation(xpos, ypos, 0); + clampshelfmat->SetName("clampShelfMat"); + clampshelfmat->RegisterYourself(); + + // Finally create and return the clamp + // (the origin of its reference system is at the conjunction + // of the vertical wall with the horizontal shelf) + TGeoCompositeShape* clampShape = new TGeoCompositeShape("clampWall:clampWallMat+clampShelf:clampShelfMat"); + + return clampShape; +} + +TGeoVolume* V3Cage::createCageClosingCross(const TGeoManager* mgr) +{ + // + // Creates the Cage Closing Cross (from drawings ALIITSUP0242) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // The closing cross as a TGeoVolume + // + // Created: 29 May 2023 Mario Sitta + // + + // Local variables + const Int_t nv = 8; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos; + + TString compoShape; + + // A single vertical post: a Xtru + xv[0] = 0.; + yv[0] = 0.; + xv[1] = (sCageCrossXWidthTot - sCageCrossXWidthInt) / 2; + yv[1] = yv[0]; + xv[2] = xv[1]; + yv[2] = (sCageCrossYHeightTot - sCageCrossYHeightInt) / 2; + xv[3] = (sCageCrossXWidthExt - sCageCrossXWidthInt) / 2; + yv[3] = yv[2]; + xv[4] = xv[3]; + yv[4] = yv[3] + sCageCrossYHeightInt; + xv[5] = xv[2]; + yv[5] = yv[4]; + xv[6] = xv[5]; + yv[6] = sCageCrossYHeightTot; + xv[7] = xv[0]; + yv[7] = yv[6]; + + zlen = sCageCrossZLength / 2; + + TGeoXtru* vpost = new TGeoXtru(2); + vpost->SetName("crossvertpost"); + vpost->DefinePolygon(nv, xv, yv); + vpost->DefineSection(0, -zlen); + vpost->DefineSection(1, zlen); + + // The vertical post matrices + xpos = sCageCrossXWidthInt / 2; + TGeoTranslation* vpostmat1 = new TGeoTranslation("vertpostmat1", xpos, 0, 0); + vpostmat1->RegisterYourself(); + + TGeoCombiTrans* vpostmat2 = new TGeoCombiTrans(-xpos, 0, 0, new TGeoRotation("", 90, 180, -90)); + vpostmat2->SetName("vertpostmat2"); + vpostmat2->RegisterYourself(); + + compoShape = Form("crossvertpost:vertpostmat1+crossvertpost:vertpostmat2"); + + // A single oblique post: a BBox + Double_t leg = vpost->GetY(4); + xlen = TMath::Sqrt(sCageCrossXWidthInt * sCageCrossXWidthInt + leg * leg) / 2; + ylen = sCageCrossBarThick / 2; + TGeoBBox* xpost = new TGeoBBox("crossoblqpost", xlen, ylen, zlen); + + // The oblique post matrices + Double_t phi = sCageCrossBarPhi / 2; + ypos = sCageCrossYHeightTot - sCageCrossYMid; + + TGeoCombiTrans* xpostmat1 = new TGeoCombiTrans(0, ypos, 0, new TGeoRotation("", phi, 0, 0)); + xpostmat1->SetName("oblqpostmat1"); + xpostmat1->RegisterYourself(); + + TGeoCombiTrans* xpostmat2 = new TGeoCombiTrans(0, ypos, 0, new TGeoRotation("", -phi, 0, 0)); + xpostmat2->SetName("oblqpostmat2"); + xpostmat2->RegisterYourself(); + + compoShape += Form("+crossoblqpost:oblqpostmat1+crossoblqpost:oblqpostmat2"); + + // The actual closing cross shape: a CompositeShape + TGeoCompositeShape* closCrossSh = new TGeoCompositeShape(compoShape); + + // We have the shape: now create the real volume + TGeoMedium* medAl = mgr->GetMedium(Form("%s_ALUMINUM$", GetDetName())); + + TGeoVolume* closCrossVol = new TGeoVolume("CageClosingCross", closCrossSh, medAl); + closCrossVol->SetFillColor(kGray); + closCrossVol->SetLineColor(kGray); + + // Finally return the closing cross volume + return closCrossVol; +} + +void V3Cage::createAndPlaceMFTRailsInsideCage(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the MFT Rails located inside the Cage and place them + // + // Input: + // motmat : the material of the mother volume (for the container box) + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 10 May 2025 Mario Sitta + // Updated: 20 May 2025 Mario Sitta Hinges added + // + + // Local variables + Double_t rdist, rpos, xpos, yposup, yposdw, zpos, alpha; + Double_t xbox, ybox; + + // Create a pair of rails (a BBox container is returned) + TGeoVolume* cageMFTRails = createMFTRailsPair(mother->GetMedium(), mgr); + + // Create hinge holding a pair of rails + TGeoVolume* cageMFTRailsHinge = createMFTRailsHinge(mgr); + + // Now compute the radial distance and the XY position of the box + xbox = (static_cast(cageMFTRails->GetShape()))->GetDX(); + ybox = (static_cast(cageMFTRails->GetShape()))->GetDY(); + + rdist = TMath::Sqrt(sCageCoverRint * sCageCoverRint - xbox * xbox); + rpos = rdist - ybox; + + // Finally place the four pairs of rails inside the mother volume + xpos = rpos * TMath::Sin(sCageEndCapCableCutPhi * TMath::DegToRad()); + yposup = rpos * TMath::Cos(sCageEndCapCableCutPhi * TMath::DegToRad()) + sCageYInBarrel; + yposdw = rpos * TMath::Cos(sCageEndCapCableCutPhi * TMath::DegToRad()) - sCageYInBarrel; + zpos = sCageMFTRailZPos; + + alpha = -sCageEndCapCableCutPhi + 180; + mother->AddNode(cageMFTRails, 1, new TGeoCombiTrans(xpos, yposup, zpos, new TGeoRotation("", alpha, 0, 0))); + alpha = sCageEndCapCableCutPhi + 180; + mother->AddNode(cageMFTRails, 2, new TGeoCombiTrans(-xpos, yposup, zpos, new TGeoRotation("", alpha, 0, 0))); + + alpha = sCageEndCapCableCutPhi; + mother->AddNode(cageMFTRails, 3, new TGeoCombiTrans(xpos, -yposdw, zpos, new TGeoRotation("", alpha, 0, 0))); + alpha = -sCageEndCapCableCutPhi; + mother->AddNode(cageMFTRails, 4, new TGeoCombiTrans(-xpos, -yposdw, zpos, new TGeoRotation("", alpha, 0, 0))); + + // And the hinges too + xpos = rdist * TMath::Sin(sCageEndCapCableCutPhi * TMath::DegToRad()); + yposup = rdist * TMath::Cos(sCageEndCapCableCutPhi * TMath::DegToRad()) + sCageYInBarrel; + yposdw = rdist * TMath::Cos(sCageEndCapCableCutPhi * TMath::DegToRad()) - sCageYInBarrel; + zpos = (static_cast(cageMFTRails->GetShape()))->GetDZ() + sCageMFTRailZPos; + + alpha = sCageEndCapCableCutPhi; + mother->AddNode(cageMFTRailsHinge, 1, new TGeoCombiTrans(xpos, yposup, zpos, new TGeoRotation("", -alpha, 0, 0))); + mother->AddNode(cageMFTRailsHinge, 2, new TGeoCombiTrans(-xpos, yposup, zpos, new TGeoRotation("", alpha, 0, 0))); + + mother->AddNode(cageMFTRailsHinge, 3, new TGeoCombiTrans(xpos, -yposdw, zpos, new TGeoRotation("", 180 + alpha, 0, 0))); + mother->AddNode(cageMFTRailsHinge, 4, new TGeoCombiTrans(-xpos, -yposdw, zpos, new TGeoRotation("", 180 - alpha, 0, 0))); + + zpos = (static_cast(cageMFTRails->GetShape()))->GetDZ() - sCageMFTRailZPos; + mother->AddNode(cageMFTRailsHinge, 5, new TGeoCombiTrans(xpos, yposup, -zpos, new TGeoRotation("", 90, 180, -90 + alpha))); // On Z<0 apply 180deg rotation on Y axis + mother->AddNode(cageMFTRailsHinge, 6, new TGeoCombiTrans(-xpos, yposup, -zpos, new TGeoRotation("", 90, 180, -90 - alpha))); + + mother->AddNode(cageMFTRailsHinge, 7, new TGeoCombiTrans(xpos, -yposdw, -zpos, new TGeoRotation("", 90, 180, 90 - alpha))); + mother->AddNode(cageMFTRailsHinge, 8, new TGeoCombiTrans(-xpos, -yposdw, -zpos, new TGeoRotation("", 90, 180, 90 + alpha))); + + return; +} + +TGeoVolume* V3Cage::createMFTRailsPair(const TGeoMedium* motmed, const TGeoManager* mgr) +{ + // + // Creates a pair of MFT Rails located inside the Cage (from drawings + // ALI-MFT-DF-0057 and elements therein) + // A box containing a pair of rails is returned (a physical box + // is preferred over an Assembly for better performance) + // + // Input: + // motmed : the medium of the mother volume (for the container box) + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // A rail pair as a TGeoVolume + // + // Created: 10 May 2025 Mario Sitta + // + + // Local variables + const Int_t nv = 16; + Double_t xv[nv], yv[nv]; + Double_t deltah, xlen, ylen, zlen; + Double_t xpos, ypos; + + // The shape of a single rail: a Xtru + xv[0] = sCageMFTRailBaseWidth / 2; + yv[0] = 0.; + xv[1] = xv[0]; + yv[1] = sCageMFTRailTotHeight - sCageMFTRailExtHeight; + xv[2] = sCageMFTRailTotWidth / 2; + yv[2] = yv[1]; + xv[3] = xv[2]; + yv[3] = sCageMFTRailTotHeight; + xv[4] = sCageMFTRailIntWidth / 2; + yv[4] = yv[3]; + deltah = (sCageMFTRailExtHeight - sCageMFTRailIntHeight) / 2; + xv[5] = xv[4]; + yv[5] = yv[4] - deltah; + xv[6] = sCageMFTRailExtWidth / 2; + yv[6] = yv[5]; + xv[7] = xv[6]; + yv[7] = yv[6] - sCageMFTRailIntHeight; + + for (Int_t i = 8; i < nv; i++) { + xv[i] = -xv[15 - i]; + yv[i] = yv[15 - i]; + } + + zlen = sCageMFTRailZLen / 2; + + TGeoXtru* mftRailSh = new TGeoXtru(2); + mftRailSh->SetName("mftrailshape"); + mftRailSh->DefinePolygon(nv, xv, yv); + mftRailSh->DefineSection(0, -zlen); + mftRailSh->DefineSection(1, zlen); + + // The air container: a BBox + xlen = 2 * sCageMFTRailTotWidth + sCageMFTRailsXDist; + ylen = sCageMFTRailTotHeight / 2; + zlen = sCageMFTRailZLen / 2; + TGeoBBox* mftRailBoxSh = new TGeoBBox(xlen / 2, ylen, zlen); + + // We have the shape: now create the real volume + TGeoMedium* medAl = mgr->GetMedium(Form("%s_ALUMINUM$", GetDetName())); + + TGeoVolume* mftRailVol = new TGeoVolume("MFTRailInsideCage", mftRailSh, medAl); + mftRailVol->SetFillColor(kGray); + mftRailVol->SetLineColor(kGray); + + TGeoVolume* mftRailBoxVol = new TGeoVolume("MFTRailPairInsideCage", mftRailBoxSh, motmed); + + // Put the two rails inside the holding box + // (rail Y origin is on its lower face) + xpos = mftRailBoxSh->GetDX() - 0.5 * sCageMFTRailTotWidth; + ypos = mftRailBoxSh->GetDY(); + mftRailBoxVol->AddNode(mftRailVol, 1, new TGeoTranslation(xpos, -ypos, 0)); + mftRailBoxVol->AddNode(mftRailVol, 2, new TGeoTranslation(-xpos, -ypos, 0)); + + // Finally return the rails volume + return mftRailBoxVol; +} + +TGeoVolume* V3Cage::createMFTRailsHinge(const TGeoManager* mgr) +{ + // + // Creates a hinge holding a pair of MFT Rails to the Cage (from drawing + // ALIMFT-0042 and elements inside CAD files) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // A rail hinge as a TGeoVolume + // + // Created: 19 May 2025 Mario Sitta + // + + // Local variables + const Int_t nv = 6; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + TString compoShape; + + // The main body: a Xtru + xv[0] = sCageMFTHingeTotWid / 2; + yv[0] = 0; + xv[1] = xv[0]; + yv[1] = sCageMFTHingeIntHei; + xv[2] = sCageMFTHingeIntWid / 2; + yv[2] = sCageMFTHingeHeight; + + for (Int_t i = 3; i < nv; i++) { + xv[i] = -xv[5 - i]; + yv[i] = yv[5 - i]; + } + + zlen = sCageMFTHingeIntLen / 2; + + TGeoXtru* mftHingeBodySh = new TGeoXtru(2); + mftHingeBodySh->SetName("mfthingebodyshape"); + mftHingeBodySh->DefinePolygon(nv, xv, yv); + mftHingeBodySh->DefineSection(0, -zlen); + mftHingeBodySh->DefineSection(1, zlen); + + // The bulge: a BBox + xlen = sCageMFTHingeBulgeWid / 2; + ylen = sCageMFTHingeBulgeHei / 2; + zlen = (sCageMFTHingeTotLen - sCageMFTHingeIntLen) / 2; + TGeoBBox* mftHingeBulgeSh = new TGeoBBox("mfthingebulgeshape", xlen, ylen, zlen); + + // The actual hinge: a CompositeShape + xpos = mftHingeBodySh->GetX(0) - (sCageMFTHingeBulgePos + mftHingeBulgeSh->GetDX()); + ypos = mftHingeBodySh->GetY(2) - mftHingeBulgeSh->GetDY(); + zpos = mftHingeBodySh->GetZ(1) + mftHingeBulgeSh->GetDZ(); + + TGeoTranslation* bulgpos1 = new TGeoTranslation(xpos, ypos, zpos); + bulgpos1->SetName("bulge1pos"); + bulgpos1->RegisterYourself(); + + TGeoTranslation* bulgpos2 = new TGeoTranslation(-xpos, ypos, zpos); + bulgpos2->SetName("bulge2pos"); + bulgpos2->RegisterYourself(); + + compoShape = Form("mfthingebodyshape+mfthingebulgeshape:bulge1pos+mfthingebulgeshape:bulge2pos"); + + TGeoCompositeShape* mftRailHingeSh = new TGeoCompositeShape(compoShape); + + // We have the shape: now create the real volume + TGeoMedium* medAl = mgr->GetMedium(Form("%s_ALUMINUM$", GetDetName())); + + TGeoVolume* mftRailHingeVol = new TGeoVolume("MFTRailHingeInsideCage", mftRailHingeSh, medAl); + mftRailHingeVol->SetFillColor(kGreen); + mftRailHingeVol->SetLineColor(kGreen); + + // Finally return the hinge volume + return mftRailHingeVol; +} diff --git a/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx b/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx index 33a1bedec74eb..e930aa23de030 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx @@ -61,9 +61,15 @@ const Double_t V3Layer::sIBFPCAlAnodeWidth1 = 13.0 * sMm; const Double_t V3Layer::sIBFPCAlAnodeWidth2 = 14.7 * sMm; const Double_t V3Layer::sIBFlexCableKapThick = 75.0 * sMicron; const Double_t V3Layer::sIBFlexCablePolyThick = 20.0 * sMicron; -const Double_t V3Layer::sIBFlexCapacitorXWid = 0.2 * sMm; -const Double_t V3Layer::sIBFlexCapacitorYHi = 0.2 * sMm; -const Double_t V3Layer::sIBFlexCapacitorZLen = 0.4 * sMm; +const Double_t V3Layer::sIBFlexCapacitor1XWid = 0.5 * sMm; +const Double_t V3Layer::sIBFlexCapacitor1YHi = 0.5 * sMm; +const Double_t V3Layer::sIBFlexCapacitor1ZLen = 1.0 * sMm; +const Double_t V3Layer::sIBFlexCapacitor22XWid = 0.7 * sMm; +const Double_t V3Layer::sIBFlexCapacitor22YHi = 0.6 * sMm; +const Double_t V3Layer::sIBFlexCapacitor22ZLen = 1.1 * sMm; +const Double_t V3Layer::sIBFlexResistorXWid = 0.2 * sMm; +const Double_t V3Layer::sIBFlexResistorYHi = 0.2 * sMm; +const Double_t V3Layer::sIBFlexResistorZLen = 0.4 * sMm; const Double_t V3Layer::sIBColdPlateWidth = 15.4 * sMm; const Double_t V3Layer::sIBColdPlateZLen = 290.0 * sMm; const Double_t V3Layer::sIBGlueThick = 50.0 * sMicron; @@ -599,8 +605,11 @@ TGeoVolume* V3Layer::createModuleInnerB(const Double_t xchip, const Double_t zch // the module as a TGeoVolume // // Updated: 03 Apr 2021 + // Updated: 03 Nov 2025 Change volume from BBox to Xtru to avoid fake overlaps Double_t xtot, ytot, ztot; + Double_t ymid, shrinkFactor = 0.73; + Double_t xv[5], yv[5]; Double_t xpos, ypos, zpos; const Int_t nameLen = 30; char volumeName[nameLen]; @@ -619,9 +628,25 @@ TGeoVolume* V3Layer::createModuleInnerB(const Double_t xchip, const Double_t zch Double_t ygnd = (static_cast(aluGndCableVol->GetShape()))->GetDY(); Double_t yano = (static_cast(aluAnodeCableVol->GetShape()))->GetDY(); - ytot = sIBGlueThick / 2 + ygnd + sIBFlexCableKapThick / 2 + yano + sIBFlexCapacitorYHi / 2; + ytot = sIBGlueThick / 2 + ygnd + sIBFlexCableKapThick / 2 + yano + sIBFlexCapacitor22YHi / 2; + ymid = sIBGlueThick / 2 + ygnd + sIBFlexCableKapThick / 2 + yano; - TGeoBBox* module = new TGeoBBox(xtot, ytot, ztot); + xv[0] = xtot; + yv[0] = -ytot; + xv[1] = xv[0]; + yv[1] = yv[0] + 6 * ymid; + xv[2] = xtot * shrinkFactor; + yv[2] = ytot; + xv[3] = -xtot; + yv[3] = yv[2]; + xv[4] = xv[3]; + yv[4] = yv[0]; + + TGeoXtru* module = new TGeoXtru(2); + module->DefinePolygon(6, xv, yv); + module->DefinePolygon(5, xv, yv); + module->DefineSection(0, -ztot); + module->DefineSection(1, ztot); // Now the volumes TGeoMedium* medAir = mgr->GetMedium(Form("%s_AIR$", GetDetName())); @@ -674,6 +699,7 @@ void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yz // // Created: 13 Feb 2018 Mario Sitta // Updated: 03 Apr 2019 Mario Sitta Fix positions (180' rotation) + // Updated: 31 Oct 2025 Mario Sitta Fix dimensions and weight // // Position of the various capacitors (A.Junique private communication @@ -705,63 +731,72 @@ void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yz Double_t xpos, ypos, zpos; Int_t nCapacitors; - TGeoVolume *capacitor, *resistor; + TGeoVolume *capacitorSmall, *capacitorLarge, *resistor; - // Check whether we already have the volume, otherwise create it - // (so as to avoid creating multiple copies of the very same volume + // Check whether we already have the volumes, otherwise create them + // (so as to avoid creating multiple copies of the very same volumes // for each layer) - capacitor = mgr->GetVolume("IBFPCCapacitor"); + // The "small" capacitor is the 1 uF substrate capacitor + // The "large" capacitor is the 22 uF analog/digital PS capacitor + capacitorSmall = mgr->GetVolume("IBFPCCapacitorSmall"); - if (!capacitor) { - TGeoBBox* capsh = new TGeoBBox(sIBFlexCapacitorXWid / 2, sIBFlexCapacitorYHi / 2, sIBFlexCapacitorZLen / 2); + if (!capacitorSmall) { + TGeoBBox* capSmsh = new TGeoBBox(sIBFlexCapacitor1XWid / 2, sIBFlexCapacitor1YHi / 2, sIBFlexCapacitor1ZLen / 2); + TGeoBBox* capLgsh = new TGeoBBox(sIBFlexCapacitor22XWid / 2, sIBFlexCapacitor22YHi / 2, sIBFlexCapacitor22ZLen / 2); TGeoMedium* medCeramic = mgr->GetMedium(Form("%s_CERAMIC$", GetDetName())); - capacitor = new TGeoVolume("IBFPCCapacitor", capsh, medCeramic); - capacitor->SetLineColor(kBlack); - capacitor->SetFillColor(kBlack); + capacitorSmall = new TGeoVolume("IBFPCCapacitorSmall", capSmsh, medCeramic); + capacitorSmall->SetLineColor(kBlack); + capacitorSmall->SetFillColor(kBlack); + + capacitorLarge = new TGeoVolume("IBFPCCapacitorLarge", capLgsh, medCeramic); + capacitorLarge->SetLineColor(kBlack); + capacitorLarge->SetFillColor(kBlack); - TGeoBBox* ressh = new TGeoBBox(sIBFlexCapacitorXWid / 2, // Resistors have - sIBFlexCapacitorYHi / 2, // the same dim's - sIBFlexCapacitorZLen / 2); // as capacitors + TGeoBBox* ressh = new TGeoBBox(sIBFlexResistorXWid / 2, + sIBFlexResistorYHi / 2, + sIBFlexResistorZLen / 2); resistor = new TGeoVolume("IBFPCResistor", ressh, medCeramic); resistor->SetLineColor(kBlack); resistor->SetFillColor(kBlack); } else { // Volumes already defined, get them + capacitorLarge = mgr->GetVolume("IBFPCCapacitorLarge"); resistor = mgr->GetVolume("IBFPCResistor"); } // Place all the capacitors (they are really a lot...) - ypos = yzero + sIBFlexCapacitorYHi / 2; + ypos = yzero + sIBFlexCapacitor22YHi / 2; xpos = xGroup1A; for (Int_t j = 0; j < sIBChipsPerRow; j++) { zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup1A[0]; - modvol->AddNode(capacitor, 2 * j + 1, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorLarge, 2 * j + 1, new TGeoTranslation(-xpos, ypos, -zpos)); zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup1A[1]; - modvol->AddNode(capacitor, 2 * j + 2, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorLarge, 2 * j + 2, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors = 2 * sIBChipsPerRow; xpos = xGroup1B; for (Int_t j = 0; j < sIBChipsPerRow; j++) { zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup1B; - modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorLarge, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors += sIBChipsPerRow; + ypos = yzero + sIBFlexCapacitor1YHi / 2; xpos = xGroup2; // We have only 8 in these group, missing the central one for (Int_t j = 0; j < sIBChipsPerRow - 1; j++) { zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup2; - modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorSmall, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors += (sIBChipsPerRow - 1); xpos = xGroup3; zpos = zGroup3; - modvol->AddNode(capacitor, 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorSmall, 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); nCapacitors++; for (Int_t j = 0; j < sIBChipsPerRow; j++) { @@ -771,10 +806,11 @@ void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yz xpos = xGroup4[0]; } zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup4[j]; - modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorSmall, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors += sIBChipsPerRow; + ypos = yzero + sIBFlexCapacitor22YHi / 2; for (Int_t j = 0; j < nGroup5A; j++) { if (j == 0) { xpos = xGroup5A[0]; @@ -782,14 +818,14 @@ void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yz xpos = xGroup5A[1]; } zpos = zGroup5A[j]; - modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorLarge, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors += nGroup5A; xpos = xGroup5B; for (Int_t j = 0; j < nGroup5B; j++) { zpos = zGroup5B[j]; - modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorLarge, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } // Place the resistors @@ -1061,7 +1097,7 @@ TGeoVolume* V3Layer::createStaveModelInnerB4(const TGeoManager* mgr) yv[1] = layerHeight + sIBSideVertexHeight + topfil->GetDZ(); ; xv[2] = sIBEndSupportXUp / 2; - yv[2] = sIBStaveHeight + sIBTopFilamentSide / sinD(-theta); // theta is neg + yv[2] = sIBStaveHeight + sIBTopFilamentSide / sinD(-theta) - 0.01; // theta is neg for (Int_t i = 0; i < 3; i++) { xv[3 + i] = -xv[2 - i]; yv[3 + i] = yv[2 - i]; diff --git a/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx b/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx index 4bdcb6a19a590..03fae3e1f984c 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx @@ -27,7 +27,7 @@ #include // for TGeoPcon #include // for TGeoManager, gGeoManager #include // for TGeoCombiTrans, TGeoRotation, etc -//#include // for TGeoTrd1 +// #include // for TGeoTrd1 #include // for TGeoTube, TGeoTubeSeg #include // for TGeoVolume, TGeoVolumeAssembly #include // for TGeoXtru @@ -929,8 +929,8 @@ TGeoVolume* V3Services::ibCyssCylinder(const TGeoManager* mgr) TGeoTubeSeg* cyssInnerCylSh = new TGeoTubeSeg(rmin, rmax, zlen, phimin, phimax); // We have all shapes: now create the real volumes - TGeoMedium* medPrepreg = mgr->GetMedium(Form("%s_F6151B05M$", GetDetName())); - TGeoMedium* medRohacell = mgr->GetMedium(Form("%s_ROHACELL$", GetDetName())); + TGeoMedium* medPrepreg = mgr->GetMedium(Form("%s_AS4C200$", GetDetName())); + TGeoMedium* medRohacell = mgr->GetMedium(Form("%s_RIST110$", GetDetName())); TGeoVolume* cyssOuterCylVol = new TGeoVolume("IBCYSSCylinder", cyssOuterCylSh, medPrepreg); cyssOuterCylVol->SetLineColor(35); @@ -1051,8 +1051,8 @@ TGeoVolume* V3Services::ibCyssCone(const TGeoManager* mgr) cyssConeFoamSh->DefineSection(4, zlen1, rmin, rmax); // We have all shapes: now create the real volumes - TGeoMedium* medPrepreg = mgr->GetMedium(Form("%s_F6151B05M$", GetDetName())); - TGeoMedium* medRohacell = mgr->GetMedium(Form("%s_ROHACELL$", GetDetName())); + TGeoMedium* medPrepreg = mgr->GetMedium(Form("%s_AS4C200$", GetDetName())); + TGeoMedium* medRohacell = mgr->GetMedium(Form("%s_RIST110$", GetDetName())); TGeoVolume* cyssConeVol = new TGeoVolume("IBCYSSCone", cyssConeSh, medPrepreg); cyssConeVol->SetLineColor(35); @@ -2908,6 +2908,216 @@ TGeoCompositeShape* V3Services::ibConvWireOutSupport() return supportShape; } +void V3Services::createAllITSServices(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Steering method to creates the ITS services: tubes, cables and the like + // + // Input: + // motherVolume : the volume hosting the supports + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 12 Apr 2023 Mario Sitta First version (very roughly) + // + + // The present version is very rough: all services are approximated + // as cylinders/cones with a proper thickness to guess the total + // material budget (original code by A.Morsch) + // (some figures are hardcoded) + + // Inner Barrel + static const Double_t sIBServicesZIn = 35.0 * sCm; + static const Double_t sIBServicesZMid = 44.0 * sCm; + static const Double_t sIBServicesZOut = 253.45 * sCm; + + static const Double_t sIBServicesR1max = 13.3 * sCm; + static const Double_t sIBServicesR2max = 43.53 * sCm; + + static const Double_t sIBServicesCarbonThick = 0.2 * sCm; + static const Double_t sIBServicesCopperThick = 0.018 * sCm; + static const Double_t sIBServicesPolymerThick = 0.42 * sCm; + + // Outer Barrel + static const Double_t sOBServicesZIn = 83.0 * sCm; + static const Double_t sOBServicesZOut = 248.00 * sCm; + + static const Double_t sOBServicesRmax = 47.2 * sCm; + + static const Double_t sOBServicesTotalThick = 4.42 * sCm; + static const Double_t sOBServicesCarbonThick = 0.25 * sCm; + static const Double_t sOBServicesCopperThick = 0.23 * sCm; + static const Double_t sOBServicesPolymerThick = 3.8 * sCm; + + static const Double_t sBeamPipeSupportRHole = 28.2 * sMm; + static const Double_t sBeamPipeSupportZPos = 182.1 * sCm; + static const Double_t sBeamPipeSupportXIB = 32.5 * sCm; + + // Local variables + Double_t rmin, rmax, zlen, xpos, zpos; + + // The hole where the Beam Pipe Support should pass + zlen = 0.9 * sOBServicesTotalThick; + TGeoTube* obBPSuppHole = new TGeoTube(0, sBeamPipeSupportRHole, zlen); + obBPSuppHole->SetName("bpSuppHole"); + + // The Outer Barrel services as CompositeShape's (to avoid fake overlaps + // with the Beam Pipe Support on Side A) with a Pcon as basic shape + // Carbon + TGeoPcon* ibCarbonCon = new TGeoPcon(0., 360., 3); + rmin = sIBServicesR1max - (sIBServicesCarbonThick + 1.8 * 0.462); + ibCarbonCon->DefineSection(0, sIBServicesZIn, rmin, sIBServicesR1max); + ibCarbonCon->DefineSection(1, sIBServicesZMid, rmin, sIBServicesR1max); + rmin = sIBServicesR2max - (sIBServicesCarbonThick + 0.462 / 1.8); + ibCarbonCon->DefineSection(2, sIBServicesZOut, rmin, sIBServicesR2max); + ibCarbonCon->SetName("ibCarbonCon"); + + xpos = sBeamPipeSupportXIB; + zpos = sBeamPipeSupportZPos; + TGeoCombiTrans* ibHoleMatL = new TGeoCombiTrans(xpos, 0, zpos, new TGeoRotation("", 90, 90, -90)); + ibHoleMatL->SetName("ibHoleMatL"); + ibHoleMatL->RegisterYourself(); + + TGeoCombiTrans* ibHoleMatR = new TGeoCombiTrans(-xpos, 0, zpos, new TGeoRotation("", 90, 90, -90)); + ibHoleMatR->SetName("ibHoleMatR"); + ibHoleMatR->RegisterYourself(); + + TGeoCompositeShape* ibCarbonSh = new TGeoCompositeShape("ibCarbonCon-bpSuppHole:ibHoleMatL-bpSuppHole:ibHoleMatR"); + + // Copper + TGeoPcon* ibCopperCon = new TGeoPcon(0., 360., 3); + rmin = ibCarbonCon->GetRmin(0); + rmax = ibCarbonCon->GetRmax(0) - sIBServicesCarbonThick; + ibCopperCon->DefineSection(0, sIBServicesZIn, rmin, rmax); + ibCopperCon->DefineSection(1, sIBServicesZMid, rmin, rmax); + rmin = ibCarbonCon->GetRmin(2); + rmax = ibCarbonCon->GetRmax(2) - sIBServicesCarbonThick; + ibCopperCon->DefineSection(2, sIBServicesZOut, rmin, rmax); + ibCopperCon->SetName("ibCopperCon"); + + TGeoCompositeShape* ibCopperSh = new TGeoCompositeShape("ibCopperCon-bpSuppHole:ibHoleMatL-bpSuppHole:ibHoleMatR"); + + // Polymer + TGeoPcon* ibPolyCon = new TGeoPcon(0., 360., 3); + rmin = ibCarbonCon->GetRmin(0); + rmax = ibCopperCon->GetRmax(0) - sIBServicesCopperThick * 1.8; + ibPolyCon->DefineSection(0, sIBServicesZIn, rmin, rmax); + ibPolyCon->DefineSection(1, sIBServicesZMid, rmin, rmax); + rmin = ibCopperCon->GetRmin(2); + rmax = ibCopperCon->GetRmax(2) - sIBServicesCopperThick / 1.8; + ibPolyCon->DefineSection(2, sIBServicesZOut, rmin, rmax); + ibPolyCon->SetName("ibPolyCon"); + + TGeoCompositeShape* ibPolySh = new TGeoCompositeShape("ibPolyCon-bpSuppHole:ibHoleMatL-bpSuppHole:ibHoleMatR"); + + // Water + TGeoPcon* ibWaterCon = new TGeoPcon(0., 360., 3); + rmin = ibCarbonCon->GetRmin(0); + rmax = ibPolyCon->GetRmax(0) - sIBServicesPolymerThick * 1.8; + ibWaterCon->DefineSection(0, sIBServicesZIn, rmin, rmax); + ibWaterCon->DefineSection(1, sIBServicesZMid, rmin, rmax); + rmin = ibPolyCon->GetRmin(2); + rmax = ibPolyCon->GetRmax(2) - sIBServicesPolymerThick / 1.8; + ibWaterCon->DefineSection(2, sIBServicesZOut, rmin, rmax); + ibWaterCon->SetName("ibWaterCon"); + + TGeoCompositeShape* ibWaterSh = new TGeoCompositeShape("ibWaterCon-bpSuppHole:ibHoleMatL-bpSuppHole:ibHoleMatR"); + + // The Outer Barrel services as CompositeShape's (to avoid fake overlaps + // with the Beam Pipe Support on Side A) with a Tube as basic shape + // Carbon + rmin = sOBServicesRmax - sOBServicesTotalThick; + zlen = sOBServicesZOut - sOBServicesZIn; + TGeoTube* obCarbonTub = new TGeoTube(rmin, sOBServicesRmax, zlen / 2); + obCarbonTub->SetName("obCarbonTub"); + + xpos = (rmin + sOBServicesRmax) / 2; + zpos = sBeamPipeSupportZPos - (sOBServicesZIn + zlen / 2); + TGeoCombiTrans* obHoleMatL = new TGeoCombiTrans(xpos, 0, zpos, new TGeoRotation("", 90, 90, -90)); + obHoleMatL->SetName("obHoleMatL"); + obHoleMatL->RegisterYourself(); + + TGeoCombiTrans* obHoleMatR = new TGeoCombiTrans(-xpos, 0, zpos, new TGeoRotation("", 90, 90, -90)); + obHoleMatR->SetName("obHoleMatR"); + obHoleMatR->RegisterYourself(); + + TGeoCompositeShape* obCarbonSh = new TGeoCompositeShape("obCarbonTub-bpSuppHole:obHoleMatL-bpSuppHole:obHoleMatR"); + + // Copper + rmax = sOBServicesRmax - sOBServicesCarbonThick; + TGeoTube* obCopperTub = new TGeoTube(rmin, rmax, zlen / 2); + obCopperTub->SetName("obCopperTub"); + + TGeoCompositeShape* obCopperSh = new TGeoCompositeShape("obCopperTub-bpSuppHole:obHoleMatL-bpSuppHole:obHoleMatR"); + + // Polymer + rmax -= sOBServicesCopperThick; + TGeoTube* obPolyTub = new TGeoTube(rmin, rmax, zlen / 2); + obPolyTub->SetName("obPolyTub"); + + TGeoCompositeShape* obPolySh = new TGeoCompositeShape("obPolyTub-bpSuppHole:obHoleMatL-bpSuppHole:obHoleMatR"); + + // Water + rmax -= sOBServicesPolymerThick; + TGeoTube* obWaterTub = new TGeoTube(rmin, rmax, zlen / 2); + obWaterTub->SetName("obWaterTub"); + + TGeoCompositeShape* obWaterSh = new TGeoCompositeShape("obWaterTub-bpSuppHole:obHoleMatL-bpSuppHole:obHoleMatR"); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium(Form("%s_C4SERVICES$", GetDetName())); + TGeoMedium* medCopper = mgr->GetMedium(Form("%s_COPPER$", GetDetName())); + TGeoMedium* medPolymer = mgr->GetMedium(Form("%s_POLY4SERVICES$", GetDetName())); + TGeoMedium* medWater = mgr->GetMedium(Form("%s_WATER$", GetDetName())); + + TGeoVolume* ibCarbonVol = new TGeoVolume("ITSServicesCarbonIB", ibCarbonSh, medCarbon); + ibCarbonVol->SetFillColor(kGray); + ibCarbonVol->SetLineColor(kGray); + + TGeoVolume* ibCopperVol = new TGeoVolume("ITSServicesCopperIB", ibCopperSh, medCopper); + ibCopperVol->SetFillColor(kRed); + ibCopperVol->SetLineColor(kRed); + + TGeoVolume* ibPolyVol = new TGeoVolume("ITSServicesPolymerIB", ibPolySh, medPolymer); + ibPolyVol->SetFillColor(kYellow); + ibPolyVol->SetLineColor(kYellow); + + TGeoVolume* ibWaterVol = new TGeoVolume("ITSServicesWaterIB", ibWaterSh, medWater); + ibWaterVol->SetFillColor(kBlue); + ibWaterVol->SetLineColor(kBlue); + + TGeoVolume* obCarbonVol = new TGeoVolume("ITSServicesCarbonOB", obCarbonSh, medCarbon); + obCarbonVol->SetFillColor(kGray); + obCarbonVol->SetLineColor(kGray); + + TGeoVolume* obCopperVol = new TGeoVolume("ITSServicesCopperOB", obCopperSh, medCopper); + obCopperVol->SetFillColor(kRed); + obCopperVol->SetLineColor(kRed); + + TGeoVolume* obPolyVol = new TGeoVolume("ITSServicesPolymerOB", obPolySh, medPolymer); + obPolyVol->SetFillColor(kYellow); + obPolyVol->SetLineColor(kYellow); + + TGeoVolume* obWaterVol = new TGeoVolume("ITSServicesWaterOB", obWaterSh, medWater); + obWaterVol->SetFillColor(kBlue); + obWaterVol->SetLineColor(kBlue); + + // Finally place all volumes + mother->AddNode(ibCarbonVol, 1, new TGeoTranslation(0., 30., 0.)); + ibCarbonVol->AddNode(ibCopperVol, 1, nullptr); + ibCopperVol->AddNode(ibPolyVol, 1, nullptr); + ibPolyVol->AddNode(ibWaterVol, 1, nullptr); + + zpos = sOBServicesZIn + zlen / 2; + mother->AddNode(obCarbonVol, 1, new TGeoTranslation(0., 30., zpos)); + obCarbonVol->AddNode(obCopperVol, 1, nullptr); + obCopperVol->AddNode(obPolyVol, 1, nullptr); + obPolyVol->AddNode(obWaterVol, 1, nullptr); +} + void V3Services::obConvWire(TGeoVolume* mother, const TGeoManager* mgr) { // diff --git a/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx b/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx index 0998272f745d0..a82ad4001aff3 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx @@ -42,7 +42,7 @@ constexpr int DefRDHVersion = o2::raw::RDHUtils::getVersion& m2r, std::string_view outDir, std::string_view outPrefix, std::string_view fileFor); void digi2raw(std::string_view inpName, std::string_view outDir, std::string_view fileFor, int verbosity, uint32_t rdhV = DefRDHVersion, bool enablePadding = false, - bool noEmptyHBF = false, int superPageSizeInB = 1024 * 1024); + bool noEmptyHBF = false, bool noEmptyROF = false, int superPageSizeInB = 1024 * 1024); int main(int argc, char** argv) { @@ -63,6 +63,7 @@ int main(int argc, char** argv) add_option("rdh-version,r", bpo::value()->default_value(DefRDHVersion), "RDH version to use"); add_option("enable-padding", bpo::value()->default_value(false)->implicit_value(true), "enable GBT word padding to 128 bits even for RDH V7"); add_option("no-empty-hbf,e", bpo::value()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); + add_option("no-empty-rof", bpo::value()->default_value(false)->implicit_value(true), "do not create empty ROF blocks"); add_option("hbfutils-config,u", bpo::value()->default_value(std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)), "config file for HBFUtils (or none)"); add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); @@ -96,7 +97,8 @@ int main(int argc, char** argv) vm["verbosity"].as(), vm["rdh-version"].as(), vm["enable-padding"].as(), - vm["no-empty-hbf"].as()); + vm["no-empty-hbf"].as(), + vm["no-empty-rof"].as()); LOG(info) << "HBFUtils settings used for conversion:"; o2::raw::HBFUtils::Instance().print(); @@ -104,7 +106,7 @@ int main(int argc, char** argv) return 0; } -void digi2raw(std::string_view inpName, std::string_view outDir, std::string_view fileFor, int verbosity, uint32_t rdhV, bool enablePadding, bool noEmptyHBF, int superPageSizeInB) +void digi2raw(std::string_view inpName, std::string_view outDir, std::string_view fileFor, int verbosity, uint32_t rdhV, bool enablePadding, bool noEmptyHBF, bool noEmptyROF, int superPageSizeInB) { TStopwatch swTot; swTot.Start(); @@ -178,9 +180,9 @@ void digi2raw(std::string_view inpName, std::string_view outDir, std::string_vie LOG(info) << "Processing ROF:" << rofRec.getROFrame() << " with " << nDigROF << " entries"; rofRec.print(); } - if (!nDigROF) { + if (!nDigROF && noEmptyROF) { if (verbosity) { - LOG(info) << "Frame is empty"; // ?? + LOG(info) << "Frame is empty"; } continue; } diff --git a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index 16a1595a2d6c5..1dd64b6f1874b 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -13,40 +13,52 @@ o2_add_library(ITStracking TARGETVARNAME targetName SOURCES src/ClusterLines.cxx src/Cluster.cxx - src/ROframe.cxx + src/Configuration.cxx + src/FastMultEstConfig.cxx + src/FastMultEst.cxx + src/LineVertexerHelpers.cxx src/TimeFrame.cxx src/IOUtils.cxx - src/Label.cxx - src/Road.cxx src/Tracker.cxx src/TrackerTraits.cxx src/TrackingConfigParam.cxx - src/ClusterLines.cxx src/Vertexer.cxx src/VertexerTraits.cxx - src/Smoother.cxx - PUBLIC_LINK_LIBRARIES O2::GPUCommon - Microsoft.GSL::GSL - O2::CommonConstants - O2::DataFormatsITSMFT - O2::SimulationDataFormat - O2::ITSBase - O2::ITSReconstruction - O2::ITSMFTReconstruction - O2::DataFormatsITS) + PUBLIC_LINK_LIBRARIES + O2::GPUCommon + Microsoft.GSL::GSL + O2::CommonConstants + O2::DataFormatsITSMFT + O2::SimulationDataFormat + O2::ITSBase + O2::CommonUtils + O2::ITSReconstruction + O2::ITSMFTReconstruction + O2::DataFormatsITS + PRIVATE_LINK_LIBRARIES + O2::Steer + TBB::tbb) +# target_compile_options(${targetName} PRIVATE -O0 -g -fPIC -fno-omit-frame-pointer) -if (OpenMP_CXX_FOUND) - target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) - target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) -endif() +o2_add_library(ITSTrackingInterface + TARGETVARNAME targetName + SOURCES src/TrackingInterface.cxx + PRIVATE_LINK_LIBRARIES + O2::ITStracking + O2::Framework + O2::GPUTracking) o2_target_root_dictionary(ITStracking HEADERS include/ITStracking/ClusterLines.h include/ITStracking/Tracklet.h include/ITStracking/Cluster.h + include/ITStracking/Definitions.h + include/ITStracking/FastMultEstConfig.h include/ITStracking/TrackingConfigParam.h LINKDEF src/TrackingLinkDef.h) if(CUDA_ENABLED OR HIP_ENABLED) add_subdirectory(GPU) endif() + +add_subdirectory(test) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Array.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Array.h deleted file mode 100644 index 7bbc7bb098091..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Array.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Array.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_ARRAY_H_ -#define ITSTRACKINGGPU_ARRAY_H_ - -#include "GPUCommonDef.h" -#ifdef __HIPCC__ -#include -#endif - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -namespace -{ -template -struct ArrayTraits final { - typedef T InternalArray[Size]; - - GPUhd() static constexpr T& getReference(const InternalArray& internalArray, size_t index) noexcept - { - return const_cast(internalArray[index]); - } - - GPUhd() static constexpr T* getPointer(const InternalArray& internalArray) noexcept - { - return const_cast(internalArray); - } -}; -} // namespace - -template -struct Array final { - - void copy(const Array& t) - { - memcpy(InternalArray, t.data(), Size * sizeof(T)); - } - - GPUhd() T* data() noexcept { return const_cast(InternalArray); } - GPUhd() const T* data() const noexcept { return const_cast(InternalArray); } - GPUhd() T& operator[](const int index) noexcept { return const_cast(InternalArray[index]); } - GPUhd() constexpr T& operator[](const int index) const noexcept { return const_cast(InternalArray[index]); } - GPUhd() size_t size() const noexcept { return Size; } - - T InternalArray[Size]; -}; -} // namespace gpu -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h deleted file mode 100644 index 3a8af39d1ee63..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file ClusterLinesGPU.h -/// \brief GPU-compliant version of ClusterLines, for the moment separated, might create a common traits for ClusterLines + later specifications for each arch, later. - -#ifndef ITSTRACKINGGPU_CLUSTERLINESGPU_H_ -#define ITSTRACKINGGPU_CLUSTERLINESGPU_H_ - -#include "GPUCommonDef.h" -#include /// Required to properly compile MathUtils -#include "ITStracking/ClusterLines.h" -#ifdef __HIPCC__ -#include -#endif - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -struct GPUVertex final { - GPUhd() GPUVertex() : realVertex{false} - { - } - - GPUhd() GPUVertex(float x, float y, float z, float eX, float eY, float eZ, int contrib) : xCoord{x}, - yCoord{y}, - zCoord{z}, - errorX{eZ}, - errorY{eY}, - errorZ{eZ}, - contributors{contrib}, - realVertex{true} - { - } - float xCoord; - float yCoord; - float zCoord; - float errorX; - float errorY; - float errorZ; - int contributors; - int timeStamp; - unsigned char realVertex; -}; - -class ClusterLinesGPU final -{ - public: - GPUd() ClusterLinesGPU(const Line& firstLine, const Line& secondLine); // poor man solution to calculate duplets' centroid - GPUd() void computeClusterCentroid(); - GPUd() inline float* getVertex() { return mVertex; } - - private: - float mAMatrix[6]; // AX=B - float mBMatrix[3]; // AX=B - float mVertexCandidate[3]; // vertex candidate - float mWeightMatrix[9]; // weight matrix - float mVertex[3]; // cluster centroid position -}; - -} // namespace gpu -} // namespace its -} // namespace o2 -#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Context.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Context.h deleted file mode 100644 index 294016ffdcaee..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Context.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Context.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_CONTEXT_H_ -#define ITSTRACKINGGPU_CONTEXT_H_ - -#include -#include -#include "ITStracking/Definitions.h" -#ifdef __HIPCC__ -#include -#endif - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -struct DeviceProperties final { - std::string name; - int gpuProcessors; - int gpuCores; - long globalMemorySize; - long constantMemorySize; - long sharedMemorySize; - long maxClockRate; - int busWidth; - long l2CacheSize; - long registersPerBlock; - int warpSize; - int maxThreadsPerBlock; - int maxBlocksPerSM; - dim3 maxThreadsDim; - dim3 maxGridDim; -}; - -class Context final -{ - public: - static Context& getInstance(); - - Context(const Context&); - Context& operator=(const Context&); - - const DeviceProperties& getDeviceProperties(); - const DeviceProperties& getDeviceProperties(const int); - - private: - Context(bool dumpDevices = false); - ~Context() = default; - - int mDevicesNum; - std::vector mDeviceProperties; -}; -} // namespace gpu -} // namespace its -} // namespace o2 - -#endif /* TRAKINGITSU_INCLUDE_GPU_CONTEXT_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/PrimaryVertexContextGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/PrimaryVertexContextGPU.h deleted file mode 100644 index 67e5109b04623..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/PrimaryVertexContextGPU.h +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file PrimaryVertexContextNVNV.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_PRIMARYVERTEXCONTEXTGPU_H_ -#define ITSTRACKINGGPU_PRIMARYVERTEXCONTEXTGPU_H_ - -#include - -#ifndef GPUCA_GPUCODE_GENRTC -#include -#include -#endif - -#include "ITStracking/Configuration.h" -#include "ITStracking/Constants.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/PrimaryVertexContext.h" -#include "ITStracking/Road.h" -#include "ITStracking/Tracklet.h" - -#include "DeviceStoreGPU.h" -#include "UniquePointer.h" - -namespace o2 -{ -namespace its -{ - -class PrimaryVertexContextNV final : public PrimaryVertexContext -{ - public: - PrimaryVertexContextNV() = default; - ~PrimaryVertexContextNV() override; - - void initialise(const MemoryParameters& memParam, const TrackingParameters& trkParam, - const std::vector>& cl, const std::array& pv, const int iteration) override; - - gpu::DeviceStoreNV& getDeviceContext(); - gpu::Array, constants::its2::LayersNumber>& getDeviceClusters(); - gpu::Array, constants::its2::TrackletsPerRoad>& getDeviceTracklets(); - gpu::Array, constants::its2::CellsPerRoad>& getDeviceTrackletsLookupTable(); - gpu::Array, constants::its2::CellsPerRoad>& getDeviceTrackletsPerClustersTable(); - gpu::Array, constants::its2::CellsPerRoad>& getDeviceCells(); - gpu::Array, constants::its2::CellsPerRoad - 1>& getDeviceCellsLookupTable(); - gpu::Array, constants::its2::CellsPerRoad - 1>& getDeviceCellsPerTrackletTable(); - std::array, constants::its2::CellsPerRoad>& getTempTableArray(); - std::array, constants::its2::CellsPerRoad>& getTempTrackletArray(); - std::array, constants::its2::CellsPerRoad - 1>& getTempCellArray(); - void updateDeviceContext(); - - private: - gpu::DeviceStoreNV mGPUContext; - gpu::UniquePointer mGPUContextDevicePointer; - std::array, constants::its2::CellsPerRoad> mTempTableArray; - std::array, constants::its2::CellsPerRoad> mTempTrackletArray; - std::array, constants::its2::CellsPerRoad - 1> mTempCellArray; -}; - -inline PrimaryVertexContextNV::~PrimaryVertexContextNV() = default; - -inline gpu::DeviceStoreNV& PrimaryVertexContextNV::getDeviceContext() -{ - return *mGPUContextDevicePointer; -} - -inline gpu::Array, constants::its2::LayersNumber>& PrimaryVertexContextNV::getDeviceClusters() -{ - return mGPUContext.getClusters(); -} - -inline gpu::Array, constants::its2::TrackletsPerRoad>& PrimaryVertexContextNV::getDeviceTracklets() -{ - return mGPUContext.getTracklets(); -} - -inline gpu::Array, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getDeviceTrackletsLookupTable() -{ - return mGPUContext.getTrackletsLookupTable(); -} - -inline gpu::Array, constants::its2::CellsPerRoad>& - PrimaryVertexContextNV::getDeviceTrackletsPerClustersTable() -{ - return mGPUContext.getTrackletsPerClusterTable(); -} - -inline gpu::Array, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getDeviceCells() -{ - return mGPUContext.getCells(); -} - -inline gpu::Array, constants::its2::CellsPerRoad - 1>& PrimaryVertexContextNV::getDeviceCellsLookupTable() -{ - return mGPUContext.getCellsLookupTable(); -} - -inline gpu::Array, constants::its2::CellsPerRoad - 1>& - PrimaryVertexContextNV::getDeviceCellsPerTrackletTable() -{ - return mGPUContext.getCellsPerTrackletTable(); -} - -inline std::array, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getTempTableArray() -{ - return mTempTableArray; -} - -inline std::array, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getTempTrackletArray() -{ - return mTempTrackletArray; -} - -inline std::array, constants::its2::CellsPerRoad - 1>& PrimaryVertexContextNV::getTempCellArray() -{ - return mTempCellArray; -} - -inline void PrimaryVertexContextNV::updateDeviceContext() -{ - mGPUContextDevicePointer = gpu::UniquePointer{mGPUContext}; -} - -inline void PrimaryVertexContextNV::initialise(const MemoryParameters& memParam, const TrackingParameters& trkParam, - const std::vector>& cl, const std::array& pv, const int iteration) -{ - ///TODO: to be re-enabled in the future - // this->PrimaryVertexContext::initialise(memParam, cl, pv, iteration); - // mGPUContextDevicePointer = mGPUContext.initialise(mPrimaryVertex, mClusters, mTracklets, mCells, mCellsLookupTable, mMinR, mMaxR); -} - -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Stream.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Stream.h deleted file mode 100644 index 0ac38e7a8ac4c..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Stream.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Stream.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_STREAM_H_ -#define ITSTRACKINGGPU_STREAM_H_ - -#include "ITStracking/Definitions.h" - -#ifdef __HIPCC__ -#include -#endif - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -class Stream final -{ - - public: - Stream(); - ~Stream(); - - const GPUStream& get() const; - - private: - GPUStream mStream; -}; -} // namespace gpu -} // namespace its -} // namespace o2 - -#endif /* TRAKINGITSU_INCLUDE_GPU_STREAM_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index 6c865550423c6..38de1e712108d 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -13,238 +13,271 @@ #ifndef TRACKINGITSGPU_INCLUDE_TIMEFRAMEGPU_H #define TRACKINGITSGPU_INCLUDE_TIMEFRAMEGPU_H -#ifdef __HIPCC__ -#include -#endif +#include +#include +#include "ITStracking/BoundedAllocator.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/Configuration.h" +#include "ITStracking/TrackExtensionHypothesis.h" +#include "ITStrackingGPU/Utils.h" -#include "ITStrackingGPU/ClusterLinesGPU.h" -#include "ITStrackingGPU/Array.h" -#include "ITStrackingGPU/Vector.h" -#include "ITStrackingGPU/Stream.h" - -#include - -#include "GPUCommonDef.h" -#include "GPUCommonMath.h" -#include "GPUCommonLogger.h" - -namespace o2 -{ - -namespace its +namespace o2::its::gpu { -namespace gpu +template +class TimeFrameGPU : public TimeFrame { + using typename TimeFrame::IndexTableUtilsN; + using typename TimeFrame::ROFOverlapTableN; + using typename TimeFrame::ROFVertexLookupTableN; + using typename TimeFrame::ROFMaskTableN; + using typename TimeFrame::TrackingTopologyN; + using typename TimeFrame::TrackSeedN; + static constexpr int MaxLinks = TrackingTopologyN::MaxLinks; + static constexpr int MaxCells = TrackingTopologyN::MaxCells; + static constexpr int MaxStreams = MaxCells > NLayers ? MaxCells : NLayers; -template -struct StaticTrackingParameters { - StaticTrackingParameters& operator=(const StaticTrackingParameters& t) = default; - void set(const TrackingParameters& pars) - { - ClusterSharing = pars.ClusterSharing; - MinTrackLength = pars.MinTrackLength; - NSigmaCut = pars.NSigmaCut; - PVres = pars.PVres; - DeltaROF = pars.DeltaROF; - ZBins = pars.ZBins; - PhiBins = pars.PhiBins; - CellDeltaTanLambdaSigma = pars.CellDeltaTanLambdaSigma; - } - - /// General parameters - int ClusterSharing = 0; - int MinTrackLength = nLayers; - float NSigmaCut = 5; - float PVres = 1.e-2f; - int DeltaROF = 0; - int ZBins{256}; - int PhiBins{128}; - - /// Cell finding cuts - float CellDeltaTanLambdaSigma = 0.007f; -}; - -enum class Task { - Tracker = 0, - Vertexer = 1 -}; - -template -class GpuTimeFrameChunk -{ - public: - static size_t computeScalingSizeBytes(const int, const TimeFrameGPUParameters&); - static size_t computeFixedSizeBytes(const TimeFrameGPUParameters&); - static size_t computeRofPerChunk(const TimeFrameGPUParameters&, const size_t); - - GpuTimeFrameChunk() = delete; - GpuTimeFrameChunk(o2::its::TimeFrame* tf, TimeFrameGPUParameters& conf) - { - mTimeFramePtr = tf; - mTFGPUParams = &conf; - } - ~GpuTimeFrameChunk(); - - /// Most relevant operations - void allocate(const size_t, Stream&); - void reset(const Task, Stream&); - size_t loadDataOnDevice(const size_t, const size_t, const int, Stream&); - - /// Interface - Cluster* getDeviceClusters(const int); - TrackingFrameInfo* getDeviceTrackingFrameInfo(const int); - int* getDeviceClusterExternalIndices(const int); - int* getDeviceIndexTables(const int); - Tracklet* getDeviceTracklets(const int); - int* getDeviceTrackletsLookupTables(const int); - Cell* getDeviceCells(const int); - int* getDeviceCellsLookupTables(const int); - TimeFrameGPUParameters* getTimeFrameGPUParameters() const { return mTFGPUParams; } - - int* getDeviceCUBTmpBuffer() { return mCUBTmpBufferDevice; } - int* getDeviceFoundTracklets() { return mFoundTrackletsDevice; } - int* getDeviceNFoundCells() { return mNFoundCellsDevice; } - int* getDeviceCellNeigboursLookupTables(const int); - int* getDeviceCellNeighbours(const int); - - /// Vertexer only - int* getDeviceNTrackletCluster(const int combid) { return mNTrackletsPerClusterDevice[combid]; } - Line* getDeviceLines() { return mLinesDevice; }; - int* getDeviceNFoundLines() { return mNFoundLinesDevice; } - int* getDeviceNExclusiveFoundLines() { return mNExclusiveFoundLinesDevice; } - unsigned char* getDeviceUsedTracklets() { return mUsedTrackletsDevice; } - int* getDeviceClusteredLines() { return mClusteredLinesDevice; } - size_t getNPopulatedRof() const { return mNPopulatedRof; } - - private: - /// Host - std::array, nLayers> mHostClusters; - std::array, nLayers> mHostIndexTables; - - /// Device - std::array mClustersDevice; - std::array mTrackingFrameInfoDevice; - std::array mClusterExternalIndicesDevice; - std::array mIndexTablesDevice; - std::array mTrackletsDevice; - std::array mTrackletsLookupTablesDevice; - std::array mCellsDevice; - std::array mCellsLookupTablesDevice; - std::array mNeighboursCellDevice; - std::array mNeighboursCellLookupTablesDevice; - - int* mCUBTmpBufferDevice; - int* mFoundTrackletsDevice; - int* mNFoundCellsDevice; - - /// Vertexer only - Line* mLinesDevice; - int* mNFoundLinesDevice; - int* mNExclusiveFoundLinesDevice; - unsigned char* mUsedTrackletsDevice; - std::array mNTrackletsPerClusterDevice; - int* mClusteredLinesDevice; - - /// State and configuration - bool mAllocated = false; - size_t mNRof = 0; - size_t mNPopulatedRof = 0; - o2::its::TimeFrame* mTimeFramePtr = nullptr; - TimeFrameGPUParameters* mTFGPUParams = nullptr; -}; - -template -class TimeFrameGPU : public TimeFrame -{ public: - TimeFrameGPU(); - ~TimeFrameGPU(); + TimeFrameGPU() = default; + ~TimeFrameGPU() override = default; /// Most relevant operations + void pushMemoryStack(const int); + void popMemoryStack(const int); void registerHostMemory(const int); void unregisterHostMemory(const int); - void initialise(const int, const TrackingParameters&, const int, IndexTableUtils* utils = nullptr, const TimeFrameGPUParameters* pars = nullptr); - void initDevice(const int, IndexTableUtils*, const TrackingParameters& trkParam, const TimeFrameGPUParameters&, const int, const int); - void initDeviceChunks(const int, const int); - template - size_t loadChunkData(const size_t, const size_t, const size_t); - size_t getNChunks() const { return mMemChunks.size(); } - GpuTimeFrameChunk& getChunk(const int chunk) { return mMemChunks[chunk]; } - Stream& getStream(const size_t stream) { return mGpuStreams[stream]; } - void wipe(const int); + void initialise(const TrackingParameters&, int maxLayers); + void initialise(const TrackingParameters&, int maxLayers, int iteration); + void loadIndexTableUtils(); + void loadTrackingTopologies(); + void loadTrackingFrameInfoDevice(const int); + void createTrackingFrameInfoDeviceArray(); + void loadUnsortedClustersDevice(const int); + void createUnsortedClustersDeviceArray(const int = NLayers); + void loadClustersDevice(const int); + void createClustersDeviceArray(const int = NLayers); + void loadClustersIndexTables(const int); + void createClustersIndexTablesArray(); + void createUsedClustersDevice(const int); + void createUsedClustersDeviceArray(const int = NLayers); + void loadUsedClustersDevice(); + void loadROFrameClustersDevice(const int); + void createROFrameClustersDeviceArray(); + void loadROFCutMask(const int); + void loadVertices(); + void loadROFOverlapTable(); + void loadROFVertexLookupTable(); + void updateROFVertexLookupTable(); + + /// + void createTrackletsLUTDevice(bool, const int); + void createTrackletsLUTDeviceArray(); + void loadTrackletsDevice(); + void loadTrackletsLUTDevice(); + void loadCellsDevice(); + void loadCellsLUTDevice(); + void loadTrackSeedsDevice(); + void loadTrackSeedsChi2Device(); + void loadTrackSeedsDevice(bounded_vector&); + void createTrackletsBuffers(const int); + void createTrackletsBuffersArray(); + void createCellsBuffers(const int); + void createCellsBuffersArray(); + void createCellsDevice(); + void createCellsLUTDevice(const int); + void createCellsLUTDeviceArray(); + void createNeighboursIndexTablesDevice(const int); + void createNeighboursDevice(const unsigned int layer); + void createNeighboursLUTDevice(const int, const unsigned int); + void createTrackITSExtDevice(const size_t); + void createTrackExtensionScratchDevice(const int nThreads, const int maxHypotheses); + void downloadTrackITSExtDevice(); + void downloadTrackIndicesDevice(); + void downloadCellsNeighboursDevice(std::vector>&, const int); + void downloadNeighboursLUTDevice(bounded_vector&, const int); + void downloadCellsDevice(); + void downloadCellsLUTDevice(); + + /// synchronization + auto& getStream(const size_t stream) { return mGpuStreams[stream]; } + auto& getStreams() { return mGpuStreams; } + void syncStream(const size_t stream); + void syncStreams(const bool = true); + void waitEvent(const int, const int); + void recordEvent(const int); + void recordEvents(const int = 0, const int = NLayers); + + /// cleanup + virtual void wipe() final; /// interface - int getNClustersInRofSpan(const int, const int, const int) const; - IndexTableUtils* getDeviceIndexTableUtils() { return mIndexTableUtilsDevice; } - int* getDeviceROframesClusters(const int layer) { return mROframesClustersDevice[layer]; } - std::vector>& getVerticesInChunks() { return mVerticesInChunks; } - std::vector>& getNVerticesInChunks() { return mNVerticesInChunks; } - std::vector>& getLabelsInChunks() { return mLabelsInChunks; } - int getNAllocatedROFs() const { return mNrof; } // Allocated means maximum nROF for each chunk while populated is the number of loaded ones. - StaticTrackingParameters* getDeviceTrackingParameters() { return mTrackingParamsDevice; } - Vertex* getDeviceVertices() { return mVerticesDevice; } - int* getDeviceROframesPV() { return mROframesPVDevice; } + virtual bool isGPU() const noexcept final { return true; } + virtual const char* getName() const noexcept override final { return "GPU"; } + IndexTableUtilsN* getDeviceIndexTableUtils() { return mIndexTableUtilsDevice; } + const auto getDeviceROFOverlapTableView() { return mDeviceROFOverlapTableView; } + const auto getDeviceROFVertexLookupTableView() { return mDeviceROFVertexLookupTableView; } + const auto getDeviceROFMaskTableView() { return mDeviceROFMaskTableView; } + const auto getDeviceTrackingTopologyView() const { return mDeviceTrackingTopologyView; } + int* getDeviceROFramesClusters(const int layer) { return mROFramesClustersDevice[layer]; } + auto& getTrackITSExt() { return mTrackITSExt; } + auto& getTrackIndices() { return mTrackIndices; } + Vertex* getDeviceVertices() { return mPrimaryVerticesDevice; } + int* getDeviceROFramesPV() { return mROFramesPVDevice; } unsigned char* getDeviceUsedClusters(const int); + const o2::base::Propagator* getChainPropagator(); + + // Hybrid + TrackITSExt* getDeviceTrackITSExt() { return mTrackITSExtDevice; } + int* getDeviceTrackIndices() { return mTrackIndicesDevice; } + TrackExtensionHypothesis* getDeviceActiveTrackExtensionHypotheses() { return mActiveTrackExtensionHypothesesDevice; } + TrackExtensionHypothesis* getDeviceNextTrackExtensionHypotheses() { return mNextTrackExtensionHypothesesDevice; } + int* getDeviceNeighboursLUT(const int layer) { return mNeighboursLUTDevice[layer]; } + gsl::span getDeviceNeighboursLUTs() { return mNeighboursLUTDevice; } + CellNeighbour** getDeviceArrayNeighbours() { return mNeighboursDeviceArray; } + std::array& getDeviceNeighboursAll() { return mNeighboursDevice; } + CellNeighbour* getDeviceNeighbours(const int layer) { return mNeighboursDevice[layer]; } + TrackingFrameInfo* getDeviceTrackingFrameInfo(const int); + const TrackingFrameInfo** getDeviceArrayTrackingFrameInfo() const { return mTrackingFrameInfoDeviceArray; } + const Cluster** getDeviceArrayClusters() const { return mClustersDeviceArray; } + const Cluster** getDeviceArrayUnsortedClusters() const { return mUnsortedClustersDeviceArray; } + const int** getDeviceArrayClustersIndexTables() const { return mClustersIndexTablesDeviceArray; } + std::vector getClusterSizes(); + uint8_t** getDeviceArrayUsedClusters() const { return mUsedClustersDeviceArray; } + const int** getDeviceROFrameClusters() const { return mROFramesClustersDeviceArray; } + Tracklet** getDeviceArrayTracklets() { return mTrackletsDeviceArray; } + int** getDeviceArrayTrackletsLUT() const { return mTrackletsLUTDeviceArray; } + int** getDeviceArrayCellsLUT() const { return mCellsLUTDeviceArray; } + int** getDeviceArrayNeighboursCellLUT() const { return mNeighboursCellLUTDeviceArray; } + CellSeed** getDeviceArrayCells() { return mCellsDeviceArray; } + TrackSeedN* getDeviceTrackSeeds() { return mTrackSeedsDevice; } + int* getDeviceTrackSeedsLUT() { return mTrackSeedsLUTDevice; } + auto getNTrackSeeds() const { return mNTracks; } + o2::track::TrackParCovF** getDeviceArrayTrackSeeds() { return mCellSeedsDeviceArray; } + float** getDeviceArrayTrackSeedsChi2() { return mCellSeedsChi2DeviceArray; } + int* getDeviceNeighboursIndexTables(const int layer) { return mNeighboursIndexTablesDevice[layer]; } + + void setDevicePropagator(const o2::base::PropagatorImpl* p) final { this->mPropagatorDevice = p; } // Host-specific getters - gsl::span getHostNTracklets(const int chunkId); - gsl::span getHostNCells(const int chunkId); + gsl::span getNTracklets() { return {mNTracklets.data(), static_cast::size_type>(this->mTrackingTopologyView.nLinks)}; } + gsl::span getNCells() { return {mNCells.data(), static_cast::size_type>(this->mTrackingTopologyView.nCells)}; } + auto& getArrayNCells() { return mNCells; } + gsl::span getNNeighbours() { return {mNNeighbours.data(), static_cast::size_type>(this->mTrackingTopologyView.nCells)}; } + auto& getArrayNNeighbours() { return mNNeighbours; } + + // Host-available device getters + gsl::span getDeviceTrackletsLUTs() { return mTrackletsLUTDevice; } + gsl::span getDeviceCellLUTs() { return mCellsLUTDevice; } + gsl::span getDeviceTracklets() { return mTrackletsDevice; } + gsl::span getDeviceCells() { return mCellsDevice; } + + // Overridden getters + size_t getNumberOfTracklets() const final; + size_t getNumberOfCells() const final; + size_t getNumberOfNeighbours() const final; private: - bool mHostRegistered = false; - std::vector> mMemChunks; - TimeFrameGPUParameters mGpuParams; - StaticTrackingParameters mStaticTrackingParams; + void allocMemAsync(void**, size_t, Stream&, bool, int32_t = o2::gpu::GPUMemoryResource::MEMORY_GPU); // Abstract owned and unowned memory allocations on specific stream + void allocMem(void**, size_t, bool, int32_t = o2::gpu::GPUMemoryResource::MEMORY_GPU); // Abstract owned and unowned memory allocations on default stream + + // Host-available device buffer sizes + std::array mNTracklets{}; + std::array mNCells{}; + std::array mNNeighbours{}; // Device pointers - StaticTrackingParameters* mTrackingParamsDevice; - IndexTableUtils* mIndexTableUtilsDevice; - std::array mROframesClustersDevice; - std::array mUsedClustersDevice; - Vertex* mVerticesDevice; - int* mROframesPVDevice; + IndexTableUtilsN* mIndexTableUtilsDevice; + // device navigation views + ROFOverlapTableN::View mDeviceROFOverlapTableView; + ROFVertexLookupTableN::View mDeviceROFVertexLookupTableView; + ROFMaskTableN::View mDeviceROFMaskTableView; + std::vector mDeviceTrackerTopologyViews; + typename TrackingTopologyN::View mDeviceTrackingTopologyView; + + // Hybrid pref + Vertex* mPrimaryVerticesDevice; + int* mROFramesPVDevice; + std::array mClustersDevice; + std::array mUnsortedClustersDevice; + std::array mClustersIndexTablesDevice; + std::array mUsedClustersDevice; + std::array mROFramesClustersDevice; + const Cluster** mClustersDeviceArray; + const Cluster** mUnsortedClustersDeviceArray; + const int** mClustersIndexTablesDeviceArray; + uint8_t** mUsedClustersDeviceArray; + const int** mROFramesClustersDeviceArray; + std::array mTrackletsDevice{}; + std::array mTrackletsLUTDevice{}; + std::array mCellsLUTDevice{}; + std::array mNeighboursLUTDevice{}; + + Tracklet** mTrackletsDeviceArray{nullptr}; + int** mCellsLUTDeviceArray{nullptr}; + int** mNeighboursCellLUTDeviceArray{nullptr}; + int** mTrackletsLUTDeviceArray{nullptr}; + std::array mCellsDevice{}; + CellSeed** mCellsDeviceArray; + std::array mNeighboursIndexTablesDevice{}; + TrackSeedN* mTrackSeedsDevice{nullptr}; + int* mTrackSeedsLUTDevice{nullptr}; + unsigned int mNTracks{0}; + std::array mCellSeedsDevice{}; + o2::track::TrackParCovF** mCellSeedsDeviceArray; + std::array mCellSeedsChi2Device{}; + float** mCellSeedsChi2DeviceArray; + + TrackITSExt* mTrackITSExtDevice; + int* mTrackIndicesDevice{nullptr}; + TrackExtensionHypothesis* mActiveTrackExtensionHypothesesDevice{nullptr}; + TrackExtensionHypothesis* mNextTrackExtensionHypothesesDevice{nullptr}; + std::array mNeighboursDevice{}; + CellNeighbour** mNeighboursDeviceArray{nullptr}; + std::array mTrackingFrameInfoDevice; + const TrackingFrameInfo** mTrackingFrameInfoDeviceArray; // State - std::vector mGpuStreams; - size_t mAvailMemGB; - bool mFirstInit = true; - - // Output - std::vector> mVerticesInChunks; - std::vector> mNVerticesInChunks; - std::vector> mLabelsInChunks; - - // Host memeory used only in GPU tracking - std::vector mHostNTracklets; - std::vector mHostNCells; + Streams mGpuStreams; + std::bitset mPinnedUnsortedClusters{0}; + std::bitset mPinnedClusters{0}; + std::bitset mPinnedClustersIndexTables{0}; + std::bitset mPinnedUsedClusters{0}; + std::bitset mPinnedROFramesClusters{0}; + std::bitset mPinnedTrackingFrameInfo{0}; + + // Temporary buffer for storing output tracks from GPU tracking + bounded_vector mTrackITSExt; + bounded_vector mTrackIndices; }; -template -template -size_t TimeFrameGPU::loadChunkData(const size_t chunk, const size_t offset, const size_t maxRofs) // offset: readout frame to start from, maxRofs: to manage boundaries +template +inline std::vector TimeFrameGPU::getClusterSizes() { - size_t nRof{0}; - - mMemChunks[chunk].reset(task, mGpuStreams[chunk]); // Reset chunks memory - if constexpr ((bool)task) { - nRof = mMemChunks[chunk].loadDataOnDevice(offset, maxRofs, 3, mGpuStreams[chunk]); - } else { - nRof = mMemChunks[chunk].loadDataOnDevice(offset, maxRofs, nLayers, mGpuStreams[chunk]); - } - LOGP(debug, "In chunk {}: loaded {} readout frames starting from {}", chunk, nRof, offset); - return nRof; + std::vector sizes(this->mUnsortedClusters.size()); + std::transform(this->mUnsortedClusters.begin(), this->mUnsortedClusters.end(), sizes.begin(), + [](const auto& v) { return static_cast(v.size()); }); + return sizes; } -template -inline int TimeFrameGPU::getNClustersInRofSpan(const int rofIdstart, const int rofSpanSize, const int layerId) const +template +inline size_t TimeFrameGPU::getNumberOfTracklets() const { - return static_cast(mROframesClusters[layerId][(rofIdstart + rofSpanSize) < mROframesClusters.size() ? rofIdstart + rofSpanSize : mROframesClusters.size() - 1] - mROframesClusters[layerId][rofIdstart]); + return std::accumulate(mNTracklets.begin(), mNTracklets.begin() + this->mTrackingTopologyView.nLinks, 0); } -} // namespace gpu -} // namespace its -} // namespace o2 -#endif \ No newline at end of file + +template +inline size_t TimeFrameGPU::getNumberOfCells() const +{ + return std::accumulate(mNCells.begin(), mNCells.begin() + this->mTrackingTopologyView.nCells, 0); +} + +template +inline size_t TimeFrameGPU::getNumberOfNeighbours() const +{ + return std::accumulate(mNNeighbours.begin(), mNNeighbours.begin() + this->mTrackingTopologyView.nCells, 0); +} + +} // namespace o2::its::gpu + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h deleted file mode 100644 index b8f9c73d8fc29..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -#include "ITStracking/Definitions.h" - -#if defined(__CUDACC__) && defined(__USE_GPU_TRACER__) -namespace o2 -{ -namespace its -{ -namespace gpu -{ -class Tracer -{ - public: - Tracer(const char* name, int color_id = 0); - ~Tracer(); -}; -} // namespace gpu -} // namespace its -} // namespace o2 -#define RANGE(name, cid) o2::its::gpu::Tracer tracer(name, cid); -#else -#define RANGE(name, cid) -#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h index 314182fdd48c6..81d870c5b46c2 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h @@ -13,34 +13,33 @@ #ifndef ITSTRACKINGGPU_TRACKERTRAITSGPU_H_ #define ITSTRACKINGGPU_TRACKERTRAITSGPU_H_ -#include "ITStracking/Configuration.h" -#include "ITStracking/Definitions.h" #include "ITStracking/TrackerTraits.h" #include "ITStrackingGPU/TimeFrameGPU.h" -namespace o2 -{ -namespace its +namespace o2::its { -template -class TrackerTraitsGPU : public TrackerTraits +template +class TrackerTraitsGPU final : public TrackerTraits { + using typename TrackerTraits::IndexTableUtilsN; + public: TrackerTraitsGPU() = default; - ~TrackerTraitsGPU() override = default; + ~TrackerTraitsGPU() final = default; + + void adoptTimeFrame(TimeFrame* tf) final; + void initialiseTimeFrame(const int iteration) final; - // void computeLayerCells() final; - void adoptTimeFrame(TimeFrame* tf) override; - void initialiseTimeFrame(const int iteration) override; - void computeLayerTracklets(const int iteration) final; - void computeLayerCells(const int iteration) override; - void setBz(float) override; - void findCellsNeighbours(const int iteration) override; - void findRoads(const int iteration) override; - void findTracks() override; - void extendTracks(const int iteration) override; - // void refitTracks(const std::vector>& tf, std::vector& tracks) override; + void computeLayerTracklets(const int iteration, int) final; + void computeLayerCells(const int iteration) final; + void findCellsNeighbours(const int iteration) final; + void findRoads(const int iteration) final; + + void setBz(float) final; + + const char* getName() const noexcept final { return "GPU"; } + bool isGPU() const noexcept final { return true; } // TimeFrameGPU information forwarding int getTFNumberOfClusters() const override; @@ -48,17 +47,10 @@ class TrackerTraitsGPU : public TrackerTraits int getTFNumberOfCells() const override; private: - IndexTableUtils* mDeviceIndexTableUtils; - gpu::TimeFrameGPU<7>* mTimeFrameGPU; - gpu::StaticTrackingParameters* mStaticTrkPars; + IndexTableUtilsN* mDeviceIndexTableUtils; + gpu::TimeFrameGPU* mTimeFrameGPU; }; -template -inline void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) -{ - mTimeFrameGPU = static_cast*>(tf); -} -} // namespace its -} // namespace o2 +} // namespace o2::its -#endif \ No newline at end of file +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h new file mode 100644 index 0000000000000..34ac3e564e26d --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -0,0 +1,262 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// + +#ifndef ITSTRACKINGGPU_TRACKINGKERNELS_H_ +#define ITSTRACKINGGPU_TRACKINGKERNELS_H_ + +#include +#include + +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ROFLookupTables.h" +#include "ITStracking/TrackingTopology.h" +#include "ITStracking/TrackExtensionHypothesis.h" +#include "ITStrackingGPU/Utils.h" +#include "DetectorsBase/Propagator.h" + +namespace o2::its +{ +class CellSeed; +struct CellNeighbour; +template +class TrackSeed; +class TrackingFrameInfo; +class Tracklet; +template +class IndexTableUtils; +class Cluster; +class TrackITSExt; +class ExternalAllocator; + +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, + const int linkId, + const int fromLayer, + const int toLayer, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const typename TrackingTopology::View topology, + bounded_vector& linkPhiCuts, + const float resolutionPV, + std::array& minR, + std::array& maxR, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& linkMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, + const int linkId, + const int fromLayer, + const int toLayer, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const typename TrackingTopology::View topology, + bounded_vector& linkPhiCuts, + const float resolutionPV, + std::array& minR, + std::array& maxR, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& linkMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template +void countCellsHandler(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const typename TrackingTopology::View topology, + CellSeed* cells, + int** cellsLUTsDeviceArray, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template +void computeCellsHandler(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const typename TrackingTopology::View topology, + CellSeed* cells, + int** cellsLUTsDeviceArray, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + gpu::Streams& streams); + +template +void countCellNeighboursHandler(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream); + +void scanCellNeighboursHandler(int* neighboursCursor, + int* neighboursLUT, + const unsigned int nCells, + o2::its::ExternalAllocator* alloc, + gpu::Stream& stream); + +template +void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream); + +int filterCellNeighboursHandler(gpuPair*, + int*, + unsigned int, + gpu::Stream&, + o2::its::ExternalAllocator* = nullptr); + +template +void processNeighboursHandler(const int startLevel, + const int defaultCellTopologyId, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const int* currentCellTopologyIds, + const int* currentCellIds, + const int* nCells, + const unsigned char** usedClusters, + CellNeighbour** neighbours, + int** neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + bounded_vector>& seedsHost, + const float bz, + const float MaxChi2ClusterAttachment, + const float maxChi2NDF, + const int maxHoles, + const int minSeedingClusters, + const LayerMask holeLayerMask, + const LayerMask nonSeedingLayerMask, + const std::vector& layerxX0Host, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template +void countTrackSeedHandler(TrackSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const float Bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template +void computeTrackSeedHandler(TrackSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, + const typename ROFOverlapTable::View& rofOverlaps, + const Cluster** clusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + const int** ROFClusters, + o2::its::TrackITSExt* tracks, + int* trackIndices, + const int* seedLUT, + TrackExtensionHypothesis* activeHypotheses, + TrackExtensionHypothesis* nextHypotheses, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const unsigned int nTracks, + const float Bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const int nLayers, + const int phiBins, + const int maxHypotheses, + const bool extendTop, + const bool extendBot, + const float nSigmaCutPhi, + const float nSigmaCutZ, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +} // namespace o2::its +#endif // ITSTRACKINGGPU_TRACKINGKERNELS_H_ diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/UniquePointer.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/UniquePointer.h deleted file mode 100644 index ce04da3dde622..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/UniquePointer.h +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file UniquePointer.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_UNIQUEPOINTER_H_ -#define ITSTRACKINGGPU_UNIQUEPOINTER_H_ - -#include "Utils.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -namespace -{ -template -struct UniquePointerTraits final { - typedef T* InternalPointer; - - GPUhd() static constexpr T& getReference(const InternalPointer& internalPointer) noexcept - { - return const_cast(*internalPointer); - } - - GPUhd() static constexpr T* getPointer(const InternalPointer& internalPointer) noexcept - { - return const_cast(internalPointer); - } -}; -} // namespace - -template -class UniquePointer final -{ - typedef UniquePointerTraits PointerTraits; - - public: - UniquePointer(); - explicit UniquePointer(const T&); - ~UniquePointer(); - - UniquePointer(const UniquePointer&) = delete; - UniquePointer& operator=(const UniquePointer&) = delete; - - UniquePointer(UniquePointer&&); - UniquePointer& operator=(UniquePointer&&); - - GPUhd() T* get() noexcept; - GPUhd() const T* get() const noexcept; - GPUhd() T& operator*() noexcept; - GPUhd() const T& operator*() const noexcept; - - protected: - void destroy(); - - private: - typename PointerTraits::InternalPointer mDevicePointer; -}; - -template -UniquePointer::UniquePointer() : mDevicePointer{nullptr} -{ - // Nothing to do -} - -template -UniquePointer::UniquePointer(const T& ref) -{ - try { - - utils::host::gpuMalloc(reinterpret_cast(&mDevicePointer), sizeof(T)); - utils::host::gpuMemcpyHostToDevice(mDevicePointer, &ref, sizeof(T)); - - } catch (...) { - - destroy(); - - throw; - } -} - -template -UniquePointer::~UniquePointer() -{ - destroy(); -} - -template -UniquePointer::UniquePointer(UniquePointer&& other) : mDevicePointer{other.mDevicePointer} -{ - // Nothing to do -} - -template -UniquePointer& UniquePointer::operator=(UniquePointer&& other) -{ - mDevicePointer = other.mDevicePointer; - other.mDevicePointer = nullptr; - - return *this; -} - -template -void UniquePointer::destroy() -{ - if (mDevicePointer != nullptr) { - - utils::host::gpuFree(mDevicePointer); - } -} - -template -GPUhd() T* UniquePointer::get() noexcept -{ - return PointerTraits::getPointer(mDevicePointer); -} - -template -GPUhd() const T* UniquePointer::get() const noexcept -{ - return PointerTraits::getPointer(mDevicePointer); -} - -template -GPUhd() T& UniquePointer::operator*() noexcept -{ - return PointerTraits::getReference(mDevicePointer); -} - -template -GPUhd() const T& UniquePointer::operator*() const noexcept -{ - return PointerTraits::getReference(mDevicePointer); -} -} // namespace gpu -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h index 42b2777d9b4dd..bcc20ace7bbc2 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h @@ -16,60 +16,445 @@ #ifndef ITSTRACKINGGPU_UTILS_H_ #define ITSTRACKINGGPU_UTILS_H_ +#include +#include +#include + +#include "ITStracking/MathUtils.h" +#include "ITStracking/ExternalAllocator.h" + #include "GPUCommonDef.h" -#include "Stream.h" +#include "GPUCommonHelpers.h" +#include "GPUCommonLogger.h" +#include "GPUCommonDefAPI.h" -namespace o2 -{ -namespace its +#ifdef GPUCA_GPUCODE +#include +#ifndef __HIPCC__ +#define THRUST_NAMESPACE thrust::cuda +#else +#define THRUST_NAMESPACE thrust::hip +#endif +#endif + +#ifdef ITS_GPU_LOG +#define GPULog(...) \ + do { \ + LOGP(info, __VA_ARGS__); \ + GPUChkErrS(cudaDeviceSynchronize()); \ + } while (0) +#else +#define GPULog(...) +#endif + +namespace o2::its { +// FWD declarations +template +class IndexTableUtils; +class Tracklet; + +template +using gpuPair = std::pair; + namespace gpu { -template -GPUhd() T* getPtrFromRuler(int index, T* src, const int* ruler, const int stride = 1) + +template +struct gpuSpan { + using value_type = T; + using ptr = T*; + using ref = T&; + + GPUd() gpuSpan() : _data(nullptr), _size(0) {} + GPUd() gpuSpan(ptr data, unsigned int dim) : _data(data), _size(dim) {} + GPUd() ref operator[](unsigned int idx) const { return _data[idx]; } + GPUd() unsigned int size() const { return _size; } + GPUd() bool empty() const { return _size == 0; } + GPUd() ref front() const { return _data[0]; } + GPUd() ref back() const { return _data[_size - 1]; } + GPUd() ptr begin() const { return _data; } + GPUd() ptr end() const { return _data + _size; } + + protected: + ptr _data; + unsigned int _size; +}; + +template +struct gpuSpan { + using value_type = T; + using ptr = const T*; + using ref = const T&; + + GPUd() gpuSpan() : _data(nullptr), _size(0) {} + GPUd() gpuSpan(ptr data, unsigned int dim) : _data(data), _size(dim) {} + GPUd() gpuSpan(const gpuSpan& other) : _data(other._data), _size(other._size) {} + GPUd() ref operator[](unsigned int idx) const { return _data[idx]; } + GPUd() unsigned int size() const { return _size; } + GPUd() bool empty() const { return _size == 0; } + GPUd() ref front() const { return _data[0]; } + GPUd() ref back() const { return _data[_size - 1]; } + GPUd() ptr begin() const { return _data; } + GPUd() ptr end() const { return _data + _size; } + + protected: + ptr _data; + unsigned int _size; +}; + +// Abstract stream class +class Stream { - return src + ruler[index] * stride; -} + public: +#if defined(__HIPCC__) + using Handle = hipStream_t; + static constexpr Handle DefaultStream = 0; + static constexpr unsigned int DefaultFlag = hipStreamNonBlocking; + using Event = hipEvent_t; +#elif defined(__CUDACC__) + using Handle = cudaStream_t; + static constexpr Handle DefaultStream = 0; + static constexpr unsigned int DefaultFlag = cudaStreamNonBlocking; + using Event = cudaEvent_t; +#else + using Handle = void*; + static constexpr Handle DefaultStream = nullptr; + static constexpr unsigned int DefaultFlag = 0; + using Event = void*; +#endif + + Stream(unsigned int flags = DefaultFlag) + { +#if defined(__HIPCC__) + GPUChkErrS(hipStreamCreateWithFlags(&mHandle, flags)); + GPUChkErrS(hipEventCreateWithFlags(&mEvent, hipEventDisableTiming)); +#elif defined(__CUDACC__) + GPUChkErrS(cudaStreamCreateWithFlags(&mHandle, flags)); + GPUChkErrS(cudaEventCreateWithFlags(&mEvent, cudaEventDisableTiming)); +#endif + } + + Stream(Handle h) : mHandle(h) {} + ~Stream() + { + if (mHandle != DefaultStream) { +#if defined(__HIPCC__) + GPUChkErrS(hipStreamDestroy(mHandle)); + GPUChkErrS(hipEventDestroy(mEvent)); +#elif defined(__CUDACC__) + GPUChkErrS(cudaStreamDestroy(mHandle)); + GPUChkErrS(cudaEventDestroy(mEvent)); +#endif + } + } + + operator bool() const { return mHandle != DefaultStream; } + const Handle& get() { return mHandle; } + const Handle& getStream() { return mHandle; } + const Event& getEvent() { return mEvent; } + void sync() const + { +#if defined(__HIPCC__) + GPUChkErrS(hipStreamSynchronize(mHandle)); +#elif defined(__CUDACC__) + GPUChkErrS(cudaStreamSynchronize(mHandle)); +#endif + } + void record() + { +#if defined(__HIPCC__) + GPUChkErrS(hipEventRecord(mEvent, mHandle)); +#elif defined(__CUDACC__) + GPUChkErrS(cudaEventRecord(mEvent, mHandle)); +#endif + } -template -GPUhd() const T* getPtrFromRuler(int index, const T* src, const int* ruler, const int stride = 1) + private: + Handle mHandle{DefaultStream}; + Event mEvent{nullptr}; +}; + +// Abstract vector for streams. +class Streams { - return src + ruler[index] * stride; -} + public: + size_t size() const noexcept { return mStreams.size(); } + void resize(size_t n) { mStreams.resize(n); } + void clear() { mStreams.clear(); } + auto& operator[](size_t i) { return mStreams[i]; } + void push_back(const Stream& stream) { mStreams.push_back(stream); } + void sync(bool device = true) + { + if (device) { +#if defined(__HIPCC__) + GPUChkErrS(hipDeviceSynchronize()); +#elif defined(__CUDACC__) + GPUChkErrS(cudaDeviceSynchronize()); +#endif + } else { + for (auto& s : mStreams) { + s.sync(); + } + } + } + void waitEvent(size_t iStream, size_t iEvent) + { +#if defined(__HIPCC__) + GPUChkErrS(hipStreamWaitEvent(mStreams[iStream].get(), mStreams[iEvent].getEvent())); +#elif defined(__CUDACC__) + GPUChkErrS(cudaStreamWaitEvent(mStreams[iStream].get(), mStreams[iEvent].getEvent())); +#endif + } -GPUh() void gpuThrowOnError(); + private: + std::vector mStreams; +}; -namespace utils +#ifdef ITS_MEASURE_GPU_TIME +class GPUTimer { -#ifdef __CUDACC__ -void checkGPUError(const cudaError_t error, const char* file = __FILE__, const int line = __LINE__); + public: + GPUTimer(const std::string& name) + : mName(name) + { + mStreams.emplace_back(Stream::DefaultStream); + startTimers(); + } + GPUTimer(Streams& streams, const std::string& name) + : mName(name) + { + for (size_t i{0}; i < streams.size(); ++i) { + mStreams.push_back(streams[i].get()); + } + startTimers(); + } + GPUTimer(Streams& streams, const std::string& name, size_t end, size_t start = 0) + : mName(name) + { + for (size_t sta{start}; sta < end; ++sta) { + mStreams.push_back(streams[sta].get()); + } + startTimers(); + } + GPUTimer(Stream& stream, const std::string& name, const int id = 0) + : mName(name) + { + mStreams.push_back(stream.get()); + mName += ":id" + std::to_string(id); + startTimers(); + } + ~GPUTimer() + { + for (size_t i{0}; i < mStreams.size(); ++i) { + float ms = 0.0f; +#if defined(__HIPCC__) + GPUChkErrS(hipEventRecord(mStops[i], mStreams[i])); + GPUChkErrS(hipEventSynchronize(mStops[i])); + GPUChkErrS(hipEventElapsedTime(&ms, mStarts[i], mStops[i])); + GPUChkErrS(hipEventDestroy(mStarts[i])); + GPUChkErrS(hipEventDestroy(mStops[i])); +#elif defined(__CUDACC__) + GPUChkErrS(cudaEventRecord(mStops[i], mStreams[i])); + GPUChkErrS(cudaEventSynchronize(mStops[i])); + GPUChkErrS(cudaEventElapsedTime(&ms, mStarts[i], mStops[i])); + GPUChkErrS(cudaEventDestroy(mStarts[i])); + GPUChkErrS(cudaEventDestroy(mStops[i])); +#endif + LOGP(info, "Elapsed time for {}:{} {} ms", mName, i, ms); + } + } + + void startTimers() + { + mStarts.resize(mStreams.size()); + mStops.resize(mStreams.size()); + for (size_t i{0}; i < mStreams.size(); ++i) { +#if defined(__HIPCC__) + GPUChkErrS(hipEventCreate(&mStarts[i])); + GPUChkErrS(hipEventCreate(&mStops[i])); + GPUChkErrS(hipEventRecord(mStarts[i], mStreams[i])); +#elif defined(__CUDACC__) + GPUChkErrS(cudaEventCreate(&mStarts[i])); + GPUChkErrS(cudaEventCreate(&mStops[i])); + GPUChkErrS(cudaEventRecord(mStarts[i], mStreams[i])); #endif -#ifdef __HIPCC__ -void checkGPUError(const hipError_t error, const char* file = __FILE__, const int line = __LINE__); + } + } + + private: + std::string mName; + std::vector mStarts, mStops; + std::vector mStreams; +}; +#else // ITS_MEASURE_GPU_TIME not defined +class GPUTimer +{ + public: + template + GPUTimer(Args&&...) + { + } +}; #endif -// Dump device properties -void getDeviceProp(int, bool verbose = true); - -dim3 getBlockSize(const int); -dim3 getBlockSize(const int, const int); -dim3 getBlockSize(const int, const int, const int); -dim3 getBlocksGrid(const dim3&, const int); -dim3 getBlocksGrid(const dim3&, const int, const int); - -void gpuMalloc(void**, const int); -void gpuFree(void*); -void gpuMemset(void*, int, int); -void gpuMemcpyHostToDevice(void*, const void*, int); -void gpuMemcpyDeviceToHost(void*, const void*, int); -void gpuMemcpyToSymbol(const void* symbol, const void* src, int size); -void gpuMemcpyFromSymbol(void* dst, const void* symbol, int size); - -GPUd() int getLaneIndex(); -GPUd() int shareToWarp(const int, const int); -} // namespace utils +#ifdef GPUCA_GPUCODE +template +struct TypedAllocator { + using value_type = T; + using pointer = thrust::device_ptr; + using const_pointer = thrust::device_ptr; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + TypedAllocator() noexcept : mInternalAllocator(nullptr) {} + explicit TypedAllocator(ExternalAllocator* a) noexcept : mInternalAllocator(a) {} + + template + TypedAllocator(const TypedAllocator& o) noexcept : mInternalAllocator(o.mInternalAllocator) + { + } + + pointer allocate(size_type n) + { + void* raw = mInternalAllocator->allocateStack(n * sizeof(T)); + return thrust::device_pointer_cast(static_cast(raw)); + } + + void deallocate(pointer p, size_type n) noexcept + { + if (!p) { + return; + } + void* raw = thrust::raw_pointer_cast(p); + mInternalAllocator->deallocate(static_cast(raw), n * sizeof(T)); + } + + bool operator==(TypedAllocator const& o) const noexcept + { + return mInternalAllocator == o.mInternalAllocator; + } + bool operator!=(TypedAllocator const& o) const noexcept + { + return !(*this == o); + } + + private: + ExternalAllocator* mInternalAllocator; +}; + +GPUdii() gpuSpan getPrimaryVertices(const int rof, + const int* roframesPV, + const int nROF, + const uint8_t* mask, + const Vertex* vertices) +{ + const int start_pv_id = roframesPV[rof]; + const int stop_rof = rof >= nROF - 1 ? nROF : rof + 1; + size_t delta = mask[rof] ? roframesPV[stop_rof] - start_pv_id : 0; // return empty span if ROF is excluded + return gpuSpan(&vertices[start_pv_id], delta); +}; + +GPUdii() gpuSpan getPrimaryVertices(const int romin, + const int romax, + const int* roframesPV, + const int nROF, + const Vertex* vertices) +{ + const int start_pv_id = roframesPV[romin]; + const int stop_rof = romax >= nROF - 1 ? nROF : romax + 1; + return gpuSpan(&vertices[start_pv_id], roframesPV[stop_rof] - roframesPV[romin]); +}; + +GPUdii() gpuSpan getClustersOnLayer(const int rof, + const int totROFs, + const int layer, + const int** roframesClus, + const Cluster** clusters) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[layer][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[layer][stop_rof] - start_clus_id; + return gpuSpan(&(clusters[layer][start_clus_id]), delta); +} + +GPUdii() gpuSpan getTrackletsPerCluster(const int rof, + const int totROFs, + const int mode, + const int** roframesClus, + const Tracklet** tracklets) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[1][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[1][stop_rof] - start_clus_id; + return gpuSpan(&(tracklets[mode][start_clus_id]), delta); +} + +GPUdii() gpuSpan getNTrackletsPerCluster(const int rof, + const int totROFs, + const int mode, + const int** roframesClus, + int** ntracklets) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[1][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[1][stop_rof] - start_clus_id; + return gpuSpan(&(ntracklets[mode][start_clus_id]), delta); +} + +GPUdii() gpuSpan getNTrackletsPerCluster(const int rof, + const int totROFs, + const int mode, + const int** roframesClus, + const int** ntracklets) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[1][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[1][stop_rof] - start_clus_id; + return gpuSpan(&(ntracklets[mode][start_clus_id]), delta); +} + +GPUdii() gpuSpan getNLinesPerCluster(const int rof, + const int totROFs, + const int** roframesClus, + int* nlines) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[1][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[1][stop_rof] - start_clus_id; + return gpuSpan(&(nlines[start_clus_id]), delta); +} + +GPUdii() gpuSpan getNLinesPerCluster(const int rof, + const int totROFs, + const int** roframesClus, + const int* nlines) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[1][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[1][stop_rof] - start_clus_id; + return gpuSpan(&(nlines[start_clus_id]), delta); +} +#endif } // namespace gpu -} // namespace its -} // namespace o2 +} // namespace o2::its -#endif \ No newline at end of file +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Vector.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Vector.h deleted file mode 100644 index 3912caec8449c..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Vector.h +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Vector.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_VECTOR_H_ -#define ITSTRACKINGGPU_VECTOR_H_ - -#include -#include -#include -#include - -#include "Stream.h" -#include "Utils.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -template -class Vector final -{ - static_assert(std::is_trivially_destructible::value, "Vector only supports trivially destructible objects."); - - public: - Vector(); - explicit Vector(const size_t, const size_t = 0); - Vector(const T* const, const size_t, const size_t = 0); - GPUhd() ~Vector(); - - Vector(const Vector&) = delete; - Vector& operator=(const Vector&) = delete; - - GPUhd() Vector(Vector&&); - Vector& operator=(Vector&&); - - size_t getSizeFromDevice() const; - - T getElementFromDevice(const size_t) const; - - void resize(const size_t); - void reset(const size_t, const size_t = 0); - void reset(const T* const, const size_t, const size_t = 0); - - void resetWithInt(const size_t, const int value = 0); - void copyIntoSizedVector(std::vector&); - - GPUhd() T* get() const; - GPUhd() size_t capacity() const; - GPUhd() Vector getWeakCopy() const; - GPUd() T& operator[](const size_t) const; - - GPUd() size_t size() const; - GPUhd() void dump(); - - template - GPUd() void emplace(const size_t, Args&&...); - - protected: - void destroy(); - - private: - GPUhd() Vector(const Vector&, const bool); - - T* mArrayPtr = nullptr; - size_t* mDeviceSizePtr = nullptr; - size_t mCapacity; - bool mIsWeak; -}; - -template -Vector::Vector() : Vector{nullptr, 0} -{ - // Nothing to do -} - -template -Vector::Vector(const size_t capacity, const size_t initialSize) : Vector{nullptr, capacity, initialSize} -{ - // Nothing to do -} - -template -Vector::Vector(const T* const source, const size_t size, const size_t initialSize) : mCapacity{size}, mIsWeak{false} -{ - if (size > 0) { - try { - - utils::gpuMalloc(reinterpret_cast(&mArrayPtr), size * sizeof(T)); - utils::gpuMalloc(reinterpret_cast(&mDeviceSizePtr), sizeof(size_t)); - - if (source != nullptr) { - - utils::gpuMemcpyHostToDevice(mArrayPtr, source, size * sizeof(T)); - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &size, sizeof(size_t)); - - } else { - - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &initialSize, sizeof(size_t)); - } - - } catch (...) { - - destroy(); - - throw; - } - } -} - -template -GPUhd() Vector::Vector(const Vector& other, const bool isWeak) - : mArrayPtr{other.mArrayPtr}, - mDeviceSizePtr{other.mDeviceSizePtr}, - mCapacity{other.mCapacity}, - mIsWeak{isWeak} -{ - // Nothing to do -} - -template -GPUhd() Vector::~Vector() -{ - if (mIsWeak) { - return; - } else { -#if defined(TRACKINGITSU_GPU_DEVICE) - assert(0); -#else - destroy(); -#endif - } -} - -template -GPUhd() Vector::Vector(Vector&& other) - : mArrayPtr{other.mArrayPtr}, - mDeviceSizePtr{other.mDeviceSizePtr}, - mCapacity{other.mCapacity}, - mIsWeak{other.mIsWeak} -{ - other.mArrayPtr = nullptr; - other.mDeviceSizePtr = nullptr; -} - -template -Vector& Vector::operator=(Vector&& other) -{ - destroy(); - - mArrayPtr = other.mArrayPtr; - mDeviceSizePtr = other.mDeviceSizePtr; - mCapacity = other.mCapacity; - mIsWeak = other.mIsWeak; - - other.mArrayPtr = nullptr; - other.mDeviceSizePtr = nullptr; - - return *this; -} - -template -size_t Vector::getSizeFromDevice() const -{ - size_t size; - utils::gpuMemcpyDeviceToHost(&size, mDeviceSizePtr, sizeof(size_t)); - - return size; -} - -template -void Vector::resize(const size_t size) -{ - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &size, sizeof(size_t)); -} - -template -void Vector::reset(const size_t capacity, const size_t initialSize) -{ - reset(nullptr, capacity, initialSize); -} - -template -void Vector::reset(const T* const source, const size_t size, const size_t initialSize) -{ - if (size > mCapacity) { - if (mArrayPtr != nullptr) { - utils::gpuFree(mArrayPtr); - } - utils::gpuMalloc(reinterpret_cast(&mArrayPtr), size * sizeof(T)); - mCapacity = size; - } - if (mDeviceSizePtr == nullptr) { - utils::gpuMalloc(reinterpret_cast(&mDeviceSizePtr), sizeof(size_t)); - } - - if (source != nullptr) { - utils::gpuMemcpyHostToDevice(mArrayPtr, source, size * sizeof(T)); - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &size, sizeof(size_t)); - } else { - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &initialSize, sizeof(size_t)); - } -} - -template -void Vector::resetWithInt(const size_t size, const int value) -{ - if (size > mCapacity) { - if (mArrayPtr != nullptr) { - utils::gpuFree(mArrayPtr); - } - utils::gpuMalloc(reinterpret_cast(&mArrayPtr), size * sizeof(int)); - mCapacity = size; - } - if (mDeviceSizePtr == nullptr) { - utils::gpuMalloc(reinterpret_cast(&mDeviceSizePtr), sizeof(int)); - } - - utils::gpuMemset(mArrayPtr, value, size * sizeof(int)); - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &size, sizeof(int)); -} - -template -void Vector::copyIntoSizedVector(std::vector& destinationVector) -{ - utils::gpuMemcpyDeviceToHost(destinationVector.data(), mArrayPtr, destinationVector.size() * sizeof(T)); -} - -template -inline void Vector::destroy() -{ - if (mArrayPtr != nullptr) { - utils::gpuFree(mArrayPtr); - } - if (mDeviceSizePtr != nullptr) { - utils::gpuFree(mDeviceSizePtr); - } -} - -template -GPUhd() T* Vector::get() const -{ - return mArrayPtr; -} - -template -GPUhd() size_t Vector::capacity() const -{ - return mCapacity; -} - -template -GPUhd() Vector Vector::getWeakCopy() const -{ - return Vector{*this, true}; -} - -template -GPUd() T& Vector::operator[](const size_t index) const -{ - return mArrayPtr[index]; -} - -template -GPUh() T Vector::getElementFromDevice(const size_t index) const -{ - T element; - utils::gpuMemcpyDeviceToHost(&element, mArrayPtr + index, sizeof(T)); - - return element; -} - -template -GPUd() size_t Vector::size() const -{ - return *mDeviceSizePtr; -} - -template -template -GPUd() void Vector::emplace(const size_t index, Args&&... arguments) -{ - new (mArrayPtr + index) T(std::forward(arguments)...); -} - -template -GPUhd() void Vector::dump() -{ - printf("mArrayPtr = %p\nmDeviceSize = %p\nmCapacity = %d\nmIsWeak = %s\n", - mArrayPtr, mDeviceSizePtr, mCapacity, mIsWeak ? "true" : "false"); -} -} // namespace gpu -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h deleted file mode 100644 index f9d3e3fd0ed61..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file VertexerTraitsGPU.h -/// \brief -/// \author matteo.concas@cern.ch - -// #define VTX_DEBUG -#ifndef ITSTRACKINGGPU_VERTEXERTRAITSGPU_H_ -#define ITSTRACKINGGPU_VERTEXERTRAITSGPU_H_ - -#include -#include - -#include "ITStracking/VertexerTraits.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/Cluster.h" -#include "ITStracking/Constants.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/Tracklet.h" - -#include "ITStrackingGPU/TimeFrameGPU.h" - -namespace o2 -{ -namespace its -{ -class ROframe; - -using constants::its2::InversePhiBinSize; - -class VertexerTraitsGPU : public VertexerTraits -{ - public: - VertexerTraitsGPU(); - ~VertexerTraitsGPU() override; - void initialise(const TrackingParameters&) override; - void adoptTimeFrame(TimeFrame*) override; - void computeTracklets() override; - void computeTrackletMatching() override; - void computeVertices() override; - void updateVertexingParameters(const VertexingParameters&, const TimeFrameGPUParameters&) override; - - void computeVerticesHist(); - - protected: - IndexTableUtils* mDeviceIndexTableUtils; - gpu::TimeFrameGPU<7>* mTimeFrameGPU; - TimeFrameGPUParameters mTfGPUParams; -}; - -inline void VertexerTraitsGPU::adoptTimeFrame(TimeFrame* tf) { mTimeFrameGPU = static_cast*>(tf); } - -} // namespace its -} // namespace o2 -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt index 6ab442f3d33b8..38f11265682ce 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt @@ -11,27 +11,30 @@ # CUDA if(CUDA_ENABLED) -find_package(CUDAToolkit) + find_package(CUDAToolkit) + message(STATUS "Building ITS CUDA tracker") + o2_add_library(ITStrackingCUDA + SOURCES TrackerTraitsGPU.cxx + TimeFrameGPU.cu + TrackingKernels.cu + PUBLIC_INCLUDE_DIRECTORIES ../ + PUBLIC_LINK_LIBRARIES O2::ITStracking + O2::MathUtils + O2::SimConfig + O2::SimulationDataFormat + O2::ReconstructionDataFormats + O2::GPUCommon + PRIVATE_LINK_LIBRARIES O2::GPUTrackingCUDAExternalProvider + TARGETVARNAME targetName) -message(STATUS "Building ITS CUDA tracker") - -o2_add_library(ITStrackingCUDA - SOURCES ClusterLinesGPU.cu - Context.cu - Stream.cu - TrackerTraitsGPU.cu - TimeFrameGPU.cu - TracerGPU.cu - VertexerTraitsGPU.cu - Utils.cu - PUBLIC_INCLUDE_DIRECTORIES ../ - PUBLIC_LINK_LIBRARIES O2::ITStracking - O2::SimConfig - O2::SimulationDataFormat - CUDA::nvToolsExt # TODO: change to CUDA::nvtx3 when CMake bump >= 3.25 - TARGETVARNAME targetName) - -set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) -target_compile_definitions(${targetName} PRIVATE GPUCA_O2_LIB $) - -endif() \ No newline at end of file + set_target_gpu_arch("CUDA" ${targetName}) + # Enable relocatable device code (needed for separable compilation + debugging) + set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) + target_compile_options(${targetName} PRIVATE + $<$:-diag-error=20014> + # $<$:-G;-O0;-Xptxas=-O0> + # $<$:-O0;-g> + ) + # target_compile_definitions(${targetName} PRIVATE ITS_MEASURE_GPU_TIME ITS_GPU_LOG) + target_compile_definitions(${targetName} PRIVATE $) +endif() diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu deleted file mode 100644 index e7753087715d5..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \author matteo.concas@cern.ch - -#include "ITStrackingGPU/ClusterLinesGPU.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -GPUd() ClusterLinesGPU::ClusterLinesGPU(const Line& firstLine, const Line& secondLine) -{ - float covarianceFirst[3]; - float covarianceSecond[3]; - - for (int i{0}; i < 3; ++i) { - covarianceFirst[i] = 1.f; - covarianceSecond[i] = 1.f; - } - - double determinantFirst = - firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] * covarianceFirst[2] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1] * covarianceFirst[2]; - double determinantSecond = - secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] * covarianceSecond[2] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1] * covarianceSecond[2]; - - mAMatrix[0] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[1] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[1] * covarianceFirst[2] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[1] * covarianceSecond[2] / determinantSecond; - - mAMatrix[2] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[2] * covarianceFirst[1] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[2] * covarianceSecond[1] / determinantSecond; - - mAMatrix[3] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[4] = -firstLine.cosinesDirector[1] * firstLine.cosinesDirector[2] * covarianceFirst[0] / determinantFirst - - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[2] * covarianceSecond[0] / determinantSecond; - - mAMatrix[5] = (firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1]) / - determinantFirst + - (secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1]) / - determinantSecond; - - mBMatrix[0] = - (firstLine.cosinesDirector[1] * covarianceFirst[2] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[1]) + - firstLine.cosinesDirector[2] * covarianceFirst[1] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[0] += - (secondLine.cosinesDirector[1] * covarianceSecond[2] * (-secondLine.cosinesDirector[1] * secondLine.originPoint[0] + secondLine.cosinesDirector[0] * secondLine.originPoint[1]) + - secondLine.cosinesDirector[2] * covarianceSecond[1] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[0] + - secondLine.cosinesDirector[0] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[1] = - (firstLine.cosinesDirector[0] * covarianceFirst[2] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[2] * covarianceFirst[0] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[1] += - (secondLine.cosinesDirector[0] * covarianceSecond[2] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[1] + secondLine.cosinesDirector[1] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[2] * covarianceSecond[0] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[1] + - secondLine.cosinesDirector[1] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[2] = - (firstLine.cosinesDirector[0] * covarianceFirst[1] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[1] * covarianceFirst[0] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[1])) / - determinantFirst; - - mBMatrix[2] += - (secondLine.cosinesDirector[0] * covarianceSecond[1] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[2] + secondLine.cosinesDirector[2] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[1] * covarianceSecond[0] * - (-secondLine.cosinesDirector[1] * secondLine.originPoint[2] + - secondLine.cosinesDirector[2] * secondLine.originPoint[1])) / - determinantSecond; - - computeClusterCentroid(); -} - -GPUd() void ClusterLinesGPU::computeClusterCentroid() -{ - - double determinant{mAMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - - mAMatrix[1] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + - mAMatrix[2] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])}; - - if (determinant == 0) { - return; - } - - mVertex[0] = -(mBMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - - mAMatrix[1] * (mBMatrix[1] * mAMatrix[5] - mAMatrix[4] * mBMatrix[2]) + - mAMatrix[2] * (mBMatrix[1] * mAMatrix[4] - mBMatrix[2] * mAMatrix[3])) / - determinant; - mVertex[1] = -(mAMatrix[0] * (mBMatrix[1] * mAMatrix[5] - mBMatrix[2] * mAMatrix[4]) - - mBMatrix[0] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + - mAMatrix[2] * (mAMatrix[1] * mBMatrix[2] - mAMatrix[2] * mBMatrix[1])) / - determinant; - mVertex[2] = -(mAMatrix[0] * (mAMatrix[3] * mBMatrix[2] - mBMatrix[1] * mAMatrix[4]) - - mAMatrix[1] * (mAMatrix[1] * mBMatrix[2] - mBMatrix[1] * mAMatrix[2]) + - mBMatrix[0] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])) / - determinant; -} -} // namespace gpu -} // namespace its -} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Context.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Context.cu deleted file mode 100644 index 2df3544012e59..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Context.cu +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITStrackingGPU/Context.h" -#include "ITStrackingGPU/Utils.h" - -#include -#include -#include - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -using utils::checkGPUError; - -Context::Context(bool dumpDevices) -{ - checkGPUError(cudaGetDeviceCount(&mDevicesNum), __FILE__, __LINE__); - - if (mDevicesNum == 0) { - throw std::runtime_error{"There are no available GPU device(s)\n"}; - } - - mDeviceProperties.resize(mDevicesNum, DeviceProperties{}); - - int currentDeviceIndex; - checkGPUError(cudaGetDevice(¤tDeviceIndex), __FILE__, __LINE__); - - for (int iDevice{0}; iDevice < mDevicesNum; ++iDevice) { - - cudaDeviceProp deviceProperties; - - checkGPUError(cudaSetDevice(iDevice), __FILE__, __LINE__); - checkGPUError(cudaGetDeviceProperties(&deviceProperties, iDevice), __FILE__, __LINE__); - - int major = deviceProperties.major; - int minor = deviceProperties.minor; - - mDeviceProperties[iDevice].name = deviceProperties.name; - mDeviceProperties[iDevice].gpuProcessors = deviceProperties.multiProcessorCount; - mDeviceProperties[iDevice].gpuCores = getGPUCores(major, minor) * deviceProperties.multiProcessorCount; - mDeviceProperties[iDevice].globalMemorySize = deviceProperties.totalGlobalMem; - mDeviceProperties[iDevice].constantMemorySize = deviceProperties.totalConstMem; - mDeviceProperties[iDevice].sharedMemorySize = deviceProperties.sharedMemPerBlock; - mDeviceProperties[iDevice].maxClockRate = deviceProperties.memoryClockRate; - mDeviceProperties[iDevice].busWidth = deviceProperties.memoryBusWidth; - mDeviceProperties[iDevice].l2CacheSize = deviceProperties.l2CacheSize; - mDeviceProperties[iDevice].registersPerBlock = deviceProperties.regsPerBlock; - mDeviceProperties[iDevice].warpSize = deviceProperties.warpSize; - mDeviceProperties[iDevice].maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock; - mDeviceProperties[iDevice].maxBlocksPerSM = getGPUMaxThreadsPerComputingUnit(); - mDeviceProperties[iDevice].maxThreadsDim = dim3{static_cast(deviceProperties.maxThreadsDim[0]), - static_cast(deviceProperties.maxThreadsDim[1]), - static_cast(deviceProperties.maxThreadsDim[2])}; - mDeviceProperties[iDevice].maxGridDim = dim3{static_cast(deviceProperties.maxGridSize[0]), - static_cast(deviceProperties.maxGridSize[1]), - static_cast(deviceProperties.maxGridSize[2])}; - if (dumpDevices) { - std::cout << "################ " << GPU_ARCH << " DEVICE " << iDevice << " ################" << std::endl; - std::cout << "Name " << mDeviceProperties[iDevice].name << std::endl; - std::cout << "minor " << minor << " major " << major << std::endl; - std::cout << "gpuProcessors " << mDeviceProperties[iDevice].gpuProcessors << std::endl; - std::cout << "gpuCores " << mDeviceProperties[iDevice].gpuCores << std::endl; - std::cout << "globalMemorySize " << mDeviceProperties[iDevice].globalMemorySize << std::endl; - std::cout << "constantMemorySize " << mDeviceProperties[iDevice].constantMemorySize << std::endl; - std::cout << "sharedMemorySize " << mDeviceProperties[iDevice].sharedMemorySize << std::endl; - std::cout << "maxClockRate " << mDeviceProperties[iDevice].maxClockRate << std::endl; - std::cout << "busWidth " << mDeviceProperties[iDevice].busWidth << std::endl; - std::cout << "l2CacheSize " << mDeviceProperties[iDevice].l2CacheSize << std::endl; - std::cout << "registersPerBlock " << mDeviceProperties[iDevice].registersPerBlock << std::endl; - std::cout << "warpSize " << mDeviceProperties[iDevice].warpSize << std::endl; - std::cout << "maxThreadsPerBlock " << mDeviceProperties[iDevice].maxThreadsPerBlock << std::endl; - std::cout << "maxBlocksPerSM " << mDeviceProperties[iDevice].maxBlocksPerSM << std::endl; - std::cout << "maxThreadsDim " << mDeviceProperties[iDevice].maxThreadsDim.x << ", " - << mDeviceProperties[iDevice].maxThreadsDim.y << ", " - << mDeviceProperties[iDevice].maxThreadsDim.z << std::endl; - std::cout << "maxGridDim " << mDeviceProperties[iDevice].maxGridDim.x << ", " - << mDeviceProperties[iDevice].maxGridDim.y << ", " - << mDeviceProperties[iDevice].maxGridDim.z << std::endl; - std::cout << std::endl; - } - } - - checkGPUError(cudaSetDevice(currentDeviceIndex), __FILE__, __LINE__); -} - -Context& Context::getInstance() -{ - static Context gpuContext; - return gpuContext; -} - -const DeviceProperties& Context::getDeviceProperties() -{ - int currentDeviceIndex; - checkGPUError(cudaGetDevice(¤tDeviceIndex), __FILE__, __LINE__); - - return getDeviceProperties(currentDeviceIndex); -} - -const DeviceProperties& Context::getDeviceProperties(const int deviceIndex) -{ - return mDeviceProperties[deviceIndex]; -} - -} // namespace gpu -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Stream.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Stream.cu deleted file mode 100644 index 980ba507bd613..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Stream.cu +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// - -#include "ITStrackingGPU/Stream.h" -#include "ITStrackingGPU/Utils.h" -#include "GPUCommonLogger.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ -using utils::checkGPUError; - -Stream::Stream() -{ - checkGPUError(cudaStreamCreate(&mStream)); -} -Stream::~Stream() -{ - LOGP(info, "Destroying stream"); - checkGPUError(cudaStreamDestroy(mStream)); -} - -const GPUStream& Stream::get() const -{ - return mStream; -} - -} // namespace gpu -} // namespace its -} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index 1950bed9138f8..2d467b0d5e151 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -9,427 +9,778 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -#include -#include -#include "ITStracking/Constants.h" +#include + +#include +#include +#include -#include "ITStrackingGPU/Utils.h" #include "ITStrackingGPU/TimeFrameGPU.h" -#include "ITStrackingGPU/TracerGPU.h" +#include "ITStracking/Constants.h" +#include "ITStracking/BoundedAllocator.h" +#include "ITStrackingGPU/Utils.h" -#include -#include +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "GPUCommonLogger.h" +#include "GPUCommonHelpers.h" +#include "utils/strtag.h" -#ifndef __HIPCC__ -#define THRUST_NAMESPACE thrust::cuda -#else -#define THRUST_NAMESPACE thrust::hip -#endif +namespace o2::its::gpu +{ -namespace o2 -{ -namespace its -{ -using constants::GB; -using constants::MB; - -namespace gpu -{ -using utils::checkGPUError; -///////////////////////////////////////////////////////////////////////////////////////// -// GpuChunk -///////////////////////////////////////////////////////////////////////////////////////// -template -GpuTimeFrameChunk::~GpuTimeFrameChunk() -{ - if (mAllocated) { - for (int i = 0; i < nLayers; ++i) { - checkGPUError(cudaFree(mClustersDevice[i])); - checkGPUError(cudaFree(mTrackingFrameInfoDevice[i])); - checkGPUError(cudaFree(mClusterExternalIndicesDevice[i])); - checkGPUError(cudaFree(mIndexTablesDevice[i])); - if (i < nLayers - 1) { - checkGPUError(cudaFree(mTrackletsDevice[i])); - checkGPUError(cudaFree(mTrackletsLookupTablesDevice[i])); - if (i < nLayers - 2) { - checkGPUError(cudaFree(mCellsDevice[i])); - checkGPUError(cudaFree(mCellsLookupTablesDevice[i])); - if (i < nLayers - 3) { - checkGPUError(cudaFree(mNeighboursCellLookupTablesDevice[i])); - checkGPUError(cudaFree(mNeighboursCellDevice[i])); - } - } +template +void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator, int32_t type) +{ + if (extAllocator) { + *ptr = (this->mExternalAllocator)->allocate(size, type); + } else { + GPULog("Calling default CUDA allocator"); + GPUChkErrS(cudaMallocAsync(reinterpret_cast(ptr), size, stream.get())); + } +} + +template +void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, int32_t type) +{ + if (extAllocator) { + *ptr = (this->mExternalAllocator)->allocate(size, type); + } else { + GPULog("Calling default CUDA allocator"); + GPUChkErrS(cudaMalloc(reinterpret_cast(ptr), size)); + } +} + +template +void TimeFrameGPU::loadIndexTableUtils() +{ + GPUTimer timer("loading indextable utils"); + { + GPULog("gpu-allocation: allocating IndexTableUtils buffer, for {:.2f} MB.", sizeof(IndexTableUtilsN) / constants::MB); + allocMem(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtilsN), this->hasFrameworkAllocator()); + } + GPULog("gpu-transfer: loading IndexTableUtils object, for {:.2f} MB.", sizeof(IndexTableUtilsN) / constants::MB); + GPUChkErrS(cudaMemcpy(mIndexTableUtilsDevice, &(this->mIndexTableUtils), sizeof(IndexTableUtilsN), cudaMemcpyHostToDevice)); +} + +template +void TimeFrameGPU::createUnsortedClustersDeviceArray(const int maxLayers) +{ + { + GPUTimer timer("creating unsorted clusters array"); + allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), NLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mUnsortedClustersDevice.data(), NLayers * sizeof(Cluster*), cudaHostRegisterPortable)); + mPinnedUnsortedClusters.set(NLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, NLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mUnsortedClusters[iLayer].data(), this->mUnsortedClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); + mPinnedUnsortedClusters.set(iLayer); } } - checkGPUError(cudaFree(mCUBTmpBufferDevice)); - checkGPUError(cudaFree(mFoundTrackletsDevice)); - checkGPUError(cudaFree(mNFoundCellsDevice)); - } -} - -template -void GpuTimeFrameChunk::allocate(const size_t nrof, Stream& stream) -{ - RANGE("device_partition_allocation", 2); - mNRof = nrof; - for (int i = 0; i < nLayers; ++i) { - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mClustersDevice[i])), sizeof(Cluster) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mTrackingFrameInfoDevice[i])), sizeof(TrackingFrameInfo) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mClusterExternalIndicesDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mIndexTablesDevice[i])), sizeof(int) * (256 * 128 + 1) * nrof, stream.get())); - if (i < nLayers - 1) { - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mTrackletsLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mTrackletsDevice[i])), sizeof(Tracklet) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - if (i < nLayers - 2) { - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mCellsLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->validatedTrackletsCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mCellsDevice[i])), sizeof(Cell) * mTFGPUParams->maxNeighboursSize * nrof, stream.get())); - if (i < nLayers - 3) { - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mNeighboursCellLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mNeighboursCellDevice[i])), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, stream.get())); - } - if (i < 2) { - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mNTrackletsPerClusterDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - } + } +} + +template +void TimeFrameGPU::loadUnsortedClustersDevice(const int layer) +{ + { + GPUTimer timer(mGpuStreams[layer], "loading unsorted clusters", layer); + GPULog("gpu-transfer: loading {} unsorted clusters on layer {}, for {:.2f} MB.", this->mUnsortedClusters[layer].size(), layer, this->mUnsortedClusters[layer].size() * sizeof(Cluster) / constants::MB); + allocMemAsync(reinterpret_cast(&mUnsortedClustersDevice[layer]), this->mUnsortedClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mUnsortedClustersDevice[layer], this->mUnsortedClusters[layer].data(), this->mUnsortedClusters[layer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mUnsortedClustersDeviceArray[layer], &mUnsortedClustersDevice[layer], sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } +} + +template +void TimeFrameGPU::createClustersDeviceArray(const int maxLayers) +{ + { + GPUTimer timer("creating sorted clusters array"); + allocMem(reinterpret_cast(&mClustersDeviceArray), NLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mClustersDevice.data(), NLayers * sizeof(Cluster*), cudaHostRegisterPortable)); + mPinnedClusters.set(NLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, NLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mClusters[iLayer].data(), this->mClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); + mPinnedClusters.set(iLayer); } } } - checkGPUError(cudaMallocAsync(reinterpret_cast(&mCUBTmpBufferDevice), mTFGPUParams->tmpCUBBufferSize * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mLinesDevice), sizeof(Line) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mNFoundLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mNExclusiveFoundLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof + 1, stream.get())); // + 1 for cub::DeviceScan::ExclusiveSum, to cover cases where we have maximum number of clusters per ROF - checkGPUError(cudaMallocAsync(reinterpret_cast(&mUsedTrackletsDevice), sizeof(unsigned char) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mClusteredLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mTFGPUParams->maxTrackletsPerCluster * nrof, stream.get())); +} + +template +void TimeFrameGPU::loadClustersDevice(const int layer) +{ + { + GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); + GPULog("gpu-transfer: loading {} clusters on layer {}, for {:.2f} MB.", this->mClusters[layer].size(), layer, this->mClusters[layer].size() * sizeof(Cluster) / constants::MB); + allocMemAsync(reinterpret_cast(&mClustersDevice[layer]), this->mClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mClustersDevice[layer], this->mClusters[layer].data(), this->mClusters[layer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mClustersDeviceArray[layer], &mClustersDevice[layer], sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } +} - /// Invariant allocations - checkGPUError(cudaMallocAsync(reinterpret_cast(&mFoundTrackletsDevice), (nLayers - 1) * sizeof(int) * nrof, stream.get())); // No need to reset, we always read it after writing - checkGPUError(cudaMallocAsync(reinterpret_cast(&mNFoundCellsDevice), (nLayers - 2) * sizeof(int) * nrof, stream.get())); +template +void TimeFrameGPU::createClustersIndexTablesArray() +{ + { + GPUTimer timer("creating clustersindextable array"); + allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), NLayers * sizeof(int*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mClustersIndexTablesDevice.data(), NLayers * sizeof(int*), cudaHostRegisterPortable)); + mPinnedClustersIndexTables.set(NLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mIndexTables[iLayer].data(), this->mIndexTables[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); + mPinnedClustersIndexTables.set(iLayer); + } + } + } +} - mAllocated = true; +template +void TimeFrameGPU::loadClustersIndexTables(const int layer) +{ + { + GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); + GPULog("gpu-transfer: loading clusters indextable for layer {} with {} elements, for {:.2f} MB.", layer, this->mIndexTables[layer].size(), this->mIndexTables[layer].size() * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mClustersIndexTablesDevice[layer]), this->mIndexTables[layer].size() * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mClustersIndexTablesDevice[layer], this->mIndexTables[layer].data(), this->mIndexTables[layer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mClustersIndexTablesDeviceArray[layer], &mClustersIndexTablesDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } } -template -void GpuTimeFrameChunk::reset(const Task task, Stream& stream) +template +void TimeFrameGPU::createUsedClustersDeviceArray(const int maxLayers) { - RANGE("buffer_reset", 0); - if ((bool)task) { // Vertexer-only initialisation (cannot be constexpr: due to the presence of gpu raw calls can't be put in header) - for (int i = 0; i < 2; i++) { - auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); - auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; - thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); - checkGPUError(cudaMemsetAsync(mNTrackletsPerClusterDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); + { + GPUTimer timer("creating used clusters flags"); + allocMem(reinterpret_cast(&mUsedClustersDeviceArray), NLayers * sizeof(uint8_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), NLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(NLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, NLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(uint8_t), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(iLayer); + } } - checkGPUError(cudaMemsetAsync(mUsedTrackletsDevice, false, sizeof(unsigned char) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - checkGPUError(cudaMemsetAsync(mClusteredLinesDevice, -1, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mTFGPUParams->maxTrackletsPerCluster * mNRof, stream.get())); - } else { - for (int i = 0; i < nLayers; ++i) { - if (i < nLayers - 1) { - checkGPUError(cudaMemsetAsync(mTrackletsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); - auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; - thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); - if (i < nLayers - 2) { - checkGPUError(cudaMemsetAsync(mCellsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->cellsLUTsize * mNRof, stream.get())); - if (i < nLayers - 3) { - checkGPUError(cudaMemsetAsync(mNeighboursCellLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); - checkGPUError(cudaMemsetAsync(mNeighboursCellDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); - } - } + } +} + +template +void TimeFrameGPU::createUsedClustersDevice(const int layer) +{ + { + GPUTimer timer(mGpuStreams[layer], "creating used clusters flags", layer); + GPULog("gpu-transfer: creating {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[layer].size(), layer, this->mUsedClusters[layer].size() * sizeof(unsigned char) / constants::MB); + allocMemAsync(reinterpret_cast(&mUsedClustersDevice[layer]), this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemsetAsync(mUsedClustersDevice[layer], 0, this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mUsedClustersDeviceArray[layer], &mUsedClustersDevice[layer], sizeof(unsigned char*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } +} + +template +void TimeFrameGPU::loadUsedClustersDevice() +{ + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { + GPUTimer timer(mGpuStreams[iLayer], "loading used clusters flags", iLayer); + GPULog("gpu-transfer: loading {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[iLayer].size(), iLayer, this->mUsedClusters[iLayer].size() * sizeof(unsigned char) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(mUsedClustersDevice[iLayer], this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(unsigned char), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); + } +} + +template +void TimeFrameGPU::createROFrameClustersDeviceArray() +{ + { + GPUTimer timer("creating ROFrame clusters array"); + allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), NLayers * sizeof(int*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mROFramesClustersDevice.data(), NLayers * sizeof(int*), cudaHostRegisterPortable)); + mPinnedROFramesClusters.set(NLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mROFramesClusters[iLayer].data(), this->mROFramesClusters[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); + mPinnedROFramesClusters.set(iLayer); } } - checkGPUError(cudaMemsetAsync(mNFoundCellsDevice, 0, (nLayers - 2) * sizeof(int), stream.get())); } } -template -size_t GpuTimeFrameChunk::computeScalingSizeBytes(const int nrof, const TimeFrameGPUParameters& config) +template +void TimeFrameGPU::loadROFrameClustersDevice(const int layer) { - size_t rofsize = nLayers * sizeof(int); // number of clusters per ROF - rofsize += nLayers * sizeof(Cluster) * config.clustersPerROfCapacity; // clusters - rofsize += nLayers * sizeof(TrackingFrameInfo) * config.clustersPerROfCapacity; // tracking frame info - rofsize += nLayers * sizeof(int) * config.clustersPerROfCapacity; // external cluster indices - rofsize += nLayers * sizeof(int) * (256 * 128 + 1); // index tables - rofsize += (nLayers - 1) * sizeof(int) * config.clustersPerROfCapacity; // tracklets lookup tables - rofsize += (nLayers - 1) * sizeof(Tracklet) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // tracklets - rofsize += 2 * sizeof(int) * config.clustersPerROfCapacity; // tracklets found per cluster (vertexer) - rofsize += sizeof(unsigned char) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // used tracklets (vertexer) - rofsize += (nLayers - 2) * sizeof(int) * config.validatedTrackletsCapacity; // cells lookup tables - rofsize += (nLayers - 2) * sizeof(Cell) * config.maxNeighboursSize; // cells - rofsize += (nLayers - 3) * sizeof(int) * config.maxNeighboursSize; // cell neighbours lookup tables - rofsize += (nLayers - 3) * sizeof(int) * config.maxNeighboursSize; // cell neighbours - rofsize += sizeof(Line) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // lines - rofsize += sizeof(int) * config.clustersPerROfCapacity; // found lines - rofsize += sizeof(int) * config.clustersPerROfCapacity; // found lines exclusive sum - rofsize += sizeof(int) * config.clustersPerROfCapacity * config.maxTrackletsPerCluster; // lines used in clusterlines + { + GPUTimer timer(mGpuStreams[layer], "loading ROframe clusters", layer); + GPULog("gpu-transfer: loading {} ROframe clusters info on layer {}, for {:.2f} MB.", this->mROFramesClusters[layer].size(), layer, this->mROFramesClusters[layer].size() * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mROFramesClustersDevice[layer]), this->mROFramesClusters[layer].size() * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mROFramesClustersDevice[layer], this->mROFramesClusters[layer].data(), this->mROFramesClusters[layer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mROFramesClustersDeviceArray[layer], &mROFramesClustersDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } +} - rofsize += (nLayers - 1) * sizeof(int); // total found tracklets - rofsize += (nLayers - 2) * sizeof(int); // total found cells +template +void TimeFrameGPU::createTrackingFrameInfoDeviceArray() +{ + { + GPUTimer timer("creating trackingframeinfo array"); + allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), NLayers * sizeof(TrackingFrameInfo*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mTrackingFrameInfoDevice.data(), NLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); + mPinnedTrackingFrameInfo.set(NLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mTrackingFrameInfo[iLayer].data(), this->mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), cudaHostRegisterPortable)); + mPinnedTrackingFrameInfo.set(iLayer); + } + } + } +} - return rofsize * nrof; +template +void TimeFrameGPU::loadTrackingFrameInfoDevice(const int layer) +{ + { + GPUTimer timer(mGpuStreams[layer], "loading trackingframeinfo", layer); + GPULog("gpu-transfer: loading {} tfinfo on layer {}, for {:.2f} MB.", this->mTrackingFrameInfo[layer].size(), layer, this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo) / constants::MB); + allocMemAsync(reinterpret_cast(&mTrackingFrameInfoDevice[layer]), this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mTrackingFrameInfoDevice[layer], this->mTrackingFrameInfo[layer].data(), this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mTrackingFrameInfoDeviceArray[layer], &mTrackingFrameInfoDevice[layer], sizeof(TrackingFrameInfo*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } } -template -size_t GpuTimeFrameChunk::computeFixedSizeBytes(const TimeFrameGPUParameters& config) +template +void TimeFrameGPU::loadROFCutMask(const int iteration) { - size_t total = config.tmpCUBBufferSize; // CUB tmp buffers - total += sizeof(gpu::StaticTrackingParameters); // static parameters loaded once - return total; + { + GPUTimer timer("loading multiplicity cut mask"); + const auto& hostTable = *(this->mROFMask); + const auto hostView = hostTable.getView(); + using TableEntry = ROFMaskTable::TableEntry; + using TableIndex = ROFMaskTable::TableIndex; + TableEntry* d_flatTable{nullptr}; + TableIndex* d_indices{nullptr}; + GPULog("gpu-transfer: iteration {} loading multiplicity cut mask with {} elements, for {:.2f} MB.", + iteration, hostTable.getFlatMaskSize(), hostTable.getFlatMaskSize() * sizeof(TableEntry) / constants::MB); + allocMem(reinterpret_cast(&d_flatTable), hostTable.getFlatMaskSize() * sizeof(TableEntry), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&d_indices), NLayers * sizeof(uint32_t), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_indices, hostView.mLayerROFOffsets, NLayers * sizeof(TableIndex), cudaMemcpyHostToDevice)); + // Re-copy the flat mask on every qualifying iteration (e.g. after swapMasks() for UPC) + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatMask, hostTable.getFlatMaskSize() * sizeof(TableEntry), cudaMemcpyHostToDevice)); + mDeviceROFMaskTableView = hostTable.getDeviceView(d_flatTable, d_indices); + } } -template -size_t GpuTimeFrameChunk::computeRofPerChunk(const TimeFrameGPUParameters& config, const size_t m) +template +void TimeFrameGPU::loadVertices() { - return (m * GB / (float)(config.nTimeFrameChunks) - GpuTimeFrameChunk::computeFixedSizeBytes(config)) / (float)GpuTimeFrameChunk::computeScalingSizeBytes(1, config); + { + GPUTimer timer("loading seeding vertices"); + GPULog("gpu-transfer: loading {} seeding vertices, for {:.2f} MB.", this->mPrimaryVertices.size(), this->mPrimaryVertices.size() * sizeof(Vertex) / constants::MB); + allocMem(reinterpret_cast(&mPrimaryVerticesDevice), this->mPrimaryVertices.size() * sizeof(Vertex), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mPrimaryVerticesDevice, this->mPrimaryVertices.data(), this->mPrimaryVertices.size() * sizeof(Vertex), cudaMemcpyHostToDevice)); + } } -/// Interface -template -Cluster* GpuTimeFrameChunk::getDeviceClusters(const int layer) +template +void TimeFrameGPU::loadROFOverlapTable() { - return mClustersDevice[layer]; + { + GPUTimer timer("initialising device view of ROFOverlapTable"); + const auto& hostTable = this->getROFOverlapTable(); + const auto& hostView = this->getROFOverlapTableView(); + using TableEntry = ROFOverlapTable::TableEntry; + using TableIndex = ROFOverlapTable::TableIndex; + using LayerTiming = o2::its::LayerTiming; + TableEntry* d_flatTable{nullptr}; + TableIndex* d_indices{nullptr}; + LayerTiming* d_layers{nullptr}; + size_t flatTableSize = hostTable.getFlatTableSize(); + allocMem(reinterpret_cast(&d_flatTable), flatTableSize * sizeof(TableEntry), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatTable, flatTableSize * sizeof(TableEntry), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_indices), hostTable.getIndicesSize() * sizeof(TableIndex), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_indices, hostView.mIndices, hostTable.getIndicesSize() * sizeof(TableIndex), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_layers), NLayers * sizeof(LayerTiming), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_layers, hostView.mLayers, NLayers * sizeof(LayerTiming), cudaMemcpyHostToDevice)); + mDeviceROFOverlapTableView = hostTable.getDeviceView(d_flatTable, d_indices, d_layers); + } } -template -TrackingFrameInfo* GpuTimeFrameChunk::getDeviceTrackingFrameInfo(const int layer) +template +void TimeFrameGPU::loadROFVertexLookupTable() { - return mTrackingFrameInfoDevice[layer]; + { + GPUTimer timer("initialising device view of ROFVertexLookupTable"); + const auto& hostTable = this->getROFVertexLookupTable(); + const auto& hostView = this->getROFVertexLookupTableView(); + using TableEntry = ROFVertexLookupTable::TableEntry; + using TableIndex = ROFVertexLookupTable::TableIndex; + using LayerTiming = o2::its::LayerTiming; + TableEntry* d_flatTable{nullptr}; + TableIndex* d_indices{nullptr}; + LayerTiming* d_layers{nullptr}; + size_t flatTableSize = hostTable.getFlatTableSize(); + allocMem(reinterpret_cast(&d_flatTable), flatTableSize * sizeof(TableEntry), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatTable, flatTableSize * sizeof(TableEntry), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_indices), hostTable.getIndicesSize() * sizeof(TableIndex), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_indices, hostView.mIndices, hostTable.getIndicesSize() * sizeof(TableIndex), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_layers), NLayers * sizeof(LayerTiming), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_layers, hostView.mLayers, NLayers * sizeof(LayerTiming), cudaMemcpyHostToDevice)); + mDeviceROFVertexLookupTableView = hostTable.getDeviceView(d_flatTable, d_indices, d_layers); + } } -template -int* GpuTimeFrameChunk::getDeviceClusterExternalIndices(const int layer) +template +void TimeFrameGPU::loadTrackingTopologies() { - return mClusterExternalIndicesDevice[layer]; + GPUTimer timer("initialising device views of TrackingTopology"); + const auto& hostTopologies = this->getTrackerTopologies(); + mDeviceTrackerTopologyViews.resize(hostTopologies.size()); + using LayerLink = typename TrackingTopologyN::LayerLink; + using CellTopology = typename TrackingTopologyN::CellTopology; + using Range = typename TrackingTopologyN::Range; + using Id = typename TrackingTopologyN::Id; + for (size_t iteration = 0; iteration < hostTopologies.size(); ++iteration) { + const auto& topology = hostTopologies[iteration]; + LayerLink* dLinks{nullptr}; + CellTopology* dCells{nullptr}; + Range* dCellsByFirstLinkIndex{nullptr}; + Id* dCellsByFirstLink{nullptr}; + allocMem(reinterpret_cast(&dLinks), topology.getNLinks() * sizeof(LayerLink), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&dCells), topology.getNCells() * sizeof(CellTopology), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&dCellsByFirstLinkIndex), topology.getNLinks() * sizeof(Range), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&dCellsByFirstLink), topology.getNCellsByFirstLink() * sizeof(Id), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(dLinks, topology.getLinks().data(), topology.getNLinks() * sizeof(LayerLink), cudaMemcpyHostToDevice)); + GPUChkErrS(cudaMemcpy(dCells, topology.getCells().data(), topology.getNCells() * sizeof(CellTopology), cudaMemcpyHostToDevice)); + GPUChkErrS(cudaMemcpy(dCellsByFirstLinkIndex, topology.getCellsByFirstLinkIndex().data(), topology.getNLinks() * sizeof(Range), cudaMemcpyHostToDevice)); + GPUChkErrS(cudaMemcpy(dCellsByFirstLink, topology.getCellsByFirstLink().data(), topology.getNCellsByFirstLink() * sizeof(Id), cudaMemcpyHostToDevice)); + mDeviceTrackerTopologyViews[iteration] = topology.getDeviceView(dLinks, dCells, dCellsByFirstLinkIndex, dCellsByFirstLink); + } + if (!mDeviceTrackerTopologyViews.empty()) { + mDeviceTrackingTopologyView = mDeviceTrackerTopologyViews.front(); + } } -template -int* GpuTimeFrameChunk::getDeviceIndexTables(const int layer) +template +void TimeFrameGPU::updateROFVertexLookupTable() { - return mIndexTablesDevice[layer]; + const auto& hostTable = this->getROFVertexLookupTable(); + { + GPUTimer timer("updating device view of ROFVertexLookupTable"); + const auto& hostView = this->getROFVertexLookupTableView(); + using TableEntry = ROFVertexLookupTable::TableEntry; + TableEntry* d_flatTable{nullptr}; + size_t flatTableSize = hostTable.getFlatTableSize(); + allocMem(reinterpret_cast(&d_flatTable), flatTableSize * sizeof(TableEntry), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatTable, flatTableSize * sizeof(TableEntry), cudaMemcpyHostToDevice)); + mDeviceROFVertexLookupTableView = hostTable.getDeviceView(d_flatTable, hostView.mIndices, hostView.mLayers); + } } -template -Tracklet* GpuTimeFrameChunk::getDeviceTracklets(const int layer) +template +void TimeFrameGPU::createTrackletsLUTDeviceArray() { - return mTrackletsDevice[layer]; + { + allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), MaxLinks * sizeof(int*), this->hasFrameworkAllocator()); + } } -template -int* GpuTimeFrameChunk::getDeviceTrackletsLookupTables(const int layer) +template +void TimeFrameGPU::createTrackletsLUTDevice(bool allocate, const int layer) { - return mTrackletsLookupTablesDevice[layer]; + GPUTimer timer(mGpuStreams[layer], "creating tracklets LUTs", layer); + const int fromLayer = this->mTrackingTopologyView.getLink(layer).fromLayer; + const int ncls = this->mClusters[fromLayer].size() + 1; + if (allocate || mTrackletsLUTDevice[layer] == nullptr) { + GPULog("gpu-allocation: creating tracklets LUT for {} elements on layer {}, for {:.2f} MB.", ncls, layer, ncls * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[layer]), ncls * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(&mTrackletsLUTDeviceArray[layer], &mTrackletsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } + GPUChkErrS(cudaMemsetAsync(mTrackletsLUTDevice[layer], 0, ncls * sizeof(int), mGpuStreams[layer].get())); } -template -Cell* GpuTimeFrameChunk::getDeviceCells(const int layer) +template +void TimeFrameGPU::createTrackletsBuffersArray() { - return mCellsDevice[layer]; + { + GPUTimer timer("creating tracklet buffers array"); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), MaxLinks * sizeof(Tracklet*), this->hasFrameworkAllocator()); + } } -template -int* GpuTimeFrameChunk::getDeviceCellsLookupTables(const int layer) +template +void TimeFrameGPU::createTrackletsBuffers(const int layer) { - return mCellsLookupTablesDevice[layer]; + GPUTimer timer(mGpuStreams[layer], "creating tracklet buffers", layer); + mNTracklets[layer] = 0; + const int fromLayer = this->mTrackingTopologyView.getLink(layer).fromLayer; + GPUChkErrS(cudaMemcpyAsync(&mNTracklets[layer], mTrackletsLUTDevice[layer] + this->mClusters[fromLayer].size(), sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + mGpuStreams[layer].sync(); // ensure number of tracklets is correct + GPULog("gpu-transfer: creating tracklets buffer for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer], layer, mNTracklets[layer] * sizeof(Tracklet) / constants::MB); + allocMemAsync(reinterpret_cast(&mTrackletsDevice[layer]), mNTracklets[layer] * sizeof(Tracklet), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mTrackletsDevice[layer], 0, mNTracklets[layer] * sizeof(Tracklet), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mTrackletsDeviceArray[layer], &mTrackletsDevice[layer], sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } -template -int* GpuTimeFrameChunk::getDeviceCellNeigboursLookupTables(const int layer) +template +void TimeFrameGPU::loadTrackletsDevice() { - return mNeighboursCellLookupTablesDevice[layer]; + GPUTimer timer(mGpuStreams, "loading tracklets", NLayers - 1); + for (auto iLayer{0}; iLayer < NLayers - 1; ++iLayer) { + GPULog("gpu-transfer: loading {} tracklets on layer {}, for {:.2f} MB.", this->mTracklets[iLayer].size(), iLayer, this->mTracklets[iLayer].size() * sizeof(Tracklet) / constants::MB); + GPUChkErrS(cudaHostRegister(this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaHostRegisterPortable)); + GPUChkErrS(cudaMemcpyAsync(mTrackletsDevice[iLayer], this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); + } } -template -int* GpuTimeFrameChunk::getDeviceCellNeighbours(const int layer) +template +void TimeFrameGPU::loadTrackletsLUTDevice() { - return mNeighboursCellDevice[layer]; + GPUTimer timer("loading tracklets"); + for (auto iLayer{0}; iLayer < NLayers - 2; ++iLayer) { + GPULog("gpu-transfer: loading tracklets LUT for {} elements on layer {}, for {:.2f} MB", this->mTrackletsLookupTable[iLayer].size(), iLayer + 1, this->mTrackletsLookupTable[iLayer].size() * sizeof(int) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(mTrackletsLUTDevice[iLayer + 1], this->mTrackletsLookupTable[iLayer].data(), this->mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); + } + mGpuStreams.sync(); + GPUChkErrS(cudaMemcpy(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (NLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); } -// Load data -template -size_t GpuTimeFrameChunk::loadDataOnDevice(const size_t startRof, const size_t maxRof, const int maxLayers, Stream& stream) +template +void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) { - RANGE("load_clusters_data", 5); - auto nRofs = std::min(maxRof - startRof, mNRof); - mNPopulatedRof = mTimeFramePtr->getNClustersROFrange(startRof, nRofs, 0).size(); - for (int i = 0; i < maxLayers; ++i) { - mHostClusters[i] = mTimeFramePtr->getClustersPerROFrange(startRof, nRofs, i); - mHostIndexTables[i] = mTimeFramePtr->getIndexTablePerROFrange(startRof, nRofs, i); - if (mHostClusters[i].size() > mTFGPUParams->clustersPerROfCapacity * nRofs) { - LOGP(warning, "Clusters on layer {} exceed the expected value, resizing to config value: {}, will lose information!", i, mTFGPUParams->clustersPerROfCapacity * nRofs); - } - checkGPUError(cudaMemcpyAsync(mClustersDevice[i], - mHostClusters[i].data(), - (int)std::min(mHostClusters[i].size(), mTFGPUParams->clustersPerROfCapacity * nRofs) * sizeof(Cluster), - cudaMemcpyHostToDevice, stream.get())); - if (mHostIndexTables[i].data()) { - checkGPUError(cudaMemcpyAsync(mIndexTablesDevice[i], - mHostIndexTables[i].data(), - mHostIndexTables[i].size() * sizeof(int), - cudaMemcpyHostToDevice, stream.get())); - } + GPUTimer timer(mGpuStreams[layer], "creating cells neighbours", layer); + GPULog("gpu-transfer: reserving neighbours LUT for {} elements on layer {}, for {:.2f} MB.", mNCells[layer] + 1, layer, (mNCells[layer] + 1) * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[layer]), (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[layer], 0, (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer].get())); +} + +template +void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const unsigned int nCells) +{ + GPUTimer timer(mGpuStreams[layer], "reserving neighboursLUT"); + GPULog("gpu-allocation: reserving neighbours LUT for {} elements on layer {} , for {:.2f} MB.", nCells + 1, layer, (nCells + 1) * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mNeighboursLUTDevice[layer]), (nCells + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); // We need one element more to move exc -> inc + GPUChkErrS(cudaMemsetAsync(mNeighboursLUTDevice[layer], 0, (nCells + 1) * sizeof(int), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mNeighboursCellLUTDeviceArray[layer], &mNeighboursLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); +} + +template +void TimeFrameGPU::loadCellsDevice() +{ + GPUTimer timer(mGpuStreams, "loading cell seeds", NLayers - 2); + for (auto iLayer{0}; iLayer < NLayers - 2; ++iLayer) { + GPULog("gpu-transfer: loading {} cell seeds on layer {}, for {:.2f} MB.", this->mCells[iLayer].size(), iLayer, this->mCells[iLayer].size() * sizeof(CellSeed) / constants::MB); + allocMemAsync(reinterpret_cast(&mCellsDevice[iLayer]), this->mCells[iLayer].size() * sizeof(CellSeed), mGpuStreams[iLayer], this->hasFrameworkAllocator()); + allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[iLayer]), (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[iLayer], this->hasFrameworkAllocator()); // accessory for the neigh. finding. + GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[iLayer], 0, (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[iLayer].get())); + GPUChkErrS(cudaMemcpyAsync(mCellsDevice[iLayer], this->mCells[iLayer].data(), this->mCells[iLayer].size() * sizeof(CellSeed), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } - return mNPopulatedRof; // return the number of ROFs we loaded the data for. } -///////////////////////////////////////////////////////////////////////////////////////// -// TimeFrameGPU -///////////////////////////////////////////////////////////////////////////////////////// -template -TimeFrameGPU::TimeFrameGPU() +template +void TimeFrameGPU::createCellsLUTDeviceArray() { - mIsGPU = true; - utils::getDeviceProp(0, true); + { + GPUTimer timer("creating cells LUTs array"); + allocMem(reinterpret_cast(&mCellsLUTDeviceArray), MaxCells * sizeof(int*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mNeighboursCellLUTDeviceArray), MaxCells * sizeof(int*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemset(mNeighboursCellLUTDeviceArray, 0, MaxCells * sizeof(int*))); + } } -template -TimeFrameGPU::~TimeFrameGPU() = default; +template +void TimeFrameGPU::createCellsLUTDevice(const int layer) +{ + GPUTimer timer(mGpuStreams[layer], "creating cells LUTs", layer); + const int firstLink = this->mTrackingTopologyView.getCell(layer).firstLink; + GPULog("gpu-transfer: creating cell LUT for {} elements on layer {}, for {:.2f} MB.", mNTracklets[firstLink] + 1, layer, (mNTracklets[firstLink] + 1) * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mCellsLUTDevice[layer]), (mNTracklets[firstLink] + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mCellsLUTDevice[layer], 0, (mNTracklets[firstLink] + 1) * sizeof(int), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mCellsLUTDeviceArray[layer], &mCellsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); +} -template -void TimeFrameGPU::registerHostMemory(const int maxLayers) +template +void TimeFrameGPU::createCellsBuffersArray() { - if (mHostRegistered) { + { + GPUTimer timer("creating cells buffers array"); + allocMem(reinterpret_cast(&mCellsDeviceArray), MaxCells * sizeof(CellSeed*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mNeighboursDeviceArray), MaxCells * sizeof(CellNeighbour*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemset(mNeighboursDeviceArray, 0, MaxCells * sizeof(CellNeighbour*))); + GPUChkErrS(cudaMemcpy(mCellsDeviceArray, mCellsDevice.data(), mCellsDevice.size() * sizeof(CellSeed*), cudaMemcpyHostToDevice)); + } +} + +template +void TimeFrameGPU::createCellsBuffers(const int layer) +{ + GPUTimer timer(mGpuStreams[layer], "creating cells buffers"); + mNCells[layer] = 0; + const int firstLink = this->mTrackingTopologyView.getCell(layer).firstLink; + GPUChkErrS(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mNTracklets[firstLink], sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + mGpuStreams[layer].sync(); // ensure number of cells is correct + GPULog("gpu-transfer: creating cell buffer for {} elements on layer {}, for {:.2f} MB.", mNCells[layer], layer, mNCells[layer] * sizeof(CellSeed) / constants::MB); + allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeed), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mCellsDevice[layer], 0, mNCells[layer] * sizeof(CellSeed), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mCellsDeviceArray[layer], &mCellsDevice[layer], sizeof(CellSeed*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); +} + +template +void TimeFrameGPU::loadCellsLUTDevice() +{ + GPUTimer timer(mGpuStreams, "loading cells LUTs", NLayers - 3); + for (auto iLayer{0}; iLayer < NLayers - 3; ++iLayer) { + GPULog("gpu-transfer: loading cell LUT for {} elements on layer {}, for {:.2f} MB.", this->mCellsLookupTable[iLayer].size(), iLayer, this->mCellsLookupTable[iLayer].size() * sizeof(int) / constants::MB); + GPUChkErrS(cudaHostRegister(this->mCellsLookupTable[iLayer].data(), this->mCellsLookupTable[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); + GPUChkErrS(cudaMemcpyAsync(mCellsLUTDevice[iLayer + 1], this->mCellsLookupTable[iLayer].data(), this->mCellsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); + } +} + +template +void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seeds) +{ + GPUTimer timer("loading track seeds"); + GPULog("gpu-transfer: loading {} track seeds, for {:.2f} MB.", seeds.size(), seeds.size() * sizeof(TrackSeedN) / constants::MB); + allocMem(reinterpret_cast(&mTrackSeedsDevice), seeds.size() * sizeof(TrackSeedN), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemcpy(mTrackSeedsDevice, seeds.data(), seeds.size() * sizeof(TrackSeedN), cudaMemcpyHostToDevice)); + GPULog("gpu-transfer: creating {} track seeds LUT, for {:.2f} MB.", seeds.size() + 1, (seeds.size() + 1) * sizeof(int) / constants::MB); + allocMem(reinterpret_cast(&mTrackSeedsLUTDevice), (seeds.size() + 1) * sizeof(int), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemset(mTrackSeedsLUTDevice, 0, (seeds.size() + 1) * sizeof(int))); +} + +template +void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) +{ + GPUTimer timer(mGpuStreams[layer], "reserving neighbours", layer); + this->mNNeighbours[layer] = 0; + if (this->mNCells[layer] == 0) { + mNeighboursDevice[layer] = nullptr; + GPUChkErrS(cudaMemcpyAsync(&mNeighboursDeviceArray[layer], &mNeighboursDevice[layer], sizeof(CellNeighbour*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); return; - } else { - mHostRegistered = true; } - for (auto iLayer{0}; iLayer < maxLayers; ++iLayer) { - checkGPUError(cudaHostRegister(mClusters[iLayer].data(), mClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); - checkGPUError(cudaHostRegister(mNClustersPerROF[iLayer].data(), mNClustersPerROF[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); - checkGPUError(cudaHostRegister(mIndexTables[iLayer].data(), (mStaticTrackingParams.ZBins * mStaticTrackingParams.PhiBins + 1) * mNrof * sizeof(int), cudaHostRegisterPortable)); + GPUChkErrS(cudaMemcpyAsync(&(this->mNNeighbours[layer]), &(mNeighboursLUTDevice[layer][this->mNCells[layer]]), sizeof(unsigned int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + mGpuStreams[layer].sync(); // ensure number of neighbours is correct + if (this->mNNeighbours[layer] == 0) { + mNeighboursDevice[layer] = nullptr; + GPUChkErrS(cudaMemcpyAsync(&mNeighboursDeviceArray[layer], &mNeighboursDevice[layer], sizeof(CellNeighbour*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + return; } - checkGPUError(cudaHostRegister(mHostNTracklets.data(), (nLayers - 1) * mGpuParams.nTimeFrameChunks * sizeof(int), cudaHostRegisterPortable)); - checkGPUError(cudaHostRegister(mHostNCells.data(), (nLayers - 2) * mGpuParams.nTimeFrameChunks * sizeof(int), cudaHostRegisterPortable)); + GPULog("gpu-allocation: reserving {} neighbours, for {:.2f} MB.", this->mNNeighbours[layer], (this->mNNeighbours[layer]) * sizeof(CellNeighbour) / constants::MB); + allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), (this->mNNeighbours[layer]) * sizeof(CellNeighbour), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mNeighboursDevice[layer], -1, (this->mNNeighbours[layer]) * sizeof(CellNeighbour), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mNeighboursDeviceArray[layer], &mNeighboursDevice[layer], sizeof(CellNeighbour*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); +} + +template +void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) +{ + GPUTimer timer("reserving tracks"); + mNTracks = 0; + GPUChkErrS(cudaMemcpy(&mNTracks, mTrackSeedsLUTDevice + nSeeds, sizeof(int), cudaMemcpyDeviceToHost)); + GPULog("gpu-allocation: reserving {} tracks, for {:.2f} MB.", mNTracks, mNTracks * sizeof(o2::its::TrackITSExt) / constants::MB); + mTrackITSExt = bounded_vector(mNTracks, {}, this->getMemoryPool().get()); + mTrackIndices = bounded_vector(mNTracks, 0, this->getMemoryPool().get()); + allocMem(reinterpret_cast(&mTrackITSExtDevice), mNTracks * sizeof(o2::its::TrackITSExt), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemset(mTrackITSExtDevice, 0, mNTracks * sizeof(o2::its::TrackITSExt))); + GPULog("gpu-allocation: reserving {} track indices, for {:.2f} MB.", mNTracks, mNTracks * sizeof(int) / constants::MB); + allocMem(reinterpret_cast(&mTrackIndicesDevice), mNTracks * sizeof(int), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); } -template -void TimeFrameGPU::unregisterHostMemory(const int maxLayers) +template +void TimeFrameGPU::createTrackExtensionScratchDevice(const int nThreads, const int maxHypotheses) { - if (!mHostRegistered) { + GPUTimer timer("reserving track extension scratch"); + const size_t nHypotheses = static_cast(std::max(1, nThreads)) * std::max(1, maxHypotheses); + GPULog("gpu-allocation: reserving {} track extension hypotheses per scratch buffer, for {:.2f} MB each.", nHypotheses, nHypotheses * sizeof(o2::its::TrackExtensionHypothesis) / constants::MB); + mActiveTrackExtensionHypothesesDevice = nullptr; + mNextTrackExtensionHypothesesDevice = nullptr; + if (nHypotheses == 0) { return; } - for (auto iLayer{0}; iLayer < maxLayers; ++iLayer) { - checkGPUError(cudaHostUnregister(mClusters[iLayer].data())); - checkGPUError(cudaHostUnregister(mNClustersPerROF[iLayer].data())); - checkGPUError(cudaHostUnregister(mIndexTables[iLayer].data())); + allocMem(reinterpret_cast(&mActiveTrackExtensionHypothesesDevice), nHypotheses * sizeof(o2::its::TrackExtensionHypothesis), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + allocMem(reinterpret_cast(&mNextTrackExtensionHypothesesDevice), nHypotheses * sizeof(o2::its::TrackExtensionHypothesis), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); +} + +template +void TimeFrameGPU::downloadCellsDevice() +{ + GPUTimer timer(mGpuStreams, "downloading cells", NLayers - 2); + for (int iLayer{0}; iLayer < NLayers - 2; ++iLayer) { + GPULog("gpu-transfer: downloading {} cells on layer: {}, for {:.2f} MB.", mNCells[iLayer], iLayer, mNCells[iLayer] * sizeof(CellSeed) / constants::MB); + this->mCells[iLayer].resize(mNCells[iLayer]); + GPUChkErrS(cudaMemcpyAsync(this->mCells[iLayer].data(), this->mCellsDevice[iLayer], mNCells[iLayer] * sizeof(CellSeed), cudaMemcpyDeviceToHost, mGpuStreams[iLayer].get())); } - checkGPUError(cudaHostUnregister(mHostNTracklets.data())); - checkGPUError(cudaHostUnregister(mHostNCells.data())); - mHostRegistered = false; } -template -void TimeFrameGPU::initialise(const int iteration, - const TrackingParameters& trkParam, - const int maxLayers, - IndexTableUtils* utils, - const TimeFrameGPUParameters* gpuParam) +template +void TimeFrameGPU::downloadCellsLUTDevice() { - mGpuStreams.resize(mGpuParams.nTimeFrameChunks); - mHostNTracklets.resize((nLayers - 1) * mGpuParams.nTimeFrameChunks, 0); - mHostNCells.resize((nLayers - 2) * mGpuParams.nTimeFrameChunks, 0); + GPUTimer timer(mGpuStreams, "downloading cell luts", NLayers - 3); + for (auto iLayer{0}; iLayer < NLayers - 3; ++iLayer) { + GPULog("gpu-transfer: downloading cells lut on layer {} for {} elements", iLayer, (mNTracklets[iLayer + 1] + 1)); + this->mCellsLookupTable[iLayer].resize(mNTracklets[iLayer + 1] + 1); + GPUChkErrS(cudaMemcpyAsync(this->mCellsLookupTable[iLayer].data(), mCellsLUTDevice[iLayer + 1], (mNTracklets[iLayer + 1] + 1) * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[iLayer].get())); + } +} - auto init = [&](int p) -> void { - this->initDevice(p, utils, trkParam, *gpuParam, maxLayers, iteration); - }; - std::thread t1{init, mGpuParams.nTimeFrameChunks}; - RANGE("tf_cpu_initialisation", 1); - o2::its::TimeFrame::initialise(iteration, trkParam, maxLayers); - registerHostMemory(maxLayers); - t1.join(); -} - -template -void TimeFrameGPU::wipe(const int maxLayers) -{ - unregisterHostMemory(maxLayers); -} - -template -void TimeFrameGPU::initDevice(const int chunks, - IndexTableUtils* utils, - const TrackingParameters& trkParam, - const TimeFrameGPUParameters& gpuParam, - const int maxLayers, - const int iteration) -{ - mStaticTrackingParams.ZBins = trkParam.ZBins; - mStaticTrackingParams.PhiBins = trkParam.PhiBins; - if (mFirstInit) { - mGpuParams = gpuParam; - if (mGpuParams.maxGPUMemoryGB < 0) { - // Adaptive to available memory, hungry mode - size_t free; - checkGPUError(cudaMemGetInfo(&free, nullptr)); - mAvailMemGB = (double)free / GB; - LOGP(info, "Hungry memory mode requested, found {} free GB, going to use all of them", mAvailMemGB); - } else { - mAvailMemGB = mGpuParams.maxGPUMemoryGB; - LOGP(info, "Fixed memory mode requested, will try to use {} GB", mAvailMemGB); - } - checkGPUError(cudaMalloc(reinterpret_cast(&mTrackingParamsDevice), sizeof(gpu::StaticTrackingParameters))); - checkGPUError(cudaMemcpy(mTrackingParamsDevice, &mStaticTrackingParams, sizeof(gpu::StaticTrackingParameters), cudaMemcpyHostToDevice)); - if (utils) { // If utils is not nullptr, then its gpu vertexing - mIndexTableUtils = *utils; - checkGPUError(cudaMalloc(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtils))); - } else { // GPU tracking otherwise - mIndexTableUtils.setTrackingParameters(trkParam); - } +template +void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector>& neighbours, const int layer) +{ + GPUTimer timer(mGpuStreams[layer], "downloading neighbours from layer", layer); + GPULog("gpu-transfer: downloading {} neighbours, for {:.2f} MB.", neighbours[layer].size(), neighbours[layer].size() * sizeof(CellNeighbour) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(neighbours[layer].data(), mNeighboursDevice[layer], neighbours[layer].size() * sizeof(CellNeighbour), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); +} - mMemChunks.resize(chunks, GpuTimeFrameChunk{static_cast(this), mGpuParams}); - mVerticesInChunks.resize(chunks); - mNVerticesInChunks.resize(chunks); - mLabelsInChunks.resize(chunks); - LOGP(debug, "Size of fixed part is: {} MB", GpuTimeFrameChunk::computeFixedSizeBytes(mGpuParams) / MB); - LOGP(debug, "Size of scaling part is: {} MB", GpuTimeFrameChunk::computeScalingSizeBytes(GpuTimeFrameChunk::computeRofPerChunk(mGpuParams, mAvailMemGB), mGpuParams) / MB); - LOGP(info, "Allocating {} chunks of {} rofs capacity each.", chunks, GpuTimeFrameChunk::computeRofPerChunk(mGpuParams, mAvailMemGB)); +template +void TimeFrameGPU::downloadNeighboursLUTDevice(bounded_vector& lut, const int layer) +{ + GPUTimer timer(mGpuStreams[layer], "downloading neighbours LUT from layer", layer); + GPULog("gpu-transfer: downloading neighbours LUT for {} elements on layer {}, for {:.2f} MB.", lut.size(), layer, lut.size() * sizeof(int) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(lut.data(), mNeighboursLUTDevice[layer], lut.size() * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); +} - for (int iChunk{0}; iChunk < mMemChunks.size(); ++iChunk) { - mMemChunks[iChunk].allocate(GpuTimeFrameChunk::computeRofPerChunk(mGpuParams, mAvailMemGB), mGpuStreams[iChunk]); +template +void TimeFrameGPU::downloadTrackITSExtDevice() +{ + GPUTimer timer("downloading tracks"); + GPULog("gpu-transfer: downloading {} tracks, for {:.2f} MB.", mTrackITSExt.size(), mTrackITSExt.size() * sizeof(o2::its::TrackITSExt) / constants::MB); + GPUChkErrS(cudaMemcpy(mTrackITSExt.data(), mTrackITSExtDevice, mTrackITSExt.size() * sizeof(o2::its::TrackITSExt), cudaMemcpyDeviceToHost)); +} + +template +void TimeFrameGPU::downloadTrackIndicesDevice() +{ + GPUTimer timer("downloading track indices"); + GPULog("gpu-transfer: downloading {} track indices, for {:.2f} MB.", mTrackIndices.size(), mTrackIndices.size() * sizeof(int) / constants::MB); + GPUChkErrS(cudaMemcpy(mTrackIndices.data(), mTrackIndicesDevice, mTrackIndices.size() * sizeof(int), cudaMemcpyDeviceToHost)); +} + +template +void TimeFrameGPU::unregisterHostMemory(const int maxLayers) +{ + GPUTimer timer("unregistering host memory"); + GPULog("unregistering host memory"); + + auto checkedUnregisterEntry = [](auto& bits, auto& vec, int layer) { + if (bits.test(layer)) { + GPUChkErrS(cudaHostUnregister(vec[layer].data())); + bits.reset(layer); } - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - checkGPUError(cudaMalloc(reinterpret_cast(&mROframesClustersDevice[iLayer]), mROframesClusters[iLayer].size() * sizeof(int))); - checkGPUError(cudaMalloc(reinterpret_cast(&(mUsedClustersDevice[iLayer])), sizeof(unsigned char) * mGpuParams.clustersPerROfCapacity * mNrof)); + }; + auto checkedUnregisterArray = [](auto& bits, auto& vec) { + if (bits.test(NLayers)) { + GPUChkErrS(cudaHostUnregister(vec.data())); + bits.reset(NLayers); } - checkGPUError(cudaMalloc(reinterpret_cast(&mVerticesDevice), sizeof(Vertex) * mGpuParams.maxVerticesCapacity)); - checkGPUError(cudaMalloc(reinterpret_cast(&mROframesPVDevice), sizeof(int) * (mNrof + 1))); + }; - mFirstInit = false; + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { + checkedUnregisterEntry(mPinnedUsedClusters, this->mUsedClusters, iLayer); + checkedUnregisterEntry(mPinnedUnsortedClusters, this->mUnsortedClusters, iLayer); + checkedUnregisterEntry(mPinnedClusters, this->mClusters, iLayer); + checkedUnregisterEntry(mPinnedClustersIndexTables, this->mIndexTables, iLayer); + checkedUnregisterEntry(mPinnedTrackingFrameInfo, this->mTrackingFrameInfo, iLayer); + checkedUnregisterEntry(mPinnedROFramesClusters, this->mROFramesClusters, iLayer); } - if (maxLayers < nLayers) { // Vertexer - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - checkGPUError(cudaMemcpy(mROframesClustersDevice[iLayer], mROframesClusters[iLayer].data(), mROframesClusters[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice)); - } - } else { // Tracker - checkGPUError(cudaMemcpy(mVerticesDevice, mPrimaryVertices.data(), sizeof(Vertex) * mPrimaryVertices.size(), cudaMemcpyHostToDevice)); - checkGPUError(cudaMemcpy(mROframesPVDevice, mROframesPV.data(), sizeof(int) * mROframesPV.size(), cudaMemcpyHostToDevice)); - if (!iteration) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - checkGPUError(cudaMemset(mUsedClustersDevice[iLayer], 0, sizeof(unsigned char) * mGpuParams.clustersPerROfCapacity * mNrof)); - } - } + checkedUnregisterArray(mPinnedUsedClusters, mUsedClustersDevice); + checkedUnregisterArray(mPinnedUnsortedClusters, mUnsortedClustersDevice); + checkedUnregisterArray(mPinnedClusters, mClustersDevice); + checkedUnregisterArray(mPinnedClustersIndexTables, mClustersIndexTablesDevice); + checkedUnregisterArray(mPinnedTrackingFrameInfo, mTrackingFrameInfoDevice); + checkedUnregisterArray(mPinnedROFramesClusters, mROFramesClustersDevice); +} + +namespace detail +{ +template +constexpr uint64_t makeIterTag() +{ + static_assert(I < 10); + constexpr char tag[] = {'I', 'T', 'S', 'I', 'T', 'E', 'R', char('0' + I), '\0'}; + return qStr2Tag(tag); +} +template +constexpr auto makeIterTags(std::index_sequence) +{ + return std::array{makeIterTag()...}; +} +constexpr auto kIterTags = makeIterTags(std::make_index_sequence{}); +} // namespace detail + +template +void TimeFrameGPU::pushMemoryStack(const int iteration) +{ + // mark the beginning of memory marked with MEMORY_STACK that can be discarded + // after doing one iteration + (this->mExternalAllocator)->pushTagOnStack(detail::kIterTags[iteration]); +} + +template +void TimeFrameGPU::popMemoryStack(const int iteration) +{ + // pop all memory on the stack from this iteration + (this->mExternalAllocator)->popTagOffStack(detail::kIterTags[iteration]); +} + +template +void TimeFrameGPU::initialise(const TrackingParameters& trkParam, int maxLayers) +{ + mGpuStreams.resize(MaxStreams); + o2::its::TimeFrame::initialise(trkParam, maxLayers); +} + +template +void TimeFrameGPU::initialise(const TrackingParameters& trkParam, int maxLayers, int iteration) +{ + mGpuStreams.resize(MaxStreams); + o2::its::TimeFrame::initialise(trkParam, maxLayers, iteration); + if (iteration != constants::UnusedIndex && iteration < static_cast(mDeviceTrackerTopologyViews.size())) { + mDeviceTrackingTopologyView = mDeviceTrackerTopologyViews[iteration]; } - checkGPUError(cudaMemcpy(mIndexTableUtilsDevice, &mIndexTableUtils, sizeof(IndexTableUtils), cudaMemcpyHostToDevice)); } -template -unsigned char* TimeFrameGPU::getDeviceUsedClusters(const int layer) +template +void TimeFrameGPU::syncStream(const size_t stream) +{ + mGpuStreams[stream].sync(); +} + +template +void TimeFrameGPU::syncStreams(const bool device) +{ + mGpuStreams.sync(device); +} + +template +void TimeFrameGPU::waitEvent(const int stream, const int event) { - return mUsedClustersDevice[layer]; + mGpuStreams.waitEvent(stream, event); } -template -gsl::span TimeFrameGPU::getHostNTracklets(const int chunkId) +template +void TimeFrameGPU::recordEvent(const int event) { - return gsl::span(mHostNTracklets.data() + (nLayers - 1) * chunkId, nLayers - 1); + mGpuStreams[event].record(); } -template -gsl::span TimeFrameGPU::getHostNCells(const int chunkId) +template +void TimeFrameGPU::recordEvents(const int start, const int end) { - return gsl::span(mHostNCells.data() + (nLayers - 2) * chunkId, nLayers - 2); + for (int i{start}; i < end; ++i) { + recordEvent(i); + } +} + +template +void TimeFrameGPU::wipe() +{ + unregisterHostMemory(0); + o2::its::TimeFrame::wipe(); } template class TimeFrameGPU<7>; -template class GpuTimeFrameChunk<7>; -} // namespace gpu -} // namespace its -} // namespace o2 \ No newline at end of file +// ALICE3 upgrade +#ifdef ENABLE_UPGRADES +template class TimeFrameGPU<11>; +template class TimeFrameGPU<13>; +#endif +} // namespace o2::its::gpu diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu deleted file mode 100644 index 7c09851eaa2fb..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITStrackingGPU/TracerGPU.h" - -#if !defined(__HIPCC__) && defined(__USE_GPU_TRACER__) -#include "nvToolsExt.h" - -constexpr uint32_t colors[] = {0xff00ff00, 0xff0000ff, 0xffffff00, 0xffff00ff, 0xff00ffff, 0xffff0000, 0xffffffff}; -constexpr int num_colors = sizeof(colors) / sizeof(uint32_t); - -namespace o2 -{ -namespace its -{ -namespace gpu -{ -Tracer::Tracer(const char* name, int color_id) -{ - color_id = color_id % num_colors; - nvtxEventAttributes_t eventAttrib = {0}; - eventAttrib.version = NVTX_VERSION; - eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; - eventAttrib.colorType = NVTX_COLOR_ARGB; - eventAttrib.color = colors[color_id]; - eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII; - eventAttrib.message.ascii = name; - nvtxRangePushEx(&eventAttrib); -} - -Tracer::~Tracer() -{ - nvtxRangePop(); -} - -} // namespace gpu -} // namespace its -} // namespace o2 -#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cu deleted file mode 100644 index de24ffc947127..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cu +++ /dev/null @@ -1,765 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "ITStracking/Constants.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/IndexTableUtils.h" -#include "ITStracking/MathUtils.h" - -#include "ITStrackingGPU/TrackerTraitsGPU.h" -#include "ITStrackingGPU/TracerGPU.h" - -#include "GPUCommonLogger.h" -#include "GPUCommonAlgorithmThrust.h" - -#ifndef __HIPCC__ -#define THRUST_NAMESPACE thrust::cuda -#else -#define THRUST_NAMESPACE thrust::hip -#endif - -namespace o2 -{ -namespace its -{ -using gpu::utils::checkGPUError; -using namespace constants::its2; - -namespace gpu -{ - -GPUg() void printBufferLayerOnThread(const int layer, const int* v, size_t size, const int len = 150, const unsigned int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - for (int i{0}; i < size; ++i) { - if (!(i % len)) { - printf("\n layer %d: ===>%d/%d\t", layer, i, (int)size); - } - printf("%d\t", v[i]); - } - printf("\n"); - } -} - -GPUd() const int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, - const o2::its::IndexTableUtils& utils, - const float z1, const float z2, float maxdeltaz, float maxdeltaphi) -{ - const float zRangeMin = o2::gpu::GPUCommonMath::Min(z1, z2) - maxdeltaz; - const float phiRangeMin = currentCluster.phi - maxdeltaphi; - const float zRangeMax = o2::gpu::GPUCommonMath::Max(z1, z2) + maxdeltaz; - const float phiRangeMax = currentCluster.phi + maxdeltaphi; - - if (zRangeMax < -LayersZCoordinate()[layerIndex + 1] || - zRangeMin > LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { - - return getEmptyBinsRect(); - } - - return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), - o2::gpu::GPUCommonMath::Min(ZBins - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; -} - -GPUhd() float Sq(float q) -{ - return q * q; -} - -template -struct trackletSortEmptyFunctor : public thrust::binary_function { - GPUhd() bool operator()(const T& lhs, const T& rhs) const - { - return lhs.firstClusterIndex > rhs.firstClusterIndex; - } -}; - -template -struct trackletSortIndexFunctor : public thrust::binary_function { - GPUhd() bool operator()(const T& lhs, const T& rhs) const - { - return lhs.firstClusterIndex < rhs.firstClusterIndex || (lhs.firstClusterIndex == rhs.firstClusterIndex && lhs.secondClusterIndex < rhs.secondClusterIndex); - } -}; - -// Dump vertices -GPUg() void printVertices(const Vertex* v, size_t size, const unsigned int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - printf("vertices: "); - for (int i{0}; i < size; ++i) { - printf("x=%f y=%f z=%f\n", v[i].getX(), v[i].getY(), v[i].getZ()); - } - } -} - -// Dump tracklets -GPUg() void printTracklets(const Tracklet* t, - const int offset, - const int startRof, - const int nrof, - const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - const int maxClustersPerRof = 5e2, - const int maxTrackletsPerCluster = 50, - const unsigned int tId = 0) -{ - if (threadIdx.x == tId) { - auto offsetCurrent{roFrameClustersCurrentLayer[offset]}; - auto offsetNext{roFrameClustersNextLayer[offset]}; - auto offsetChunk{(startRof - offset) * maxClustersPerRof * maxTrackletsPerCluster}; - for (int i{offsetChunk}; i < offsetChunk + nrof * maxClustersPerRof * maxTrackletsPerCluster; ++i) { - if (t[i].firstClusterIndex != -1) { - t[i].dump(offsetCurrent, offsetNext); - } - } - } -} - -GPUg() void printTrackletsNotStrided(const Tracklet* t, - const int offset, - const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - const int ntracklets, - const unsigned int tId = 0) -{ - if (threadIdx.x == tId) { - auto offsetCurrent{roFrameClustersCurrentLayer[offset]}; - auto offsetNext{roFrameClustersNextLayer[offset]}; - for (int i{0}; i < ntracklets; ++i) { - t[i].dump(offsetCurrent, offsetNext); - } - } -} - -// Compute the tracklets for a given layer -template -GPUg() void computeLayerTrackletsKernelSingleRof( - const int rof0, - const int maxRofs, - const int layerIndex, - const Cluster* clustersCurrentLayer, // input data rof0 - const Cluster* clustersNextLayer, // input data rof0-delta * trkPars, - const IndexTableUtils* utils, - const unsigned int maxTrackletsPerCluster = 50) -{ - for (int currentClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; currentClusterIndex < currentLayerClustersSize; currentClusterIndex += blockDim.x * gridDim.x) { - unsigned int storedTracklets{0}; - const Cluster& currentCluster{clustersCurrentLayer[currentClusterIndex]}; - const int currentSortedIndex{roFrameClusters[rof0] + currentClusterIndex}; - if (usedClustersLayer[currentSortedIndex]) { - continue; - } - int minRof = (rof0 >= trkPars->DeltaROF) ? rof0 - trkPars->DeltaROF : 0; - int maxRof = (rof0 == maxRofs - trkPars->DeltaROF) ? rof0 : rof0 + trkPars->DeltaROF; - const float inverseR0{1.f / currentCluster.radius}; - for (int iPrimaryVertex{0}; iPrimaryVertex < nVertices; iPrimaryVertex++) { - const auto& primaryVertex{vertices[iPrimaryVertex]}; - if (primaryVertex.getX() == 0.f && primaryVertex.getY() == 0.f && primaryVertex.getZ() == 0.f) { - continue; - } - const float resolution{o2::gpu::GPUCommonMath::Sqrt(Sq(trkPars->PVres) / primaryVertex.getNContributors() + Sq(positionResolution))}; - const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; - const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; - const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; - const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution - const float sigmaZ{std::sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * mSAngle))}; - - const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, *utils, zAtRmin, zAtRmax, sigmaZ * trkPars->NSigmaCut, phiCut)}; - if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { - continue; - } - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += trkPars->PhiBins; - } - constexpr int tableSize{256 * 128 + 1}; // hardcoded for the time being - - for (int rof1{minRof}; rof1 <= maxRof; ++rof1) { - if (!(roFrameClustersNext[rof1 + 1] - roFrameClustersNext[rof1])) { // number of clusters on next layer > 0 - continue; - } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % trkPars->PhiBins; - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = indexTable[rof1 * tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTable[rof1 * tableSize + maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= (roFrameClustersNext[rof1 + 1] - roFrameClustersNext[rof1])) { - break; - } - const Cluster& nextCluster{getPtrFromRuler(rof1, clustersNextLayer, roFrameClustersNext)[iNextCluster]}; - if (usedClustersNextLayer[nextCluster.clusterId]) { - continue; - } - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi)}; - const float deltaZ{o2::gpu::GPUCommonMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; - - if (deltaZ / sigmaZ < trkPars->NSigmaCut && (deltaPhi < phiCut || o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < phiCut)) { - trackletsLookUpTable[currentSortedIndex]++; // Race-condition safe - const float phi{o2::gpu::GPUCommonMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; - const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; - const size_t stride{currentClusterIndex * maxTrackletsPerCluster}; - new (tracklets + stride + storedTracklets) Tracklet{currentSortedIndex, roFrameClustersNext[rof1] + iNextCluster, tanL, phi, rof0, rof1}; - ++storedTracklets; - } - } - } - } - } - if (storedTracklets > maxTrackletsPerCluster) { - printf("its-gpu-tracklet finder: found more tracklets per clusters (%d) than maximum set (%d), check the configuration!\n", maxTrackletsPerCluster, storedTracklets); - } - } -} - -template -GPUg() void compileTrackletsLookupTableKernel(const Tracklet* tracklets, - int* trackletsLookUpTable, - const int nTracklets) -{ - for (int currentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; currentTrackletIndex < nTracklets; currentTrackletIndex += blockDim.x * gridDim.x) { - auto& tracklet{tracklets[currentTrackletIndex]}; - if (tracklet.firstClusterIndex >= 0) { - atomicAdd(trackletsLookUpTable + tracklet.firstClusterIndex, 1); - } - } -} - -template -GPUg() void computeLayerTrackletsKernelMultipleRof( - const int layerIndex, - const int iteration, - const unsigned int startRofId, - const unsigned int rofSize, - const int maxRofs, - const Cluster* clustersCurrentLayer, // input data rof0 - const Cluster* clustersNextLayer, // input data rof0-delta * trkPars, - const IndexTableUtils* utils, - const unsigned int maxClustersPerRof = 5e2, - const unsigned int maxTrackletsPerCluster = 50) -{ - const int phiBins{utils->getNphiBins()}; - const int zBins{utils->getNzBins()}; - for (unsigned int iRof{blockIdx.x}; iRof < rofSize; iRof += gridDim.x) { - auto rof0 = iRof + startRofId; - auto nClustersCurrentLayerRof = roFrameClustersCurrentLayer[rof0 + 1] - roFrameClustersCurrentLayer[rof0]; - auto* clustersCurrentLayerRof = clustersCurrentLayer + (roFrameClustersCurrentLayer[rof0] - roFrameClustersCurrentLayer[startRofId]); - auto nVerticesRof0 = nVertices[rof0 + 1] - nVertices[rof0]; - auto trackletsRof0 = tracklets + maxTrackletsPerCluster * maxClustersPerRof * iRof; - for (int currentClusterIndex = threadIdx.x; currentClusterIndex < nClustersCurrentLayerRof; currentClusterIndex += blockDim.x) { - if (nClustersCurrentLayerRof > maxClustersPerRof) { - printf("its-gpu-tracklet finder: on layer %d found more clusters per ROF (%d) than maximum set (%d), check the configuration!\n", layerIndex, nClustersCurrentLayerRof, maxClustersPerRof); - } - unsigned int storedTracklets{0}; - const Cluster& currentCluster{clustersCurrentLayerRof[currentClusterIndex]}; - const int currentSortedIndex{roFrameClustersCurrentLayer[rof0] + currentClusterIndex}; - const int currentSortedIndexChunk{currentSortedIndex - roFrameClustersCurrentLayer[startRofId]}; - if (usedClustersLayer[currentSortedIndex]) { - continue; - } - - int minRof = (rof0 >= trkPars->DeltaROF) ? rof0 - trkPars->DeltaROF : 0; - int maxRof = (rof0 == maxRofs - trkPars->DeltaROF) ? rof0 : rof0 + trkPars->DeltaROF; // works with delta = {0, 1} - const float inverseR0{1.f / currentCluster.radius}; - - for (int iPrimaryVertex{0}; iPrimaryVertex < nVerticesRof0; iPrimaryVertex++) { - const auto& primaryVertex{vertices[nVertices[rof0] + iPrimaryVertex]}; - const float resolution{o2::gpu::GPUCommonMath::Sqrt(Sq(trkPars->PVres) / primaryVertex.getNContributors() + Sq(positionResolution))}; - const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; - const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; - const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; - const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution - const float sigmaZ{std::sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * mSAngle))}; - - const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, *utils, zAtRmin, zAtRmax, sigmaZ * trkPars->NSigmaCut, phiCut)}; - - if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { - continue; - } - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += trkPars->PhiBins; - } - const int tableSize{phiBins * zBins + 1}; - for (int rof1{minRof}; rof1 <= maxRof; ++rof1) { - auto nClustersNext{roFrameClustersNextLayer[rof1 + 1] - roFrameClustersNextLayer[rof1]}; - if (!nClustersNext) { // number of clusters on next layer > 0 - continue; - } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % trkPars->PhiBins; - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = indexTablesNext[(rof1 - startRofId) * tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTablesNext[(rof1 - startRofId) * tableSize + maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= nClustersNext) { - break; - } - auto nextClusterIndex{roFrameClustersNextLayer[rof1] - roFrameClustersNextLayer[startRofId] + iNextCluster}; - const Cluster& nextCluster{clustersNextLayer[nextClusterIndex]}; - if (usedClustersNextLayer[nextCluster.clusterId]) { - continue; - } - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi)}; - const float deltaZ{o2::gpu::GPUCommonMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; - - if ((deltaZ / sigmaZ < trkPars->NSigmaCut && (deltaPhi < phiCut || o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < phiCut))) { - const float phi{o2::gpu::GPUCommonMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; - const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; - const size_t stride{currentClusterIndex * maxTrackletsPerCluster}; - if (storedTracklets < maxTrackletsPerCluster) { - new (trackletsRof0 + stride + storedTracklets) Tracklet{currentSortedIndexChunk, nextClusterIndex, tanL, phi, static_cast(rof0), static_cast(rof1)}; - } - // else { - // printf("its-gpu-tracklet-finder: on rof %d layer: %d: found more tracklets (%d) than maximum allowed per cluster. This is lossy!\n", rof0, layerIndex, storedTracklets); - // } - ++storedTracklets; - } - } - } - } - } - } - } -} - -// Decrease LUT entries corresponding to duplicated tracklets. NB: duplicate tracklets are removed separately (see const Tracklets*). -GPUg() void removeDuplicateTrackletsEntriesLUTKernel( - int* trackletsLookUpTable, - const Tracklet* tracklets, - const int* nTracklets, - const int layerIndex) -{ - int id0{-1}, id1{-1}; - for (int iTracklet{0}; iTracklet < nTracklets[layerIndex]; ++iTracklet) { - auto& trk = tracklets[iTracklet]; - if (trk.firstClusterIndex == id0 && trk.secondClusterIndex == id1) { - trackletsLookUpTable[id0]--; - } else { - id0 = trk.firstClusterIndex; - id1 = trk.secondClusterIndex; - } - } -} - -// Compute cells kernel -template -GPUg() void computeLayerCellsKernel( - const Tracklet* trackletsCurrentLayer, - const Tracklet* trackletsNextLayer, - const int* trackletsCurrentLayerLUT, - const int nTrackletsCurrent, - Cell* cells, - int* cellsLUT, - const StaticTrackingParameters* trkPars) -{ - for (int iCurrentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackletIndex < nTrackletsCurrent; iCurrentTrackletIndex += blockDim.x * gridDim.x) { - const Tracklet& currentTracklet = trackletsCurrentLayer[iCurrentTrackletIndex]; - const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; - const int nextLayerFirstTrackletIndex{trackletsCurrentLayerLUT[nextLayerClusterIndex]}; - const int nextLayerLastTrackletIndex{trackletsCurrentLayerLUT[nextLayerClusterIndex + 1]}; - if (nextLayerFirstTrackletIndex == nextLayerLastTrackletIndex) { - continue; - } - int foundCells{0}; - for (int iNextTrackletIndex{nextLayerFirstTrackletIndex}; iNextTrackletIndex < nextLayerLastTrackletIndex; ++iNextTrackletIndex) { - if (trackletsNextLayer[iNextTrackletIndex].firstClusterIndex != nextLayerClusterIndex) { - break; - } - const Tracklet& nextTracklet = trackletsNextLayer[iNextTrackletIndex]; - const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; - const float tanLambda{(currentTracklet.tanLambda + nextTracklet.tanLambda) * 0.5f}; - - if (deltaTanLambda / trkPars->CellDeltaTanLambdaSigma < trkPars->NSigmaCut) { - if constexpr (!initRun) { - new (cells + cellsLUT[iCurrentTrackletIndex] + foundCells) Cell{currentTracklet.firstClusterIndex, nextTracklet.firstClusterIndex, - nextTracklet.secondClusterIndex, - iCurrentTrackletIndex, - iNextTrackletIndex, - tanLambda}; - } - ++foundCells; - } - } - if constexpr (initRun) { - // Fill cell Lookup table - cellsLUT[iCurrentTrackletIndex] = foundCells; - } - } -} - -template -GPUg() void computeLayerCellNeighboursKernel(Cell* cellsCurrentLayer, - Cell* cellsNextLayer, - const int layerIndex, - const int* cellsNextLayerLUT, - int* neighboursLUT, - int* cellNeighbours, - const int* nCells, - const int maxCellNeighbours = 1e2) -{ - for (int iCurrentCellIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCellIndex < nCells[layerIndex]; iCurrentCellIndex += blockDim.x * gridDim.x) { - const Cell& currentCell = cellsCurrentLayer[iCurrentCellIndex]; - const int nextLayerTrackletIndex{currentCell.getSecondTrackletIndex()}; - const int nextLayerFirstCellIndex{cellsNextLayerLUT[nextLayerTrackletIndex]}; - const int nextLayerLastCellIndex{cellsNextLayerLUT[nextLayerTrackletIndex + 1]}; - int foundNeighbours{0}; - for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { - Cell& nextCell = cellsNextLayer[iNextCell]; - if (nextCell.getFirstTrackletIndex() != nextLayerTrackletIndex) { // Check if cells share the same tracklet - break; - } - if constexpr (initRun) { - atomicAdd(neighboursLUT + iNextCell, 1); - } else { - if (foundNeighbours >= maxCellNeighbours) { - printf("its-gpu-neighbours-finder: on layer: %d: found more neighbours (%d) than maximum allowed per cell, skipping writing. This is lossy!\n", layerIndex, neighboursLUT[iNextCell]); - continue; - } - cellNeighbours[neighboursLUT[iNextCell] + foundNeighbours++] = iCurrentCellIndex; - - const int currentCellLevel{currentCell.getLevel()}; - if (currentCellLevel >= nextCell.getLevel()) { - atomicExch(nextCell.getLevelPtr(), currentCellLevel + 1); - } - } - } - } -} - -} // namespace gpu - -template -void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) -{ - mTimeFrameGPU->initialise(iteration, mTrkParams[iteration], nLayers); -} - -template -void TrackerTraitsGPU::computeLayerTracklets(const int iteration) -{ - if (!mTimeFrameGPU->getClusters().size()) { - return; - } - const Vertex diamondVert({mTrkParams[iteration].Diamond[0], mTrkParams[iteration].Diamond[1], mTrkParams[iteration].Diamond[2]}, {25.e-6f, 0.f, 0.f, 25.e-6f, 0.f, 36.f}, 1, 1.f); - gsl::span diamondSpan(&diamondVert, 1); - std::vector threads(mTimeFrameGPU->getNChunks()); - // std::array, nLayers - 1> totTrackletsChunk{std::array{0, 0, 0}, std::array{0, 0, 0}, std::array{0, 0, 0}, std::array{0, 0, 0}, std::array{0, 0, 0}}; - for (int chunkId{0}; chunkId < mTimeFrameGPU->getNChunks(); ++chunkId) { - int maxTracklets{static_cast(mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->clustersPerROfCapacity) * - static_cast(mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxTrackletsPerCluster)}; - int maxRofPerChunk{mTimeFrameGPU->mNrof / (int)mTimeFrameGPU->getNChunks()}; - // Define workload - auto doTrackReconstruction = [&, chunkId, maxRofPerChunk, iteration]() -> void { - auto offset = chunkId * maxRofPerChunk; - auto maxROF = offset + maxRofPerChunk; - while (offset < maxROF) { - auto rofs = mTimeFrameGPU->loadChunkData(chunkId, offset, maxROF); - RANGE("chunk_gpu_tracking", 1); - for (int iLayer{0}; iLayer < nLayers - 1; ++iLayer) { - auto nclus = mTimeFrameGPU->getTotalClustersPerROFrange(offset, rofs, iLayer); - const float meanDeltaR{mTrkParams[iteration].LayerRadii[iLayer + 1] - mTrkParams[iteration].LayerRadii[iLayer]}; - gpu::computeLayerTrackletsKernelMultipleRof<<getStream(chunkId).get()>>>( - iLayer, // const int layerIndex, - iteration, // const int iteration, - offset, // const unsigned int startRofId, - rofs, // const unsigned int rofSize, - 0, // const unsigned int deltaRof, - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(iLayer), // const Cluster* clustersCurrentLayer, - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(iLayer + 1), // const Cluster* clustersNextLayer, - mTimeFrameGPU->getDeviceROframesClusters(iLayer), // const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - mTimeFrameGPU->getDeviceROframesClusters(iLayer + 1), // const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - mTimeFrameGPU->getChunk(chunkId).getDeviceIndexTables(iLayer + 1), // const int* indexTableNextLayer, - mTimeFrameGPU->getDeviceUsedClusters(iLayer), // const int* usedClustersCurrentLayer, - mTimeFrameGPU->getDeviceUsedClusters(iLayer + 1), // const int* usedClustersNextLayer, - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), // Tracklet* tracklets, // output data - mTimeFrameGPU->getDeviceVertices(), // const Vertex* vertices, - mTimeFrameGPU->getDeviceROframesPV(), // const int* pvROFrame, - mTimeFrameGPU->getPhiCut(iLayer), // const float phiCut, - mTimeFrameGPU->getMinR(iLayer + 1), // const float minR, - mTimeFrameGPU->getMaxR(iLayer + 1), // const float maxR, - meanDeltaR, // const float meanDeltaR, - mTimeFrameGPU->getPositionResolution(iLayer), // const float positionResolution, - mTimeFrameGPU->getMSangle(iLayer), // const float mSAngle, - mTimeFrameGPU->getDeviceTrackingParameters(), // const StaticTrackingParameters* trkPars, - mTimeFrameGPU->getDeviceIndexTableUtils(), // const IndexTableUtils* utils - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->clustersPerROfCapacity, // const int clustersPerROfCapacity, - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxTrackletsPerCluster); // const int maxTrackletsPerCluster - - // Remove empty tracklets due to striding. - auto nulltracklet = o2::its::Tracklet{}; - auto thrustTrackletsBegin = thrust::device_ptr(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer)); - auto thrustTrackletsEnd = thrust::device_ptr(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer) + (int)rofs * maxTracklets); - auto thrustTrackletsAfterEraseEnd = thrust::remove(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), - thrustTrackletsBegin, - thrustTrackletsEnd, - nulltracklet); - // Sort tracklets by first cluster index. - thrust::sort(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), - thrustTrackletsBegin, - thrustTrackletsAfterEraseEnd, - gpu::trackletSortIndexFunctor()); - - // Remove duplicates. - auto thrustTrackletsAfterUniqueEnd = thrust::unique(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), thrustTrackletsBegin, thrustTrackletsAfterEraseEnd); - - discardResult(cudaStreamSynchronize(mTimeFrameGPU->getStream(chunkId).get())); - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer] = thrustTrackletsAfterUniqueEnd - thrustTrackletsBegin; - // Compute tracklet lookup table. - gpu::compileTrackletsLookupTableKernel<<getStream(chunkId).get()>>>(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer]); - discardResult(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), // d_in - mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), // d_out - nclus, // num_items - mTimeFrameGPU->getStream(chunkId).get())); - - // Create tracklets labels, at the moment on the host - if (mTimeFrameGPU->hasMCinformation()) { - std::vector tracklets(mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer]); - checkGPUError(cudaHostRegister(tracklets.data(), tracklets.size() * sizeof(o2::its::Tracklet), cudaHostRegisterDefault)); - checkGPUError(cudaMemcpyAsync(tracklets.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), tracklets.size() * sizeof(o2::its::Tracklet), cudaMemcpyDeviceToHost, mTimeFrameGPU->getStream(chunkId).get())); - for (auto& trk : tracklets) { - MCCompLabel label; - int currentId{mTimeFrameGPU->mClusters[iLayer][trk.firstClusterIndex].clusterId}; // This is not yet offsetted to the index of the first cluster of the chunk - int nextId{mTimeFrameGPU->mClusters[iLayer + 1][trk.secondClusterIndex].clusterId}; // This is not yet offsetted to the index of the first cluster of the chunk - for (auto& lab1 : mTimeFrameGPU->getClusterLabels(iLayer, currentId)) { - for (auto& lab2 : mTimeFrameGPU->getClusterLabels(iLayer + 1, nextId)) { - if (lab1 == lab2 && lab1.isValid()) { - label = lab1; - break; - } - } - if (label.isValid()) { - break; - } - } - // TODO: implment label merging. - // mTimeFrameGPU->getTrackletsLabel(iLayer).emplace_back(label); - } - checkGPUError(cudaHostUnregister(tracklets.data())); - } - } - for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - // Compute layer cells. - gpu::computeLayerCellsKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer + 1), - mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer + 1), - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], - nullptr, - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), - mTimeFrameGPU->getDeviceTrackingParameters()); - - // Compute number of found Cells - checkGPUError(cub::DeviceReduce::Sum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_in - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells() + iLayer, // d_out - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], // num_items - mTimeFrameGPU->getStream(chunkId).get())); - // Compute LUT - discardResult(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_in - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_out - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], // num_items - mTimeFrameGPU->getStream(chunkId).get())); - - gpu::computeLayerCellsKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer + 1), - mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer + 1), - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], - mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), - mTimeFrameGPU->getDeviceTrackingParameters()); - } - checkGPUError(cudaMemcpyAsync(mTimeFrameGPU->getHostNCells(chunkId).data(), - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - (nLayers - 2) * sizeof(int), - cudaMemcpyDeviceToHost, - mTimeFrameGPU->getStream(chunkId).get())); - // Create cells labels TODO: make it work after fixing the tracklets labels - if (mTimeFrameGPU->hasMCinformation()) { - for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - std::vector cells(mTimeFrameGPU->getHostNCells(chunkId)[iLayer]); - // Async with not registered memory? - checkGPUError(cudaMemcpyAsync(cells.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), mTimeFrameGPU->getHostNCells(chunkId)[iLayer] * sizeof(o2::its::Cell), cudaMemcpyDeviceToHost)); - for (auto& cell : cells) { - MCCompLabel currentLab{mTimeFrameGPU->getTrackletsLabel(iLayer)[cell.getFirstTrackletIndex()]}; - MCCompLabel nextLab{mTimeFrameGPU->getTrackletsLabel(iLayer + 1)[cell.getSecondTrackletIndex()]}; - mTimeFrameGPU->getCellsLabel(iLayer).emplace_back(currentLab == nextLab ? currentLab : MCCompLabel()); - } - } - } - - for (int iLayer{0}; iLayer < nLayers - 3; ++iLayer) { - gpu::computeLayerCellNeighboursKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer + 1), - iLayer, - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer + 1), - mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), - nullptr, - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize); - - // Compute Cell Neighbours LUT - checkGPUError(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), // d_in - mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), // d_out - mTimeFrameGPU->getHostNCells(chunkId)[iLayer + 1], // num_items - mTimeFrameGPU->getStream(chunkId).get())); - - gpu::computeLayerCellNeighboursKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer + 1), - iLayer, - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer + 1), - mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeighbours(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize); - // if (!chunkId) { - // gpu::printBufferLayerOnThread<<<1, 1, 0, mTimeFrameGPU->getStream(chunkId).get()>>>(iLayer, - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeighbours(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize * rofs); - // } - } - - // End of tracking for this chunk - offset += rofs; - } - }; - threads[chunkId] = std::thread(doTrackReconstruction); - } - for (auto& thread : threads) { - thread.join(); - } - - mTimeFrameGPU->wipe(nLayers); -} - -template -void TrackerTraitsGPU::computeLayerCells(const int iteration) -{ -} - -// void TrackerTraitsGPU::refitTracks(const std::vector>& tf, std::vector& tracks) -// { -// PrimaryVertexContextNV* pvctx = static_cast(nullptr); //TODO: FIX THIS with Time Frames -// std::array cells; -// for (int iLayer = 0; iLayer < 5; iLayer++) { -// cells[iLayer] = pvctx->getDeviceCells()[iLayer].get(); -// } -// std::array clusters; -// for (int iLayer = 0; iLayer < 7; iLayer++) { -// clusters[iLayer] = pvctx->getDeviceClusters()[iLayer].get(); -// } -// //TODO: restore this -// // mChainRunITSTrackFit(*mChain, mPrimaryVertexContext->getRoads(), clusters, cells, tf, tracks); -// } - -template -void TrackerTraitsGPU::findCellsNeighbours(const int iteration){}; - -template -void TrackerTraitsGPU::findRoads(const int iteration){}; - -template -void TrackerTraitsGPU::findTracks(){}; - -template -void TrackerTraitsGPU::extendTracks(const int iteration){}; - -template -void TrackerTraitsGPU::setBz(float bz) -{ - mBz = bz; - mTimeFrameGPU->setBz(bz); -} - -template -int TrackerTraitsGPU::getTFNumberOfClusters() const -{ - return mTimeFrameGPU->getNumberOfClusters(); -} - -template -int TrackerTraitsGPU::getTFNumberOfTracklets() const -{ - return mTimeFrameGPU->getNumberOfTracklets(); -} - -template -int TrackerTraitsGPU::getTFNumberOfCells() const -{ - return mTimeFrameGPU->getNumberOfCells(); -} - -template class TrackerTraitsGPU<7>; -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx new file mode 100644 index 0000000000000..b773553bf3849 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -0,0 +1,457 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// + +#include + +#include +#include + +#include "ITStrackingGPU/TrackerTraitsGPU.h" +#include "ITStrackingGPU/TrackingKernels.h" +#include "ITStracking/Configuration.h" + +namespace o2::its +{ +template +void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) +{ + mTimeFrameGPU->initialise(this->mTrkParams[iteration], NLayers, iteration); + + if (this->mTrkParams[iteration].PassFlags[IterationStep::FirstPass]) { + // on default stream + mTimeFrameGPU->loadVertices(); + // TODO these tables can be put in persistent memory + mTimeFrameGPU->loadROFOverlapTable(); // this can be put in constant memory actually + mTimeFrameGPU->loadROFVertexLookupTable(); + mTimeFrameGPU->loadTrackingTopologies(); + // once the tables are in persistent memory just update the vertex one + // mTimeFrameGPU->updateROFVertexLookupTable(); + mTimeFrameGPU->loadIndexTableUtils(); + // pinned on host + mTimeFrameGPU->createUsedClustersDeviceArray(); + mTimeFrameGPU->createClustersDeviceArray(); + mTimeFrameGPU->createUnsortedClustersDeviceArray(); + mTimeFrameGPU->createClustersIndexTablesArray(); + mTimeFrameGPU->createTrackingFrameInfoDeviceArray(); + mTimeFrameGPU->createROFrameClustersDeviceArray(); + // device array + mTimeFrameGPU->createTrackletsLUTDeviceArray(); + mTimeFrameGPU->createTrackletsBuffersArray(); + mTimeFrameGPU->createCellsBuffersArray(); + mTimeFrameGPU->createCellsLUTDeviceArray(); + } + if (this->mTrkParams[iteration].PassFlags[IterationStep::FirstPass] || this->mTrkParams[iteration].PassFlags[IterationStep::UseUPCMask]) { + mTimeFrameGPU->loadROFCutMask(iteration); + } +} + +template +void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) +{ + mTimeFrameGPU = static_cast*>(tf); + this->mTimeFrame = static_cast*>(tf); +} + +template +void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iVertex) +{ + const auto topology = mTimeFrameGPU->getDeviceTrackingTopologyView(); + const auto hostTopology = mTimeFrameGPU->getTrackingTopologyView(); + const bool loadFirstPassData = this->mTrkParams[iteration].PassFlags[IterationStep::FirstPass] && iVertex <= 0; // load data only on first pass and first vertex + for (int iLayer{0}; iLayer < this->mTrkParams[iteration].NLayers; ++iLayer) { + if (loadFirstPassData) { + mTimeFrameGPU->createUsedClustersDevice(iLayer); + mTimeFrameGPU->loadClustersDevice(iLayer); + mTimeFrameGPU->loadClustersIndexTables(iLayer); + mTimeFrameGPU->loadROFrameClustersDevice(iLayer); + } + mTimeFrameGPU->recordEvent(iLayer); + } + + for (int linkId{0}; linkId < hostTopology.nLinks; ++linkId) { + mTimeFrameGPU->createTrackletsLUTDevice(loadFirstPassData, linkId); // on first pass allocates, then only clears memory + } + + // Stack allocations created from trackleting through road finding are scoped to one tracker pass. + // With per-primary-vertex processing, the chain is called once per vertex while initialisation is only done once. + mTimeFrameGPU->pushMemoryStack(iteration); + + for (int linkId{0}; linkId < hostTopology.nLinks; ++linkId) { + const auto link = hostTopology.getLink(linkId); + mTimeFrameGPU->waitEvent(linkId, link.fromLayer); + mTimeFrameGPU->waitEvent(linkId, link.toLayer); + countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceROFMaskTableView(), + linkId, + link.fromLayer, + link.toLayer, + mTimeFrameGPU->getDeviceROFOverlapTableView(), + mTimeFrameGPU->getDeviceROFVertexLookupTableView(), + iVertex, + mTimeFrameGPU->getDeviceVertices(), + mTimeFrameGPU->getDeviceROFramesPV(), + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes(), + mTimeFrameGPU->getDeviceROFrameClusters(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + mTimeFrameGPU->getDeviceTrackletsLUTs(), + this->mTrkParams[iteration].PassFlags[IterationStep::SelectUPCVertices], + this->mTrkParams[iteration].NSigmaCut, + topology, + mTimeFrameGPU->getLinkPhiCuts(), + this->mTrkParams[iteration].PVres, + mTimeFrameGPU->getMinRs(), + mTimeFrameGPU->getMaxRs(), + mTimeFrameGPU->getPositionResolutions(), + this->mTrkParams[iteration].LayerRadii, + mTimeFrameGPU->getLinkMSAngles(), + mTimeFrameGPU->getFrameworkAllocator(), + mTimeFrameGPU->getStreams()); + mTimeFrameGPU->createTrackletsBuffers(linkId); + if (mTimeFrameGPU->getNTracklets()[linkId] == 0) { + mTimeFrameGPU->recordEvent(linkId); + continue; + } + computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceROFMaskTableView(), + linkId, + link.fromLayer, + link.toLayer, + mTimeFrameGPU->getDeviceROFOverlapTableView(), + mTimeFrameGPU->getDeviceROFVertexLookupTableView(), + iVertex, + mTimeFrameGPU->getDeviceVertices(), + mTimeFrameGPU->getDeviceROFramesPV(), + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes(), + mTimeFrameGPU->getDeviceROFrameClusters(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceTracklets(), + mTimeFrameGPU->getNTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + mTimeFrameGPU->getDeviceTrackletsLUTs(), + this->mTrkParams[iteration].PassFlags[IterationStep::SelectUPCVertices], + this->mTrkParams[iteration].NSigmaCut, + topology, + mTimeFrameGPU->getLinkPhiCuts(), + this->mTrkParams[iteration].PVres, + mTimeFrameGPU->getMinRs(), + mTimeFrameGPU->getMaxRs(), + mTimeFrameGPU->getPositionResolutions(), + this->mTrkParams[iteration].LayerRadii, + mTimeFrameGPU->getLinkMSAngles(), + mTimeFrameGPU->getFrameworkAllocator(), + mTimeFrameGPU->getStreams()); + mTimeFrameGPU->recordEvent(linkId); + } +} + +template +void TrackerTraitsGPU::computeLayerCells(const int iteration) +{ + const auto topology = mTimeFrameGPU->getDeviceTrackingTopologyView(); + const auto hostTopology = mTimeFrameGPU->getTrackingTopologyView(); + for (int iLayer{0}; iLayer < this->mTrkParams[iteration].NLayers; ++iLayer) { + if (this->mTrkParams[iteration].PassFlags[IterationStep::FirstPass]) { + mTimeFrameGPU->loadUnsortedClustersDevice(iLayer); + mTimeFrameGPU->loadTrackingFrameInfoDevice(iLayer); + } + mTimeFrameGPU->recordEvent(iLayer); + } + + for (int cellTopologyId{hostTopology.nCells}; cellTopologyId--;) { + const auto cellTopology = hostTopology.getCell(cellTopologyId); + const auto first = hostTopology.getLink(cellTopology.firstLink); + const auto second = hostTopology.getLink(cellTopology.secondLink); + const int currentLayerTrackletsNum{static_cast(mTimeFrameGPU->getNTracklets()[cellTopology.firstLink])}; + if (!currentLayerTrackletsNum || !mTimeFrameGPU->getNTracklets()[cellTopology.secondLink]) { + mTimeFrameGPU->getNCells()[cellTopologyId] = 0; + continue; + } + + mTimeFrameGPU->createCellsLUTDevice(cellTopologyId); + mTimeFrameGPU->waitEvent(cellTopologyId, cellTopology.firstLink); + mTimeFrameGPU->waitEvent(cellTopologyId, cellTopology.secondLink); + mTimeFrameGPU->waitEvent(cellTopologyId, first.fromLayer); + mTimeFrameGPU->waitEvent(cellTopologyId, first.toLayer); + mTimeFrameGPU->waitEvent(cellTopologyId, second.toLayer); + countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + currentLayerTrackletsNum, + cellTopologyId, + topology, + nullptr, + mTimeFrameGPU->getDeviceArrayCellsLUT(), + mTimeFrameGPU->getDeviceCellLUTs()[cellTopologyId], + this->mBz, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].CellDeltaTanLambdaSigma, + this->mTrkParams[iteration].NSigmaCut, + this->mTrkParams[iteration].LayerxX0, + mTimeFrameGPU->getFrameworkAllocator(), + mTimeFrameGPU->getStreams()); + mTimeFrameGPU->createCellsBuffers(cellTopologyId); + if (mTimeFrameGPU->getNCells()[cellTopologyId] == 0) { + mTimeFrameGPU->recordEvent(cellTopologyId); + continue; + } + computeCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + currentLayerTrackletsNum, + cellTopologyId, + topology, + mTimeFrameGPU->getDeviceCells()[cellTopologyId], + mTimeFrameGPU->getDeviceArrayCellsLUT(), + mTimeFrameGPU->getDeviceCellLUTs()[cellTopologyId], + this->mBz, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].CellDeltaTanLambdaSigma, + this->mTrkParams[iteration].NSigmaCut, + this->mTrkParams[iteration].LayerxX0, + mTimeFrameGPU->getStreams()); + mTimeFrameGPU->recordEvent(cellTopologyId); + } + mTimeFrameGPU->syncStreams(false); +} + +template +void TrackerTraitsGPU::findCellsNeighbours(const int iteration) +{ + const auto hostTopology = mTimeFrameGPU->getTrackingTopologyView(); + for (int outerLayer{0}; outerLayer < NLayers; ++outerLayer) { + for (int targetCellTopologyId{0}; targetCellTopologyId < hostTopology.nCells; ++targetCellTopologyId) { + const auto targetCellTopology = hostTopology.getCell(targetCellTopologyId); + if (targetCellTopology.hitLayerMask.last() != outerLayer) { + continue; + } + const int targetCellsNum{static_cast(mTimeFrameGPU->getNCells()[targetCellTopologyId])}; + if (!targetCellsNum) { + mTimeFrameGPU->getNNeighbours()[targetCellTopologyId] = 0; + mTimeFrameGPU->recordEvent(targetCellTopologyId); + continue; + } + mTimeFrameGPU->createNeighboursIndexTablesDevice(targetCellTopologyId); + mTimeFrameGPU->createNeighboursLUTDevice(targetCellTopologyId, targetCellsNum); + + for (int sourceCellTopologyId{0}; sourceCellTopologyId < hostTopology.nCells; ++sourceCellTopologyId) { + const auto sourceCellTopology = hostTopology.getCell(sourceCellTopologyId); + const int sourceCellsNum{static_cast(mTimeFrameGPU->getNCells()[sourceCellTopologyId])}; + if (!sourceCellsNum || sourceCellTopology.secondLink != targetCellTopology.firstLink) { + continue; + } + mTimeFrameGPU->waitEvent(targetCellTopologyId, sourceCellTopologyId); + countCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), + mTimeFrameGPU->getDeviceNeighboursIndexTables(targetCellTopologyId), + mTimeFrameGPU->getDeviceArrayCellsLUT(), + sourceCellTopologyId, + targetCellTopologyId, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mBz, + sourceCellsNum, + mTimeFrameGPU->getStream(targetCellTopologyId)); + } + + scanCellNeighboursHandler(mTimeFrameGPU->getDeviceNeighboursIndexTables(targetCellTopologyId), + mTimeFrameGPU->getDeviceNeighboursLUT(targetCellTopologyId), + targetCellsNum, + mTimeFrameGPU->getFrameworkAllocator(), + mTimeFrameGPU->getStream(targetCellTopologyId)); + + mTimeFrameGPU->createNeighboursDevice(targetCellTopologyId); + if (mTimeFrameGPU->getNNeighbours()[targetCellTopologyId] == 0) { + mTimeFrameGPU->recordEvent(targetCellTopologyId); + continue; + } + + for (int sourceCellTopologyId{0}; sourceCellTopologyId < hostTopology.nCells; ++sourceCellTopologyId) { + const auto sourceCellTopology = hostTopology.getCell(sourceCellTopologyId); + const int sourceCellsNum{static_cast(mTimeFrameGPU->getNCells()[sourceCellTopologyId])}; + if (!sourceCellsNum || sourceCellTopology.secondLink != targetCellTopology.firstLink) { + continue; + } + computeCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), + mTimeFrameGPU->getDeviceNeighboursIndexTables(targetCellTopologyId), + mTimeFrameGPU->getDeviceArrayCellsLUT(), + mTimeFrameGPU->getDeviceNeighbours(targetCellTopologyId), + sourceCellTopologyId, + targetCellTopologyId, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mBz, + sourceCellsNum, + mTimeFrameGPU->getStream(targetCellTopologyId)); + } + mTimeFrameGPU->recordEvent(targetCellTopologyId); + } + } + mTimeFrameGPU->syncStreams(false); +} + +template +void TrackerTraitsGPU::findRoads(const int iteration) +{ + bounded_vector> firstClusters(this->mTrkParams[iteration].NLayers, bounded_vector(this->getMemoryPool().get()), this->getMemoryPool().get()); + firstClusters.resize(this->mTrkParams[iteration].NLayers); + const auto hostTopology = mTimeFrameGPU->getTrackingTopologyView(); + const bool extendTop = this->mTrkParams[iteration].PassFlags[IterationStep::TrackFollowerTop]; + const bool extendBot = this->mTrkParams[iteration].PassFlags[IterationStep::TrackFollowerBot]; + const bool extendTracks = extendTop || extendBot; + for (int startLevel{this->mTrkParams[iteration].CellsPerRoad()}; startLevel >= this->mTrkParams[iteration].CellMinimumLevel(); --startLevel) { + bounded_vector> trackSeeds(this->getMemoryPool().get()); + for (int startCellTopologyId{0}; startCellTopologyId < hostTopology.nCells; ++startCellTopologyId) { + const int startLayer = hostTopology.getCell(startCellTopologyId).hitLayerMask.last(); + if (!(this->mTrkParams[iteration].StartLayerMask.has(startLayer)) || mTimeFrameGPU->getNCells()[startCellTopologyId] == 0) { + continue; + } + processNeighboursHandler(startLevel, + startCellTopologyId, + mTimeFrameGPU->getDeviceArrayCells(), + mTimeFrameGPU->getDeviceCells()[startCellTopologyId], + nullptr, + nullptr, + mTimeFrameGPU->getArrayNCells().data(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayNeighbours(), + mTimeFrameGPU->getDeviceArrayNeighboursCellLUT(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + trackSeeds, + this->mBz, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].MaxChi2NDF, + this->mTrkParams[iteration].MaxHoles, + this->mTrkParams[iteration].getMinSeedingClusters(), + this->mTrkParams[iteration].HoleLayerMask, + this->mTrkParams[iteration].getNonSeedingLayerMask(), + this->mTrkParams[iteration].LayerxX0, + mTimeFrameGPU->getDevicePropagator(), + this->mTrkParams[iteration].CorrType, + mTimeFrameGPU->getFrameworkAllocator()); + } + // fixme: I don't want to move tracks back and forth, but I need a way to use a thrust::allocator that is aware of our managed memory. + if (trackSeeds.empty()) { + LOGP(debug, "No track seeds found, skipping track finding"); + continue; + } + mTimeFrameGPU->loadTrackSeedsDevice(trackSeeds); + + // Since TrackITSExt is an enourmous class it is better to first count how many + // successfull fits we do and only then allocate + countTrackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceTrackSeedsLUT(), + this->mTrkParams[iteration].LayerRadii, + this->mTrkParams[iteration].MinPt, + this->mTrkParams[iteration].LayerxX0, + trackSeeds.size(), + this->mBz, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].MaxChi2NDF, + this->mTrkParams[iteration].ReseedIfShorter, + this->mTrkParams[iteration].RepeatRefitOut, + this->mTrkParams[iteration].ShiftRefToCluster, + mTimeFrameGPU->getDevicePropagator(), + this->mTrkParams[iteration].CorrType, + mTimeFrameGPU->getFrameworkAllocator()); + mTimeFrameGPU->createTrackITSExtDevice(trackSeeds.size()); + if (extendTracks) { + mTimeFrameGPU->createTrackExtensionScratchDevice(constants::GPUThreadsTotal, this->mTrkParams[iteration].TrackFollowerMaxHypotheses); + } + computeTrackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceROFMaskTableView(), + mTimeFrameGPU->getDeviceROFOverlapTableView(), + mTimeFrameGPU->getDeviceArrayClusters(), + (const unsigned char**)mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceROFrameClusters(), + mTimeFrameGPU->getDeviceTrackITSExt(), + mTimeFrameGPU->getDeviceTrackIndices(), + mTimeFrameGPU->getDeviceTrackSeedsLUT(), + extendTracks ? mTimeFrameGPU->getDeviceActiveTrackExtensionHypotheses() : nullptr, + extendTracks ? mTimeFrameGPU->getDeviceNextTrackExtensionHypotheses() : nullptr, + this->mTrkParams[iteration].LayerRadii, + this->mTrkParams[iteration].MinPt, + this->mTrkParams[iteration].LayerxX0, + trackSeeds.size(), + mTimeFrameGPU->getNTrackSeeds(), + this->mBz, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].MaxChi2NDF, + this->mTrkParams[iteration].ReseedIfShorter, + this->mTrkParams[iteration].RepeatRefitOut, + this->mTrkParams[iteration].ShiftRefToCluster, + this->mTrkParams[iteration].NLayers, + this->mTrkParams[iteration].PhiBins, + this->mTrkParams[iteration].TrackFollowerMaxHypotheses, + extendTop, + extendBot, + this->mTrkParams[iteration].TrackFollowerNSigmaCutPhi, + this->mTrkParams[iteration].TrackFollowerNSigmaCutZ, + mTimeFrameGPU->getDevicePropagator(), + this->mTrkParams[iteration].CorrType, + mTimeFrameGPU->getFrameworkAllocator()); + mTimeFrameGPU->downloadTrackITSExtDevice(); + mTimeFrameGPU->downloadTrackIndicesDevice(); + + auto& tracks = mTimeFrameGPU->getTrackITSExt(); + const auto& trackIndices = mTimeFrameGPU->getTrackIndices(); + this->acceptTracks(iteration, tracks, trackIndices, firstClusters); + mTimeFrameGPU->loadUsedClustersDevice(); + } + this->markTracks(iteration); + // wipe the artefact memory + mTimeFrameGPU->popMemoryStack(iteration); +}; + +template +int TrackerTraitsGPU::getTFNumberOfClusters() const +{ + return mTimeFrameGPU->getNumberOfClusters(); +} + +template +int TrackerTraitsGPU::getTFNumberOfTracklets() const +{ + return std::accumulate(mTimeFrameGPU->getNTracklets().begin(), mTimeFrameGPU->getNTracklets().end(), 0); +} + +template +int TrackerTraitsGPU::getTFNumberOfCells() const +{ + return mTimeFrameGPU->getNumberOfCells(); +} + +template +void TrackerTraitsGPU::setBz(float bz) +{ + this->mBz = bz; + mTimeFrameGPU->setBz(bz); +} + +template class TrackerTraitsGPU<7>; +#ifdef ENABLE_UPGRADES +template class TrackerTraitsGPU<11>; +template class TrackerTraitsGPU<13>; +#endif +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu new file mode 100644 index 0000000000000..bf443e410e7d8 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -0,0 +1,1837 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ITStracking/Constants.h" +#include "ITStracking/Definitions.h" +#include "ITStracking/IndexTableUtils.h" +#include "ITStracking/MathUtils.h" +#include "ITStracking/ExternalAllocator.h" +#include "ITStracking/Tracklet.h" +#include "ITStracking/Cluster.h" +#include "ITStracking/Cell.h" +#include "ITStracking/TrackHelpers.h" +#include "ITStracking/TrackFollower.h" +#include "DataFormatsITS/TrackITS.h" +#include "ITStrackingGPU/TrackingKernels.h" +#include "ITStrackingGPU/Utils.h" +#include "utils/strtag.h" + +// O2 track model +#include "ReconstructionDataFormats/Track.h" +#include "DetectorsBase/Propagator.h" +using namespace o2::track; + +namespace o2::its +{ +namespace gpu +{ + +template +struct sort_by_second { + GPUhd() bool operator()(const gpuPair& a, const gpuPair& b) const { return a.second < b.second; } +}; + +template +struct pair_to_first { + GPUhd() int operator()(const gpuPair& a) const + { + return a.first; + } +}; + +template +struct pair_to_second { + GPUhd() int operator()(const gpuPair& a) const + { + return a.second; + } +}; + +template +struct is_invalid_pair { + GPUhd() bool operator()(const gpuPair& p) const + { + return p.first == -1 && p.second == -1; + } +}; + +template +struct is_valid_pair { + GPUhd() bool operator()(const gpuPair& p) const + { + return !(p.first == -1 && p.second == -1); + } +}; + +struct compare_track_index_chi2 { + const TrackITSExt* tracks; + + GPUhd() bool operator()(const int a, const int b) const + { + return o2::its::track::isBetter(tracks[a], tracks[b]); + } +}; + +template +struct TrackExtensionDirectionFollowerDevice { + GPUdi() bool operator()(TrackITSInternal& candidate, bool outward) const + { + const TrackExtensionHypothesis startHypothesis{candidate, outward}; + TrackExtensionHypothesis bestHypothesis; + if (!followTrackExtensionDirection(startHypothesis, *fitCtx, *followCtx, outward, + activeHypotheses, nextHypotheses, bestHypothesis)) { + return false; + } + updateTrackFromExtensionHypothesis(bestHypothesis, outward, fitCtx->nLayers, candidate); + return true; + } + + const o2::its::track::TrackFitContext* fitCtx{nullptr}; + const TrackFollowContext* followCtx{nullptr}; + TrackExtensionHypothesis* activeHypotheses{nullptr}; + TrackExtensionHypothesis* nextHypotheses{nullptr}; +}; + +template +GPUg() void __launch_bounds__(constants::GPUThreads, 1) countTrackSeedsKernel( + TrackSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const float* layerRadii, + const float* minPts, + const float* layerxX0, + const unsigned int nSeeds, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType) +{ + const o2::its::track::TrackFitContext fitCtx{ + foundTrackingFrameInfo, layerxX0, NLayers, bz, + maxChi2ClusterAttachment, maxChi2NDF, + propagator, matCorrType, shiftRefToCluster, repeatRefitOut}; + for (int iCurrentTrackSeedIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackSeedIndex < nSeeds; iCurrentTrackSeedIndex += blockDim.x * gridDim.x) { + TrackITSInternal temporaryTrack; + if (o2::its::track::refitTrackSeed(trackSeeds[iCurrentTrackSeedIndex], + temporaryTrack, + fitCtx, + unsortedClusters, + layerRadii, + minPts, + reseedIfShorter)) { + seedLUT[iCurrentTrackSeedIndex] = 1; + } + } +} + +template +GPUg() void __launch_bounds__(constants::GPUThreads, 1) fitTrackSeedsKernel( + TrackSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + const IndexTableUtils* utils, + const typename ROFMaskTable::View rofMask, + const typename ROFOverlapTable::View rofOverlaps, + const Cluster** clusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + const int** ROFClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + TrackExtensionHypothesis* activeHypothesesScratch, + TrackExtensionHypothesis* nextHypothesesScratch, + const float* layerRadii, + const float* minPts, + const float* layerxX0, + const unsigned int nSeeds, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const int nLayers, + const int phiBins, + const int maxHypothesesConfig, + const bool extendTop, + const bool extendBot, + const float nSigmaCutPhi, + const float nSigmaCutZ, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType) +{ + const o2::its::track::TrackFitContext fitCtx{ + foundTrackingFrameInfo, layerxX0, nLayers, bz, + maxChi2ClusterAttachment, maxChi2NDF, + propagator, matCorrType, shiftRefToCluster, repeatRefitOut}; + const TrackFollowContext followCtx{ + utils, rofMask, rofOverlaps, + clusters, usedClusters, clustersIndexTables, ROFClusters, + layerRadii, phiBins, maxHypothesesConfig, nSigmaCutPhi, nSigmaCutZ}; + for (int iCurrentTrackSeedIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackSeedIndex < nSeeds; iCurrentTrackSeedIndex += blockDim.x * gridDim.x) { + if (seedLUT[iCurrentTrackSeedIndex] == seedLUT[iCurrentTrackSeedIndex + 1]) { + continue; + } + TrackITSInternal temporaryTrack; + bool refitSuccess = o2::its::track::refitTrackSeed(trackSeeds[iCurrentTrackSeedIndex], + temporaryTrack, + fitCtx, + unsortedClusters, + layerRadii, + minPts, + reseedIfShorter); + if (refitSuccess) { + if ((extendTop || extendBot) && activeHypothesesScratch && nextHypothesesScratch) { + const int maxHypotheses = o2::gpu::CAMath::Max(maxHypothesesConfig, 1); + const int threadIndex = blockIdx.x * blockDim.x + threadIdx.x; + auto* activeHypotheses = activeHypothesesScratch + threadIndex * maxHypotheses; + auto* nextHypotheses = nextHypothesesScratch + threadIndex * maxHypotheses; + const auto backup = temporaryTrack; + auto best = temporaryTrack; + uint32_t bestDiff{0}; + TrackExtensionDirectionFollowerDevice followDirection{&fitCtx, &followCtx, activeHypotheses, nextHypotheses}; + TrackExtensionBestTrial bestTrial{backup.getPattern(), fitCtx}; + followTrackExtensionBranches(backup, extendTop, extendBot, nLayers, followDirection, bestTrial, best, bestDiff); + temporaryTrack = best; + tracks[seedLUT[iCurrentTrackSeedIndex]] = makeTrackITSExt(temporaryTrack); + if (bestDiff) { + tracks[seedLUT[iCurrentTrackSeedIndex]].setExtendedLayerPattern(bestDiff); + } + continue; + } + tracks[seedLUT[iCurrentTrackSeedIndex]] = makeTrackITSExt(temporaryTrack); + } + } +} + +template +GPUg() void __launch_bounds__(constants::GPUThreads, 1) computeLayerCellNeighboursKernel( + CellSeed** cellSeedArray, + int* neighboursCursor, + int** cellsLUTs, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells) +{ + for (int iCurrentCellIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCellIndex < nCells; iCurrentCellIndex += blockDim.x * gridDim.x) { + const auto& currentCellSeed{cellSeedArray[sourceCellTopologyId][iCurrentCellIndex]}; + const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; + const int nextLayerFirstCellIndex{cellsLUTs[targetCellTopologyId][nextLayerTrackletIndex]}; + const int nextLayerLastCellIndex{cellsLUTs[targetCellTopologyId][nextLayerTrackletIndex + 1]}; + for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { + auto nextCellSeed{cellSeedArray[targetCellTopologyId][iNextCell]}; // Copy + if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeed.getTimeStamp())) { + break; + } + + if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || + !nextCellSeed.propagateTo(currentCellSeed.getX(), bz)) { + continue; + } + + float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); + if (chi2 > maxChi2ClusterAttachment) { + continue; + } + + if constexpr (initRun) { + atomicAdd(neighboursCursor + iNextCell, 1); + } else { + const int offset = atomicAdd(neighboursCursor + iNextCell, 1); + cellNeighbours[offset] = {sourceCellTopologyId, iCurrentCellIndex, targetCellTopologyId, iNextCell, currentCellSeed.getLevel() + 1}; + const int currentCellLevel{currentCellSeed.getLevel()}; + if (currentCellLevel >= nextCellSeed.getLevel()) { + atomicMax(cellSeedArray[targetCellTopologyId][iNextCell].getLevelPtr(), currentCellLevel + 1); + } + } + } + } +} + +template +GPUg() void __launch_bounds__(constants::GPUThreads, 1) computeLayerCellsKernel( + const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTrackletsCurrent, + const int cellTopologyId, + const typename TrackingTopology::View topology, + CellSeed* cells, + int** cellsLUTs, + const float* layerxX0, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut) +{ + const auto cellTopology = topology.getCell(cellTopologyId); + const auto first = topology.getLink(cellTopology.firstLink); + const auto second = topology.getLink(cellTopology.secondLink); + const int layers[3] = {first.fromLayer, first.toLayer, second.toLayer}; + for (int iCurrentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackletIndex < nTrackletsCurrent; iCurrentTrackletIndex += blockDim.x * gridDim.x) { + if constexpr (!initRun) { + if (cellsLUTs[cellTopologyId][iCurrentTrackletIndex] == cellsLUTs[cellTopologyId][iCurrentTrackletIndex + 1]) { + continue; + } + } + const Tracklet& currentTracklet = tracklets[cellTopology.firstLink][iCurrentTrackletIndex]; + const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; + const int nextLayerFirstTrackletIndex{trackletsLUT[cellTopology.secondLink][nextLayerClusterIndex]}; + const int nextLayerLastTrackletIndex{trackletsLUT[cellTopology.secondLink][nextLayerClusterIndex + 1]}; + if (nextLayerFirstTrackletIndex == nextLayerLastTrackletIndex) { + continue; + } + int foundCells{0}; + for (int iNextTrackletIndex{nextLayerFirstTrackletIndex}; iNextTrackletIndex < nextLayerLastTrackletIndex; ++iNextTrackletIndex) { + if (tracklets[cellTopology.secondLink][iNextTrackletIndex].firstClusterIndex != nextLayerClusterIndex) { + break; + } + const Tracklet& nextTracklet = tracklets[cellTopology.secondLink][iNextTrackletIndex]; + if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) { + continue; + } + const float deltaTanLambda{o2::gpu::CAMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; + + if (deltaTanLambda / cellDeltaTanLambdaSigma < nSigmaCut) { + const int clusId[3]{ + sortedClusters[layers[0]][currentTracklet.firstClusterIndex].clusterId, + sortedClusters[layers[1]][nextTracklet.firstClusterIndex].clusterId, + sortedClusters[layers[2]][nextTracklet.secondClusterIndex].clusterId}; + + const auto& cluster1_glo = unsortedClusters[layers[0]][clusId[0]]; + const auto& cluster2_glo = unsortedClusters[layers[1]][clusId[1]]; + const auto& cluster3_tf = tfInfo[layers[2]][clusId[2]]; + auto track{o2::its::track::buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf, bz)}; + float chi2{0.f}; + bool good{false}; + for (int iC{2}; iC--;) { + const TrackingFrameInfo& trackingHit = tfInfo[layers[iC]][clusId[iC]]; + if (!track.rotate(trackingHit.alphaTrackingFrame)) { + break; + } + if (!track.propagateTo(trackingHit.xTrackingFrame, bz)) { + break; + } + + if (!track.correctForMaterial(layerxX0[layers[iC]], layerxX0[layers[iC]] * constants::Radl * constants::Rho, true)) { + break; + } + + const auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; + if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { + break; + } + if (!iC && predChi2 > maxChi2ClusterAttachment) { + break; + } + good = !iC; + chi2 += predChi2; + } + if (!good) { + continue; + } + if constexpr (!initRun) { + TimeEstBC ts = currentTracklet.getTimeStamp(); + ts += nextTracklet.getTimeStamp(); + new (cells + cellsLUTs[cellTopologyId][iCurrentTrackletIndex] + foundCells) CellSeed{cellTopology.hitLayerMask, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2, ts}; + } + ++foundCells; + } + } + if constexpr (initRun) { + cellsLUTs[cellTopologyId][iCurrentTrackletIndex] = foundCells; + } + } +} + +template +GPUg() void __launch_bounds__(constants::GPUThreads, 1) computeLayerTrackletsMultiROFKernel( + const IndexTableUtils* utils, + const typename ROFMaskTable::View rofMask, + const int linkId, + const typename TrackingTopology::View topology, + const typename ROFOverlapTable::View rofOverlaps, + const typename ROFVertexLookupTable::View vertexLUT, + const Vertex* vertices, + const int* rofPV, + const int vertexId, + const Cluster** clusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** indexTables, + Tracklet** tracklets, + int** trackletsLUT, + const bool selectUPCVertices, + const float NSigmaCut, + const float phiCut, + const float resolutionPV, + const float minR, + const float maxR, + const float positionResolution, + const float meanDeltaR, + const float MSAngle) +{ + const auto link = topology.getLink(linkId); + const int fromLayer = link.fromLayer; + const int toLayer = link.toLayer; + const int phiBins{utils->getNphiBins()}; + const int zBins{utils->getNzBins()}; + const int tableSize{phiBins * zBins + 1}; + const int totalROFs0 = rofOverlaps.getLayer(fromLayer).mNROFsTF; + const int totalROFs1 = rofOverlaps.getLayer(toLayer).mNROFsTF; + for (unsigned int pivotROF{blockIdx.x}; pivotROF < totalROFs0; pivotROF += gridDim.x) { + if (!rofMask.isROFEnabled(fromLayer, pivotROF)) { + continue; + } + + const auto& pvs = vertexLUT.getVertices(fromLayer, pivotROF); + auto primaryVertices = gpuSpan(&vertices[pvs.getFirstEntry()], pvs.getEntries()); + if (primaryVertices.empty()) { + continue; + } + const auto startVtx{vertexId >= 0 ? vertexId : 0}; + const auto endVtx{vertexId >= 0 ? o2::gpu::CAMath::Min(vertexId + 1, static_cast(primaryVertices.size())) : static_cast(primaryVertices.size())}; + if (endVtx <= startVtx || (vertexId + 1) > primaryVertices.size()) { + continue; + } + + const auto& rofOverlap = rofOverlaps.getOverlap(fromLayer, toLayer, pivotROF); + if (!rofOverlap.getEntries()) { + continue; + } + + auto clustersCurrentLayer = getClustersOnLayer(pivotROF, totalROFs0, fromLayer, ROFClusters, clusters); + if (clustersCurrentLayer.empty()) { + continue; + } + + for (int currentClusterIndex = threadIdx.x; currentClusterIndex < clustersCurrentLayer.size(); currentClusterIndex += blockDim.x) { + + unsigned int storedTracklets{0}; + const auto& currentCluster{clustersCurrentLayer[currentClusterIndex]}; + const int currentSortedIndex{ROFClusters[fromLayer][pivotROF] + currentClusterIndex}; + if (usedClusters[fromLayer][currentCluster.clusterId]) { + continue; + } + if constexpr (!initRun) { + if (trackletsLUT[linkId][currentSortedIndex] == trackletsLUT[linkId][currentSortedIndex + 1]) { + continue; + } + } + + const float inverseR0{1.f / currentCluster.radius}; + for (int iV{startVtx}; iV < endVtx; ++iV) { + auto& primaryVertex{primaryVertices[iV]}; + if (!vertexLUT.isVertexCompatible(fromLayer, pivotROF, primaryVertex)) { + continue; + } + if (primaryVertex.isFlagSet(Vertex::Flags::UPCMode) != selectUPCVertices) { + continue; + } + + const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(resolutionPV) / primaryVertex.getNContributors() + math_utils::Sq(positionResolution)); + const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; + const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; + const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; + const float sqInverseDeltaZ0{1.f / (math_utils::Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + constants::Tolerance)}; /// protecting from overflows adding the detector resolution + const float sigmaZ{o2::gpu::CAMath::Sqrt(math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInverseDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f) + math_utils::Sq(meanDeltaR * MSAngle))}; + const int4 selectedBinsRect{o2::its::getBinsRect(currentCluster, toLayer, zAtRmin, zAtRmax, sigmaZ * NSigmaCut, phiCut, *utils)}; + if (selectedBinsRect.x < 0) { + continue; + } + int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; + + if (phiBinsNum < 0) { + phiBinsNum += phiBins; + } + + for (short targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) { + if (!rofMask.isROFEnabled(toLayer, targetROF)) { + continue; + } + auto clustersNextLayer = getClustersOnLayer(targetROF, totalROFs1, toLayer, ROFClusters, clusters); + if (clustersNextLayer.empty()) { + continue; + } + const auto ts = rofOverlaps.getTimeStamp(fromLayer, pivotROF, toLayer, targetROF); + if (!ts.isCompatible(primaryVertex.getTimeStamp())) { + continue; + } + for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { + int iPhiBin = (selectedBinsRect.y + iPhiCount) % phiBins; + const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; + const int firstRowClusterIndex = indexTables[toLayer][(targetROF)*tableSize + firstBinIndex]; + const int maxRowClusterIndex = indexTables[toLayer][(targetROF)*tableSize + maxBinIndex]; + for (int nextClusterIndex{firstRowClusterIndex}; nextClusterIndex < maxRowClusterIndex; ++nextClusterIndex) { + if (nextClusterIndex >= clustersNextLayer.size()) { + break; + } + const Cluster& nextCluster{clustersNextLayer[nextClusterIndex]}; + if (usedClusters[toLayer][nextCluster.clusterId]) { + continue; + } + const float deltaPhi{o2::gpu::CAMath::Abs(currentCluster.phi - nextCluster.phi)}; + const float deltaZ{o2::gpu::CAMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; + if (deltaZ / sigmaZ < NSigmaCut && (deltaPhi < phiCut || o2::gpu::CAMath::Abs(deltaPhi - o2::constants::math::TwoPI) < phiCut)) { + if constexpr (initRun) { + trackletsLUT[linkId][currentSortedIndex]++; // we need l0 as well for usual exclusive sums. + } else { + const float phi{o2::gpu::CAMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; + const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; + const int nextSortedIndex{ROFClusters[toLayer][targetROF] + nextClusterIndex}; + new (tracklets[linkId] + trackletsLUT[linkId][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, ts}; + } + ++storedTracklets; + } + } + } + } + } + } + } +} + +GPUg() void __launch_bounds__(constants::GPUThreads, 1) compileTrackletsLookupTableKernel( + const Tracklet* tracklets, + int* trackletsLookUpTable, + const int nTracklets) +{ + for (int currentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; currentTrackletIndex < nTracklets; currentTrackletIndex += blockDim.x * gridDim.x) { + atomicAdd(&trackletsLookUpTable[tracklets[currentTrackletIndex].firstClusterIndex], 1); + } +} + +template +GPUg() void __launch_bounds__(constants::GPUThreads, 1) processNeighboursKernel( + const int defaultCellTopologyId, + const int level, + CellSeed** allCellSeeds, + CurrentSeed* currentCellSeeds, + const int* currentCellIds, + const int* currentCellTopologyIds, + const unsigned int nCurrentCells, + TrackSeed* updatedCellSeeds, + int* updatedCellsIds, + int* updatedCellTopologyIds, + int* foundSeedsTable, // auxiliary only in GPU code to compute the number of cells per iteration + const unsigned char** usedClusters, // Used clusters + CellNeighbour** neighbours, + int** neighboursLUT, + const TrackingFrameInfo** foundTrackingFrameInfo, + const float* layerxX0, + const float bz, + const float maxChi2ClusterAttachment, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType) +{ + for (unsigned int iCurrentCell = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCell < nCurrentCells; iCurrentCell += blockDim.x * gridDim.x) { + if constexpr (!dryRun) { + if (foundSeedsTable[iCurrentCell] == foundSeedsTable[iCurrentCell + 1]) { + continue; + } + } + int foundSeeds{0}; + const auto& currentCell{currentCellSeeds[iCurrentCell]}; + const int cellTopologyId = currentCellTopologyIds == nullptr ? defaultCellTopologyId : currentCellTopologyIds[iCurrentCell]; + if (currentCell.getLevel() != level) { + continue; + } + if (currentCellIds == nullptr) { + bool used = false; + for (int layer = 0; layer < NLayers; ++layer) { + const int clusterIndex = currentCell.getCluster(layer); + used |= clusterIndex != constants::UnusedIndex && usedClusters[layer][clusterIndex]; + } + if (used) { + continue; + } + } + const int cellId = currentCellIds == nullptr ? iCurrentCell : currentCellIds[iCurrentCell]; + if (cellTopologyId < 0 || neighboursLUT[cellTopologyId] == nullptr || neighbours[cellTopologyId] == nullptr) { + continue; + } + + const int startNeighbourId{neighboursLUT[cellTopologyId][cellId]}; + const int endNeighbourId{neighboursLUT[cellTopologyId][cellId + 1]}; + + for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { + const auto& neighbourRef = neighbours[cellTopologyId][iNeighbourCell]; + const int neighbourCellTopologyId = neighbourRef.cellTopology; + const int neighbourCellId = neighbourRef.cell; + const auto& neighbourCell = allCellSeeds[neighbourCellTopologyId][neighbourCellId]; + + if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { + continue; + } + if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) { + continue; + } + if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { + continue; + } + const int neighbourLayer = neighbourCell.getInnerLayer(); + const int neighbourCluster = neighbourCell.getFirstClusterIndex(); + if (usedClusters[neighbourLayer][neighbourCluster]) { + continue; + } + TrackSeed seed{currentCell}; + auto& trHit = foundTrackingFrameInfo[neighbourLayer][neighbourCluster]; + + if (!seed.rotate(trHit.alphaTrackingFrame)) { + continue; + } + + if (!propagator->propagateToX(seed, trHit.xTrackingFrame, bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, matCorrType)) { + continue; + } + + if (matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!seed.correctForMaterial(layerxX0[neighbourLayer], layerxX0[neighbourLayer] * constants::Radl * constants::Rho, true)) { + continue; + } + } + + auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; + if ((predChi2 > maxChi2ClusterAttachment) || predChi2 < 0.f) { + continue; + } + seed.setChi2(seed.getChi2() + predChi2); + if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { + continue; + } + if constexpr (dryRun) { + foundSeedsTable[iCurrentCell]++; + } else { + seed.getClusters()[neighbourLayer] = neighbourCluster; + auto mask = seed.getHitLayerMask(); + mask.set(neighbourLayer); + seed.setHitLayerMask(mask); + seed.setLevel(neighbourCell.getLevel()); + seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex()); + seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex()); + updatedCellsIds[foundSeedsTable[iCurrentCell] + foundSeeds] = neighbourCellId; + updatedCellTopologyIds[foundSeedsTable[iCurrentCell] + foundSeeds] = neighbourCellTopologyId; + updatedCellSeeds[foundSeedsTable[iCurrentCell] + foundSeeds] = seed; + } + foundSeeds++; + } + } +} + +} // namespace gpu + +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, + const int linkId, + const int fromLayer, + const int toLayer, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const typename TrackingTopology::View topology, + bounded_vector& linkPhiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& linkMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams) +{ + gpu::computeLayerTrackletsMultiROFKernel<<>>( + utils, + rofMask, + linkId, + topology, + rofOverlaps, + vertexLUT, + vertices, + rofPV, + vertexId, + clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + nullptr, + trackletsLUTs, + selectUPCVertices, + NSigmaCut, + linkPhiCuts[linkId], + resolutionPV, + minRs[toLayer], + maxRs[toLayer], + resolutions[fromLayer], + radii[toLayer] - radii[fromLayer], + linkMSAngles[linkId]); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[linkId].get()); + thrust::exclusive_scan(nosync_policy, trackletsLUTsHost[linkId], trackletsLUTsHost[linkId] + nClusters[fromLayer] + 1, trackletsLUTsHost[linkId]); +} + +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, + const int linkId, + const int fromLayer, + const int toLayer, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const typename TrackingTopology::View topology, + bounded_vector& linkPhiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& linkMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams) +{ + gpu::computeLayerTrackletsMultiROFKernel<<>>( + utils, + rofMask, + linkId, + topology, + rofOverlaps, + vertexLUT, + vertices, + rofPV, + vertexId, + clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + tracklets, + trackletsLUTs, + selectUPCVertices, + NSigmaCut, + linkPhiCuts[linkId], + resolutionPV, + minRs[toLayer], + maxRs[toLayer], + resolutions[fromLayer], + radii[toLayer] - radii[fromLayer], + linkMSAngles[linkId]); + thrust::device_ptr tracklets_ptr(spanTracklets[linkId]); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[linkId].get()); + thrust::sort(nosync_policy, tracklets_ptr, tracklets_ptr + nTracklets[linkId]); + auto unique_end = thrust::unique(nosync_policy, tracklets_ptr, tracklets_ptr + nTracklets[linkId]); + nTracklets[linkId] = unique_end - tracklets_ptr; + if (fromLayer > 0) { + GPUChkErrS(cudaMemsetAsync(trackletsLUTsHost[linkId], 0, (nClusters[fromLayer] + 1) * sizeof(int), streams[linkId].get())); + gpu::compileTrackletsLookupTableKernel<<>>( + spanTracklets[linkId], + trackletsLUTsHost[linkId], + nTracklets[linkId]); + thrust::exclusive_scan(nosync_policy, trackletsLUTsHost[linkId], trackletsLUTsHost[linkId] + nClusters[fromLayer] + 1, trackletsLUTsHost[linkId]); + } +} + +template +void countCellsHandler( + const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const typename TrackingTopology::View topology, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams) +{ + thrust::device_vector layerxX0(layerxX0Host); + gpu::computeLayerCellsKernel<<>>( + sortedClusters, // const Cluster** + unsortedClusters, // const Cluster** + tfInfo, // const TrackingFrameInfo** + tracklets, // const Tracklets** + trackletsLUT, // const int** + nTracklets, // const int + cellTopologyId, // const int + topology, + cells, // CellSeed* + cellsLUTsArrayDevice, // int** + thrust::raw_pointer_cast(&layerxX0[0]), + bz, // const float + maxChi2ClusterAttachment, // const float + cellDeltaTanLambdaSigma, // const float + nSigmaCut); // const float + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[cellTopologyId].get()); + thrust::exclusive_scan(nosync_policy, cellsLUTsHost, cellsLUTsHost + nTracklets + 1, cellsLUTsHost); +} + +template +void computeCellsHandler( + const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const typename TrackingTopology::View topology, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + gpu::Streams& streams) +{ + thrust::device_vector layerxX0(layerxX0Host); + gpu::computeLayerCellsKernel<<>>( + sortedClusters, // const Cluster** + unsortedClusters, // const Cluster** + tfInfo, // const TrackingFrameInfo** + tracklets, // const Tracklets** + trackletsLUT, // const int** + nTracklets, // const int + cellTopologyId, // const int + topology, + cells, // CellSeed* + cellsLUTsArrayDevice, // int** + thrust::raw_pointer_cast(&layerxX0[0]), + bz, // const float + maxChi2ClusterAttachment, // const float + cellDeltaTanLambdaSigma, // const float + nSigmaCut); // const float +} + +template +void countCellNeighboursHandler(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream) +{ + gpu::computeLayerCellNeighboursKernel<<>>( + cellsLayersDevice, + neighboursCursor, + cellsLUTs, + nullptr, + sourceCellTopologyId, + targetCellTopologyId, + maxChi2ClusterAttachment, + bz, + nCells); +} + +void scanCellNeighboursHandler(int* neighboursCursor, + int* neighboursLUT, + const unsigned int nCells, + o2::its::ExternalAllocator* alloc, + gpu::Stream& stream) +{ + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(stream.get()); + thrust::exclusive_scan(nosync_policy, neighboursCursor, neighboursCursor + nCells + 1, neighboursCursor); + GPUChkErrS(cudaMemcpyAsync(neighboursLUT, neighboursCursor, (nCells + 1) * sizeof(int), cudaMemcpyDeviceToDevice, stream.get())); +} + +template +void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream) +{ + gpu::computeLayerCellNeighboursKernel<<>>( + cellsLayersDevice, + neighboursCursor, + cellsLUTs, + cellNeighbours, + sourceCellTopologyId, + targetCellTopologyId, + maxChi2ClusterAttachment, + bz, + nCells); +} + +int filterCellNeighboursHandler(gpuPair* cellNeighbourPairs, + int* cellNeighbours, + unsigned int nNeigh, + gpu::Stream& stream, + o2::its::ExternalAllocator* allocator) +{ + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(allocator)).on(stream.get()); + thrust::device_ptr> neighVectorPairs(cellNeighbourPairs); + thrust::device_ptr validNeighs(cellNeighbours); + auto updatedEnd = thrust::remove_if(nosync_policy, neighVectorPairs, neighVectorPairs + nNeigh, gpu::is_invalid_pair()); + size_t newSize = updatedEnd - neighVectorPairs; + thrust::stable_sort(nosync_policy, neighVectorPairs, neighVectorPairs + newSize, gpu::sort_by_second()); + thrust::transform(nosync_policy, neighVectorPairs, neighVectorPairs + newSize, validNeighs, gpu::pair_to_first()); + return newSize; +} + +template +void processNeighboursHandler(const int startLevel, + const int defaultCellTopologyId, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const int* currentCellTopologyIds, + const int* currentCellIds, + const int* nCells, + const unsigned char** usedClusters, + CellNeighbour** neighbours, + int** neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + bounded_vector>& seedsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int maxHoles, + const int minSeedingClusters, + const LayerMask holeLayerMask, + const LayerMask nonSeedingLayerMask, + const std::vector& layerxX0Host, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc) +{ + constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); + alloc->pushTagOnStack(Tag); + auto allocInt = gpu::TypedAllocator(alloc); + auto allocTrackSeed = gpu::TypedAllocator>(alloc); + thrust::device_vector layerxX0(layerxX0Host); + thrust::device_vector> foundSeedsTable(nCells[defaultCellTopologyId] + 1, 0, allocInt); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); + + gpu::processNeighboursKernel<<>>( + defaultCellTopologyId, + startLevel, + allCellSeeds, + currentCellSeeds, + nullptr, + nullptr, + nCells[defaultCellTopologyId], + nullptr, + nullptr, + nullptr, + thrust::raw_pointer_cast(&foundSeedsTable[0]), + usedClusters, + neighbours, + neighboursDeviceLUTs, + foundTrackingFrameInfo, + thrust::raw_pointer_cast(&layerxX0[0]), + bz, + maxChi2ClusterAttachment, + propagator, + matCorrType); + thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + + thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); + thrust::device_vector> updatedCellTopologyId(foundSeedsTable.back(), 0, allocInt); + thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocTrackSeed); + gpu::processNeighboursKernel<<>>( + defaultCellTopologyId, + startLevel, + allCellSeeds, + currentCellSeeds, + nullptr, + nullptr, + nCells[defaultCellTopologyId], + thrust::raw_pointer_cast(&updatedCellSeed[0]), + thrust::raw_pointer_cast(&updatedCellId[0]), + thrust::raw_pointer_cast(&updatedCellTopologyId[0]), + thrust::raw_pointer_cast(&foundSeedsTable[0]), + usedClusters, + neighbours, + neighboursDeviceLUTs, + foundTrackingFrameInfo, + thrust::raw_pointer_cast(&layerxX0[0]), + bz, + maxChi2ClusterAttachment, + propagator, + matCorrType); + GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); + + int level = startLevel; + thrust::device_vector> lastCellId(allocInt); + thrust::device_vector> lastCellTopologyId(allocInt); + thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocTrackSeed); + while (level > 2 && !updatedCellSeed.empty()) { + lastCellSeed.swap(updatedCellSeed); + lastCellId.swap(updatedCellId); + lastCellTopologyId.swap(updatedCellTopologyId); + thrust::device_vector, gpu::TypedAllocator>>(allocTrackSeed).swap(updatedCellSeed); + thrust::device_vector>(allocInt).swap(updatedCellId); + thrust::device_vector>(allocInt).swap(updatedCellTopologyId); + auto lastCellSeedSize{lastCellSeed.size()}; + foundSeedsTable.resize(lastCellSeedSize + 1); + thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); + + --level; + gpu::processNeighboursKernel><<>>( + constants::UnusedIndex, + level, + allCellSeeds, + thrust::raw_pointer_cast(&lastCellSeed[0]), + thrust::raw_pointer_cast(&lastCellId[0]), + thrust::raw_pointer_cast(&lastCellTopologyId[0]), + lastCellSeedSize, + nullptr, + nullptr, + nullptr, + thrust::raw_pointer_cast(&foundSeedsTable[0]), + usedClusters, + neighbours, + neighboursDeviceLUTs, + foundTrackingFrameInfo, + thrust::raw_pointer_cast(&layerxX0[0]), + bz, + maxChi2ClusterAttachment, + propagator, + matCorrType); + thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + + auto foundSeeds{foundSeedsTable.back()}; + updatedCellId.resize(foundSeeds); + thrust::fill(nosync_policy, updatedCellId.begin(), updatedCellId.end(), 0); + updatedCellTopologyId.resize(foundSeeds); + thrust::fill(nosync_policy, updatedCellTopologyId.begin(), updatedCellTopologyId.end(), 0); + updatedCellSeed.resize(foundSeeds); + thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), TrackSeed()); + + gpu::processNeighboursKernel><<>>( + constants::UnusedIndex, + level, + allCellSeeds, + thrust::raw_pointer_cast(&lastCellSeed[0]), + thrust::raw_pointer_cast(&lastCellId[0]), + thrust::raw_pointer_cast(&lastCellTopologyId[0]), + lastCellSeedSize, + thrust::raw_pointer_cast(&updatedCellSeed[0]), + thrust::raw_pointer_cast(&updatedCellId[0]), + thrust::raw_pointer_cast(&updatedCellTopologyId[0]), + thrust::raw_pointer_cast(&foundSeedsTable[0]), + usedClusters, + neighbours, + neighboursDeviceLUTs, + foundTrackingFrameInfo, + thrust::raw_pointer_cast(&layerxX0[0]), + bz, + maxChi2ClusterAttachment, + propagator, + matCorrType); + } + GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); + thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocTrackSeed); + auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), track::TrackSeedSelector{constants::MaxTrackSeedQ2Pt, maxChi2NDF, startLevel, maxHoles, minSeedingClusters, holeLayerMask, nonSeedingLayerMask}); + auto s{end - outSeeds.begin()}; + seedsHost.reserve(seedsHost.size() + s); + thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); + alloc->popTagOffStack(Tag); +} + +template +void countTrackSeedHandler(TrackSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc) +{ + // TODO: the minPts&layerRadii is transfered twice + // we should allocate this in constant memory and stop these + // small transferes! + thrust::device_vector minPts(minPtsHost); + thrust::device_vector layerRadii(layerRadiiHost); + thrust::device_vector layerxX0(layerxX0Host); + gpu::countTrackSeedsKernel<<>>( + trackSeeds, // CellSeed* + foundTrackingFrameInfo, // TrackingFrameInfo** + unsortedClusters, // Cluster** + seedLUT, // int* + thrust::raw_pointer_cast(&layerRadii[0]), // const float* + thrust::raw_pointer_cast(&minPts[0]), // const float* + thrust::raw_pointer_cast(&layerxX0[0]), // const float* + nSeeds, // const unsigned int + bz, // const float + maxChi2ClusterAttachment, // float + maxChi2NDF, // float + reseedIfShorter, // int + repeatRefitOut, // bool + shiftRefToCluster, // bool + propagator, // const o2::base::Propagator* + matCorrType); // o2::base::PropagatorF::MatCorrType + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); + thrust::exclusive_scan(sync_policy, seedLUT, seedLUT + nSeeds + 1, seedLUT); +} + +template +void computeTrackSeedHandler(TrackSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, + const typename ROFOverlapTable::View& rofOverlaps, + const Cluster** clusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + const int** ROFClusters, + o2::its::TrackITSExt* tracks, + int* trackIndices, + const int* seedLUT, + TrackExtensionHypothesis* activeHypotheses, + TrackExtensionHypothesis* nextHypotheses, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const int nLayers, + const int phiBins, + const int maxHypotheses, + const bool extendTop, + const bool extendBot, + const float nSigmaCutPhi, + const float nSigmaCutZ, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc) +{ + thrust::device_vector minPts(minPtsHost); + thrust::device_vector layerRadii(layerRadiiHost); + thrust::device_vector layerxX0(layerxX0Host); + gpu::fitTrackSeedsKernel<<>>( + trackSeeds, // CellSeed* + foundTrackingFrameInfo, // TrackingFrameInfo** + unsortedClusters, // Cluster** + utils, // IndexTableUtils* + rofMask, // ROFMaskTable::View + rofOverlaps, // ROFOverlapTable::View + clusters, // Cluster** + usedClusters, // unsigned char** + clustersIndexTables, // int** + ROFClusters, // int** + tracks, // TrackITSExt* + seedLUT, // const int* + activeHypotheses, // TrackExtensionHypothesis* + nextHypotheses, // TrackExtensionHypothesis* + thrust::raw_pointer_cast(&layerRadii[0]), // const float* + thrust::raw_pointer_cast(&minPts[0]), // const float* + thrust::raw_pointer_cast(&layerxX0[0]), // const float* + nSeeds, // const unsigned int + bz, // const float + maxChi2ClusterAttachment, // float + maxChi2NDF, // float + reseedIfShorter, // int + repeatRefitOut, // bool + shiftRefToCluster, // bool + nLayers, // int + phiBins, // int + maxHypotheses, // int + extendTop, // bool + extendBot, // bool + nSigmaCutPhi, // float + nSigmaCutZ, // float + propagator, // const o2::base::Propagator* + matCorrType); // o2::base::PropagatorF::MatCorrType + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); + thrust::device_ptr trackIndicesPtr(trackIndices); + thrust::sequence(sync_policy, trackIndicesPtr, trackIndicesPtr + nTracks); + thrust::sort(sync_policy, trackIndicesPtr, trackIndicesPtr + nTracks, gpu::compare_track_index_chi2{tracks}); +} + +/// Explicit instantiation of ITS2 handlers +template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, + const ROFMaskTable<7>::View& rofMask, + const int linkId, + const int fromLayer, + const int toLayer, + const ROFOverlapTable<7>::View& rofOverlaps, + const ROFVertexLookupTable<7>::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const TrackingTopology<7>::View topology, + bounded_vector& linkPhiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& linkMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, + const ROFMaskTable<7>::View& rofMask, + const int linkId, + const int fromLayer, + const int toLayer, + const ROFOverlapTable<7>::View& rofOverlaps, + const ROFVertexLookupTable<7>::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const TrackingTopology<7>::View topology, + bounded_vector& linkPhiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& linkMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void countCellsHandler<7>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const TrackingTopology<7>::View topology, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void computeCellsHandler<7>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const TrackingTopology<7>::View topology, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + gpu::Streams& streams); + +template void countCellNeighboursHandler<7>(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream); + +template void computeCellNeighboursHandler<7>(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream); + +template void processNeighboursHandler<7>(const int startLevel, + const int defaultCellTopologyId, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const int* currentCellTopologyIds, + const int* currentCellIds, + const int* nCells, + const unsigned char** usedClusters, + CellNeighbour** neighbours, + int** neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + bounded_vector>& seedsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int maxHoles, + const int minSeedingClusters, + const LayerMask holeLayerMask, + const LayerMask nonSeedingLayerMask, + const std::vector& layerxX0Host, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void countTrackSeedHandler(TrackSeed<7>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void computeTrackSeedHandler(TrackSeed<7>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + const IndexTableUtils<7>* utils, + const ROFMaskTable<7>::View& rofMask, + const ROFOverlapTable<7>::View& rofOverlaps, + const Cluster** clusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + const int** ROFClusters, + o2::its::TrackITSExt* tracks, + int* trackIndices, + const int* seedLUT, + TrackExtensionHypothesis<7>* activeHypotheses, + TrackExtensionHypothesis<7>* nextHypotheses, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const int nLayers, + const int phiBins, + const int maxHypotheses, + const bool extendTop, + const bool extendBot, + const float nSigmaCutPhi, + const float nSigmaCutZ, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +/// Explicit instantiation of ALICE3 handlers +#ifdef ENABLE_UPGRADES +template void countTrackletsInROFsHandler<11>(const IndexTableUtils<11>* utils, + const ROFMaskTable<11>::View& rofMask, + const int linkId, + const int fromLayer, + const int toLayer, + const ROFOverlapTable<11>::View& rofOverlaps, + const ROFVertexLookupTable<11>::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const TrackingTopology<11>::View topology, + bounded_vector& linkPhiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& linkMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void computeTrackletsInROFsHandler<11>(const IndexTableUtils<11>* utils, + const ROFMaskTable<11>::View& rofMask, + const int linkId, + const int fromLayer, + const int toLayer, + const ROFOverlapTable<11>::View& rofOverlaps, + const ROFVertexLookupTable<11>::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const TrackingTopology<11>::View topology, + bounded_vector& linkPhiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& linkMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void countCellsHandler<11>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const TrackingTopology<11>::View topology, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void computeCellsHandler<11>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const TrackingTopology<11>::View topology, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + gpu::Streams& streams); + +template void countCellNeighboursHandler<11>(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream); + +template void computeCellNeighboursHandler<11>(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream); + +template void processNeighboursHandler<11>(const int startLevel, + const int defaultCellTopologyId, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const int* currentCellTopologyIds, + const int* currentCellIds, + const int* nCells, + const unsigned char** usedClusters, + CellNeighbour** neighbours, + int** neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + bounded_vector>& seedsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int maxHoles, + const int minSeedingClusters, + const LayerMask holeLayerMask, + const LayerMask nonSeedingLayerMask, + const std::vector& layerxX0Host, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void countTrackSeedHandler(TrackSeed<11>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void computeTrackSeedHandler(TrackSeed<11>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + const IndexTableUtils<11>* utils, + const ROFMaskTable<11>::View& rofMask, + const ROFOverlapTable<11>::View& rofOverlaps, + const Cluster** clusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + const int** ROFClusters, + o2::its::TrackITSExt* tracks, + int* trackIndices, + const int* seedLUT, + TrackExtensionHypothesis<11>* activeHypotheses, + TrackExtensionHypothesis<11>* nextHypotheses, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const int nLayers, + const int phiBins, + const int maxHypotheses, + const bool extendTop, + const bool extendBot, + const float nSigmaCutPhi, + const float nSigmaCutZ, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void countTrackletsInROFsHandler<13>(const IndexTableUtils<13>* utils, + const ROFMaskTable<13>::View& rofMask, + const int linkId, + const int fromLayer, + const int toLayer, + const ROFOverlapTable<13>::View& rofOverlaps, + const ROFVertexLookupTable<13>::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const TrackingTopology<13>::View topology, + bounded_vector& linkPhiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& linkMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void computeTrackletsInROFsHandler<13>(const IndexTableUtils<13>* utils, + const ROFMaskTable<13>::View& rofMask, + const int linkId, + const int fromLayer, + const int toLayer, + const ROFOverlapTable<13>::View& rofOverlaps, + const ROFVertexLookupTable<13>::View& vertexLUT, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const bool selectUPCVertices, + const float NSigmaCut, + const TrackingTopology<13>::View topology, + bounded_vector& linkPhiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& linkMSAngles, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void countCellsHandler<13>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const TrackingTopology<13>::View topology, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + o2::its::ExternalAllocator* alloc, + gpu::Streams& streams); + +template void computeCellsHandler<13>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int cellTopologyId, + const TrackingTopology<13>::View topology, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const std::vector& layerxX0Host, + gpu::Streams& streams); + +template void countCellNeighboursHandler<13>(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream); + +template void computeCellNeighboursHandler<13>(CellSeed** cellsLayersDevice, + int* neighboursCursor, + int** cellsLUTs, + CellNeighbour* cellNeighbours, + const int sourceCellTopologyId, + const int targetCellTopologyId, + const float maxChi2ClusterAttachment, + const float bz, + const unsigned int nCells, + gpu::Stream& stream); + +template void processNeighboursHandler<13>(const int startLevel, + const int defaultCellTopologyId, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const int* currentCellTopologyIds, + const int* currentCellIds, + const int* nCells, + const unsigned char** usedClusters, + CellNeighbour** neighbours, + int** neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + bounded_vector>& seedsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int maxHoles, + const int minSeedingClusters, + const LayerMask holeLayerMask, + const LayerMask nonSeedingLayerMask, + const std::vector& layerxX0Host, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void countTrackSeedHandler(TrackSeed<13>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); + +template void computeTrackSeedHandler(TrackSeed<13>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + const IndexTableUtils<13>* utils, + const ROFMaskTable<13>::View& rofMask, + const ROFOverlapTable<13>::View& rofOverlaps, + const Cluster** clusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + const int** ROFClusters, + o2::its::TrackITSExt* tracks, + int* trackIndices, + const int* seedLUT, + TrackExtensionHypothesis<13>* activeHypotheses, + TrackExtensionHypothesis<13>* nextHypotheses, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const std::vector& layerxX0Host, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const int nLayers, + const int phiBins, + const int maxHypotheses, + const bool extendTop, + const bool extendBot, + const float nSigmaCutPhi, + const float nSigmaCutZ, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc); +#endif +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Utils.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Utils.cu deleted file mode 100644 index 75b17c815b9a4..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Utils.cu +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITStrackingGPU/Utils.h" -#include "ITStrackingGPU/Context.h" -#include "ITStracking/Constants.h" - -#include -#include -#include -#include -#include -#include - -namespace -{ - -int roundUp(const int numToRound, const int multiple) -{ - if (multiple == 0) { - return numToRound; - } - - int remainder{numToRound % multiple}; - if (remainder == 0) { - return numToRound; - } - return numToRound + multiple - remainder; -} - -int findNearestDivisor(const int numToRound, const int divisor) -{ - - if (numToRound > divisor) { - return divisor; - } - - int result = numToRound; - while (divisor % result != 0) { - ++result; - } - return result; -} - -} // namespace - -namespace o2 -{ -namespace its -{ -using constants::GB; -namespace gpu -{ - -GPUh() void gpuThrowOnError() -{ - cudaError_t error = cudaGetLastError(); - - if (error != cudaSuccess) { - std::ostringstream errorString{}; - errorString << GPU_ARCH << " API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" << std::endl; - throw std::runtime_error{errorString.str()}; - } -} - -double bytesToconfig(size_t s) { return (double)s / (1024.0); } -double bytesToGB(size_t s) { return (double)s / GB; } - -void utils::checkGPUError(const cudaError_t error, const char* file, const int line) -{ - if (error != cudaSuccess) { - std::ostringstream errorString{}; - errorString << file << ":" << line << std::endl - << GPU_ARCH << " API returned error [" << cudaGetErrorString(error) << "] (code " - << error << ")" << std::endl; - throw std::runtime_error{errorString.str()}; - } -} - -void utils::getDeviceProp(int deviceId, bool print) -{ - const int w1 = 34; - std::cout << std::left; - std::cout << std::setw(w1) - << "--------------------------------------------------------------------------------" - << std::endl; - std::cout << std::setw(w1) << "device#" << deviceId << std::endl; - - cudaDeviceProp props; - checkGPUError(cudaGetDeviceProperties(&props, deviceId)); - if (print) { - std::cout << std::setw(w1) << "Name: " << props.name << std::endl; - std::cout << std::setw(w1) << "pciBusID: " << props.pciBusID << std::endl; - std::cout << std::setw(w1) << "pciDeviceID: " << props.pciDeviceID << std::endl; - std::cout << std::setw(w1) << "pciDomainID: " << props.pciDomainID << std::endl; - std::cout << std::setw(w1) << "multiProcessorCount: " << props.multiProcessorCount << std::endl; - std::cout << std::setw(w1) << "maxThreadsPerMultiProcessor: " << props.maxThreadsPerMultiProcessor - << std::endl; - std::cout << std::setw(w1) << "isMultiGpuBoard: " << props.isMultiGpuBoard << std::endl; - std::cout << std::setw(w1) << "clockRate: " << (float)props.clockRate / 1000.0 << " Mhz" << std::endl; - std::cout << std::setw(w1) << "memoryClockRate: " << (float)props.memoryClockRate / 1000.0 << " Mhz" - << std::endl; - std::cout << std::setw(w1) << "memoryBusWidth: " << props.memoryBusWidth << std::endl; - std::cout << std::setw(w1) << "clockInstructionRate: " << (float)props.clockRate / 1000.0 - << " Mhz" << std::endl; - std::cout << std::setw(w1) << "totalGlobalMem: " << std::fixed << std::setprecision(2) - << bytesToGB(props.totalGlobalMem) << " GB" << std::endl; -#if !defined(__CUDACC__) - std::cout << std::setw(w1) << "maxSharedMemoryPerMultiProcessor: " << std::fixed << std::setprecision(2) - << bytesToconfig(props.sharedMemPerMultiprocessor) << " config" << std::endl; -#endif -#if defined(__HIPCC__) - std::cout << std::setw(w1) << "maxSharedMemoryPerMultiProcessor: " << std::fixed << std::setprecision(2) - << bytesToconfig(props.maxSharedMemoryPerMultiProcessor) << " config" << std::endl; -#endif - std::cout << std::setw(w1) << "totalConstMem: " << props.totalConstMem << std::endl; - std::cout << std::setw(w1) << "sharedMemPerBlock: " << (float)props.sharedMemPerBlock / 1024.0 << " config" - << std::endl; - std::cout << std::setw(w1) << "canMapHostMemory: " << props.canMapHostMemory << std::endl; - std::cout << std::setw(w1) << "regsPerBlock: " << props.regsPerBlock << std::endl; - std::cout << std::setw(w1) << "warpSize: " << props.warpSize << std::endl; - std::cout << std::setw(w1) << "l2CacheSize: " << props.l2CacheSize << std::endl; - std::cout << std::setw(w1) << "computeMode: " << props.computeMode << std::endl; - std::cout << std::setw(w1) << "maxThreadsPerBlock: " << props.maxThreadsPerBlock << std::endl; - std::cout << std::setw(w1) << "maxThreadsDim.x: " << props.maxThreadsDim[0] << std::endl; - std::cout << std::setw(w1) << "maxThreadsDim.y: " << props.maxThreadsDim[1] << std::endl; - std::cout << std::setw(w1) << "maxThreadsDim.z: " << props.maxThreadsDim[2] << std::endl; - std::cout << std::setw(w1) << "maxGridSize.x: " << props.maxGridSize[0] << std::endl; - std::cout << std::setw(w1) << "maxGridSize.y: " << props.maxGridSize[1] << std::endl; - std::cout << std::setw(w1) << "maxGridSize.z: " << props.maxGridSize[2] << std::endl; - std::cout << std::setw(w1) << "major: " << props.major << std::endl; - std::cout << std::setw(w1) << "minor: " << props.minor << std::endl; - std::cout << std::setw(w1) << "concurrentKernels: " << props.concurrentKernels << std::endl; - std::cout << std::setw(w1) << "cooperativeLaunch: " << props.cooperativeLaunch << std::endl; - std::cout << std::setw(w1) << "cooperativeMultiDeviceLaunch: " << props.cooperativeMultiDeviceLaunch << std::endl; -#if defined(__HIPCC__) - std::cout << std::setw(w1) << "arch.hasGlobalInt32Atomics: " << props.arch.hasGlobalInt32Atomics << std::endl; - std::cout << std::setw(w1) << "arch.hasGlobalFloatAtomicExch: " << props.arch.hasGlobalFloatAtomicExch - << std::endl; - std::cout << std::setw(w1) << "arch.hasSharedInt32Atomics: " << props.arch.hasSharedInt32Atomics << std::endl; - std::cout << std::setw(w1) << "arch.hasSharedFloatAtomicExch: " << props.arch.hasSharedFloatAtomicExch - << std::endl; - std::cout << std::setw(w1) << "arch.hasFloatAtomicAdd: " << props.arch.hasFloatAtomicAdd << std::endl; - std::cout << std::setw(w1) << "arch.hasGlobalInt64Atomics: " << props.arch.hasGlobalInt64Atomics << std::endl; - std::cout << std::setw(w1) << "arch.hasSharedInt64Atomics: " << props.arch.hasSharedInt64Atomics << std::endl; - std::cout << std::setw(w1) << "arch.hasDoubles: " << props.arch.hasDoubles << std::endl; - std::cout << std::setw(w1) << "arch.hasWarpVote: " << props.arch.hasWarpVote << std::endl; - std::cout << std::setw(w1) << "arch.hasWarpBallot: " << props.arch.hasWarpBallot << std::endl; - std::cout << std::setw(w1) << "arch.hasWarpShuffle: " << props.arch.hasWarpShuffle << std::endl; - std::cout << std::setw(w1) << "arch.hasFunnelShift: " << props.arch.hasFunnelShift << std::endl; - std::cout << std::setw(w1) << "arch.hasThreadFenceSystem: " << props.arch.hasThreadFenceSystem << std::endl; - std::cout << std::setw(w1) << "arch.hasSyncThreadsExt: " << props.arch.hasSyncThreadsExt << std::endl; - std::cout << std::setw(w1) << "arch.hasSurfaceFuncs: " << props.arch.hasSurfaceFuncs << std::endl; - std::cout << std::setw(w1) << "arch.has3dGrid: " << props.arch.has3dGrid << std::endl; - std::cout << std::setw(w1) << "arch.hasDynamicParallelism: " << props.arch.hasDynamicParallelism << std::endl; - std::cout << std::setw(w1) << "gcnArchName: " << props.gcnArchName << std::endl; -#endif - std::cout << std::setw(w1) << "isIntegrated: " << props.integrated << std::endl; - std::cout << std::setw(w1) << "maxTexture1D: " << props.maxTexture1D << std::endl; - std::cout << std::setw(w1) << "maxTexture2D.width: " << props.maxTexture2D[0] << std::endl; - std::cout << std::setw(w1) << "maxTexture2D.height: " << props.maxTexture2D[1] << std::endl; - std::cout << std::setw(w1) << "maxTexture3D.width: " << props.maxTexture3D[0] << std::endl; - std::cout << std::setw(w1) << "maxTexture3D.height: " << props.maxTexture3D[1] << std::endl; - std::cout << std::setw(w1) << "maxTexture3D.depth: " << props.maxTexture3D[2] << std::endl; -#if defined(__HIPCC__) - std::cout << std::setw(w1) << "isLargeBar: " << props.isLargeBar << std::endl; - std::cout << std::setw(w1) << "asicRevision: " << props.asicRevision << std::endl; -#endif - - int deviceCnt; - checkGPUError(cudaGetDeviceCount(&deviceCnt)); - std::cout << std::setw(w1) << "peers: "; - for (int i = 0; i < deviceCnt; i++) { - int isPeer; - checkGPUError(cudaDeviceCanAccessPeer(&isPeer, i, deviceId)); - if (isPeer) { - std::cout << "device#" << i << " "; - } - } - std::cout << std::endl; - std::cout << std::setw(w1) << "non-peers: "; - for (int i = 0; i < deviceCnt; i++) { - int isPeer; - checkGPUError(cudaDeviceCanAccessPeer(&isPeer, i, deviceId)); - if (!isPeer) { - std::cout << "device#" << i << " "; - } - } - std::cout << std::endl; - - size_t free, total; - checkGPUError(cudaMemGetInfo(&free, &total)); - - std::cout << std::fixed << std::setprecision(2); - std::cout << std::setw(w1) << "memInfo.total: " << bytesToGB(total) << " GB" << std::endl; - std::cout << std::setw(w1) << "memInfo.free: " << bytesToGB(free) << " GB (" << std::setprecision(0) - << (float)free / total * 100.0 << "%)" << std::endl; - } -} - -dim3 utils::getBlockSize(const int colsNum) -{ - return getBlockSize(colsNum, 1); -} - -dim3 utils::getBlockSize(const int colsNum, const int rowsNum) -{ - const DeviceProperties& deviceProperties = Context::getInstance().getDeviceProperties(); - return getBlockSize(colsNum, rowsNum, deviceProperties.gpuCores / deviceProperties.maxBlocksPerSM); -} - -dim3 utils::getBlockSize(const int colsNum, const int rowsNum, const int maxThreadsPerBlock) -{ - const DeviceProperties& deviceProperties = Context::getInstance().getDeviceProperties(); - int xThreads = max(min(colsNum, deviceProperties.maxThreadsDim.x), 1); - int yThreads = max(min(rowsNum, deviceProperties.maxThreadsDim.y), 1); - const int totalThreads = roundUp(min(xThreads * yThreads, maxThreadsPerBlock), - deviceProperties.warpSize); - - if (xThreads > yThreads) { - - xThreads = findNearestDivisor(xThreads, totalThreads); - yThreads = totalThreads / xThreads; - - } else { - - yThreads = findNearestDivisor(yThreads, totalThreads); - xThreads = totalThreads / yThreads; - } - - return dim3{static_cast(xThreads), static_cast(yThreads)}; -} - -dim3 utils::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum) -{ - return getBlocksGrid(threadsPerBlock, rowsNum, 1); -} - -dim3 utils::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum, const int colsNum) -{ - - return dim3{1 + (rowsNum - 1) / threadsPerBlock.x, 1 + (colsNum - 1) / threadsPerBlock.y}; -} - -void utils::gpuMalloc(void** p, const int size) -{ - checkGPUError(cudaMalloc(p, size), __FILE__, __LINE__); -} - -void utils::gpuFree(void* p) -{ - checkGPUError(cudaFree(p), __FILE__, __LINE__); -} - -void utils::gpuMemset(void* p, int value, int size) -{ - checkGPUError(cudaMemset(p, value, size), __FILE__, __LINE__); -} - -void utils::gpuMemcpyHostToDevice(void* dst, const void* src, int size) -{ - checkGPUError(cudaMemcpy(dst, src, size, cudaMemcpyHostToDevice), __FILE__, __LINE__); -} - -void utils::gpuMemcpyDeviceToHost(void* dst, const void* src, int size) -{ - checkGPUError(cudaMemcpy(dst, src, size, cudaMemcpyDeviceToHost), __FILE__, __LINE__); -} - -void utils::gpuMemcpyToSymbol(const void* symbol, const void* src, int size) -{ - checkGPUError(cudaMemcpyToSymbol(symbol, src, size, 0, cudaMemcpyHostToDevice), __FILE__, __LINE__); -} - -void utils::gpuMemcpyFromSymbol(void* dst, const void* symbol, int size) -{ - checkGPUError(cudaMemcpyFromSymbol(dst, symbol, size, 0, cudaMemcpyDeviceToHost), __FILE__, __LINE__); -} - -GPUd() int utils::getLaneIndex() -{ - uint32_t laneIndex; - asm volatile("mov.u32 %0, %%laneid;" - : "=r"(laneIndex)); - return static_cast(laneIndex); -} -} // namespace gpu -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cu deleted file mode 100644 index cefad5117ff9c..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cu +++ /dev/null @@ -1,781 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -/// \author matteo.concas@cern.ch - -#include -#include -#include -#include -#include -#include - -#ifndef GPUCA_GPUCODE_GENRTC -#include -#endif - -#include "ITStracking/MathUtils.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/ClusterLines.h" -#include "ITStracking/Tracklet.h" - -#include "ITStrackingGPU/Utils.h" -#include "ITStrackingGPU/ClusterLinesGPU.h" -#include "ITStrackingGPU/VertexerTraitsGPU.h" - -#include "GPUCommonArray.h" - -#ifdef VTX_DEBUG -#include "TTree.h" -#include "TFile.h" -#endif - -#include "ITStrackingGPU/TracerGPU.h" - -namespace o2 -{ -namespace its -{ -using constants::its::VertexerHistogramVolume; -using constants::math::TwoPi; -using gpu::utils::checkGPUError; -using math_utils::getNormalizedPhi; - -using namespace constants::its2; -GPUd() const int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, - const float z1, float maxdeltaz, float maxdeltaphi) -{ - const float zRangeMin = z1 - maxdeltaz; - const float phiRangeMin = currentCluster.phi - maxdeltaphi; - const float zRangeMax = z1 + maxdeltaz; - const float phiRangeMax = currentCluster.phi + maxdeltaphi; - - if (zRangeMax < -LayersZCoordinate()[layerIndex + 1] || - zRangeMin > LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { - - return getEmptyBinsRect(); - } - - return int4{o2::gpu::GPUCommonMath::Max(0, getZBinIndex(layerIndex + 1, zRangeMin)), - getPhiBinIndex(phiRangeMin), - o2::gpu::GPUCommonMath::Min(ZBins - 1, getZBinIndex(layerIndex + 1, zRangeMax)), - getPhiBinIndex(phiRangeMax)}; -} - -GPUh() void gpuThrowOnError() -{ - cudaError_t error = cudaGetLastError(); - - if (error != cudaSuccess) { - std::ostringstream errorString{}; - errorString << GPU_ARCH << " API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" << std::endl; - throw std::runtime_error{errorString.str()}; - } -} - -VertexerTraitsGPU::VertexerTraitsGPU() -{ - setIsGPU(true); -} - -VertexerTraitsGPU::~VertexerTraitsGPU() -{ - gpu::utils::gpuFree(mDeviceIndexTableUtils); -} - -void VertexerTraitsGPU::initialise(const TrackingParameters& trackingParams) -{ - mTimeFrameGPU->initialise(0, trackingParams, 3, &mIndexTableUtils, &mTfGPUParams); -} - -namespace gpu -{ - -template -GPUd() void printOnThread(const unsigned int tId, const char* str, Args... args) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - printf(str, args...); - } -} - -template -GPUd() void printOnBlock(const unsigned int bId, const char* str, Args... args) -{ - if (blockIdx.x == bId && threadIdx.x == 0) { - printf(str, args...); - } -} - -GPUg() void printBufferOnThread(const int* v, size_t size, const int len = 150, const unsigned int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - for (int i{0}; i < size; ++i) { - if (!(i % len)) { - printf("\n start: ===>%d/%d\t", i, (int)size); - } - printf("%d\t", v[i]); - } - printf("\n"); - } -} - -GPUg() void printBufferOnThreadF(const float* v, size_t size, const unsigned int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - printf("vector :"); - for (int i{0}; i < size; ++i) { - printf("%.9f\t", v[i]); - } - printf("\n"); - } -} - -GPUg() void resetTrackletsKernel(Tracklet* tracklets, const int nTracklets) -{ - for (int iCurrentLayerClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentLayerClusterIndex < nTracklets; iCurrentLayerClusterIndex += blockDim.x * gridDim.x) { - new (tracklets + iCurrentLayerClusterIndex) Tracklet{}; - } -} - -GPUg() void dumpFoundTrackletsKernel(const Tracklet* tracklets, const int* nTracklet, const size_t nClustersMiddleLayer, const int maxTrackletsPerCluster) -{ - for (int iCurrentLayerClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentLayerClusterIndex < nClustersMiddleLayer; iCurrentLayerClusterIndex += blockDim.x * gridDim.x) { - const int stride{iCurrentLayerClusterIndex * maxTrackletsPerCluster}; - for (int iTracklet{0}; iTracklet < nTracklet[iCurrentLayerClusterIndex]; ++iTracklet) { - auto& t = tracklets[stride + iTracklet]; - t.dump(); - } - } -} - -GPUg() void dumpMaximaKernel(const cub::KeyValuePair* tmpVertexBins, const int threadId) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == threadId) { - printf("XmaxBin: %d at index: %d | YmaxBin: %d at index: %d | ZmaxBin: %d at index: %d\n", - tmpVertexBins[0].value, tmpVertexBins[0].key, - tmpVertexBins[1].value, tmpVertexBins[1].key, - tmpVertexBins[2].value, tmpVertexBins[2].key); - } -} - -template -GPUg() void trackleterKernelSingleRof( - const Cluster* clustersNextLayer, // 0 2 - const Cluster* clustersCurrentLayer, // 1 1 - const int sizeNextLClusters, - const int sizeCurrentLClusters, - const int* indexTableNext, - const float phiCut, - Tracklet* Tracklets, - int* foundTracklets, - const IndexTableUtils* utils, - const int rofId, - const size_t maxTrackletsPerCluster = 1e2) -{ - const int phiBins{utils->getNphiBins()}; - const int zBins{utils->getNzBins()}; - // loop on layer1 clusters - for (int iCurrentLayerClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentLayerClusterIndex < sizeCurrentLClusters; iCurrentLayerClusterIndex += blockDim.x * gridDim.x) { - if (iCurrentLayerClusterIndex < sizeCurrentLClusters) { - unsigned int storedTracklets{0}; - const size_t stride{iCurrentLayerClusterIndex * maxTrackletsPerCluster}; - const Cluster& currentCluster = clustersCurrentLayer[iCurrentLayerClusterIndex]; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, (int)Mode, 0.f, 50.f, phiCut / 2, *utils)}; - if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += phiBins; - } - // loop on phi bins next layer - for (unsigned int iPhiBin{(unsigned int)selectedBinsRect.y}, iPhiCount{0}; iPhiCount < (unsigned int)phiBinsNum; iPhiBin = ++iPhiBin == phiBins ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int firstRowClusterIndex{indexTableNext[firstBinIndex]}; - const int maxRowClusterIndex{indexTableNext[firstBinIndex + zBins]}; - // loop on clusters next layer - for (int iNextLayerClusterIndex{firstRowClusterIndex}; iNextLayerClusterIndex < maxRowClusterIndex && iNextLayerClusterIndex < sizeNextLClusters; ++iNextLayerClusterIndex) { - const Cluster& nextCluster = clustersNextLayer[iNextLayerClusterIndex]; - if (o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi) < phiCut) { - if (storedTracklets < maxTrackletsPerCluster) { - if constexpr (Mode == TrackletMode::Layer0Layer1) { - new (Tracklets + stride + storedTracklets) Tracklet{iNextLayerClusterIndex, iCurrentLayerClusterIndex, nextCluster, currentCluster, rofId, rofId}; - } else { - new (Tracklets + stride + storedTracklets) Tracklet{iCurrentLayerClusterIndex, iNextLayerClusterIndex, currentCluster, nextCluster, rofId, rofId}; - } - ++storedTracklets; - } - } - } - } - } - foundTracklets[iCurrentLayerClusterIndex] = storedTracklets; - if (storedTracklets >= maxTrackletsPerCluster) { - printf("gpu tracklet finder: some lines will be left behind for cluster %d. valid: %u max: %zu\n", iCurrentLayerClusterIndex, storedTracklets, maxTrackletsPerCluster); - } - } - } -} - -template -GPUg() void trackleterKernelMultipleRof( - const Cluster* clustersNextLayer, // 0 2 - const Cluster* clustersCurrentLayer, // 1 1 - const int* sizeNextLClusters, - const int* sizeCurrentLClusters, - const int* nextIndexTables, - Tracklet* Tracklets, - int* foundTracklets, - const IndexTableUtils* utils, - const unsigned int startRofId, - const unsigned int rofSize, - const float phiCut, - const size_t maxTrackletsPerCluster = 1e2) -{ - const int phiBins{utils->getNphiBins()}; - const int zBins{utils->getNzBins()}; - for (unsigned int iRof{blockIdx.x}; iRof < rofSize; iRof += gridDim.x) { - auto rof = iRof + startRofId; - auto* clustersNextLayerRof = clustersNextLayer + (sizeNextLClusters[rof] - sizeNextLClusters[startRofId]); - auto* clustersCurrentLayerRof = clustersCurrentLayer + (sizeCurrentLClusters[rof] - sizeCurrentLClusters[startRofId]); - auto nClustersNextLayerRof = sizeNextLClusters[rof + 1] - sizeNextLClusters[rof]; - auto nClustersCurrentLayerRof = sizeCurrentLClusters[rof + 1] - sizeCurrentLClusters[rof]; - auto* indexTableNextRof = nextIndexTables + iRof * (phiBins * zBins + 1); - auto* TrackletsRof = Tracklets + (sizeCurrentLClusters[rof] - sizeCurrentLClusters[startRofId]) * maxTrackletsPerCluster; - auto* foundTrackletsRof = foundTracklets + (sizeCurrentLClusters[rof] - sizeCurrentLClusters[startRofId]); - - // single rof loop on layer1 clusters - for (int iCurrentLayerClusterIndex = threadIdx.x; iCurrentLayerClusterIndex < nClustersCurrentLayerRof; iCurrentLayerClusterIndex += blockDim.x) { - unsigned int storedTracklets{0}; - const size_t stride{iCurrentLayerClusterIndex * maxTrackletsPerCluster}; - const Cluster& currentCluster = clustersCurrentLayerRof[iCurrentLayerClusterIndex]; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, (int)Mode, 0.f, 50.f, phiCut / 2, *utils)}; - if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += phiBins; - } - // loop on phi bins next layer - for (unsigned int iPhiBin{(unsigned int)selectedBinsRect.y}, iPhiCount{0}; iPhiCount < (unsigned int)phiBinsNum; iPhiBin = ++iPhiBin == phiBins ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int firstRowClusterIndex{indexTableNextRof[firstBinIndex]}; - const int maxRowClusterIndex{indexTableNextRof[firstBinIndex + zBins]}; - // loop on clusters next layer - for (int iNextLayerClusterIndex{firstRowClusterIndex}; iNextLayerClusterIndex < maxRowClusterIndex && iNextLayerClusterIndex < nClustersNextLayerRof; ++iNextLayerClusterIndex) { - const Cluster& nextCluster = clustersNextLayerRof[iNextLayerClusterIndex]; - if (o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi) < phiCut) { - if (storedTracklets < maxTrackletsPerCluster) { - if constexpr (Mode == TrackletMode::Layer0Layer1) { - new (TrackletsRof + stride + storedTracklets) Tracklet{iNextLayerClusterIndex, iCurrentLayerClusterIndex, nextCluster, currentCluster, static_cast(rof), static_cast(rof)}; - } else { - new (TrackletsRof + stride + storedTracklets) Tracklet{iCurrentLayerClusterIndex, iNextLayerClusterIndex, currentCluster, nextCluster, static_cast(rof), static_cast(rof)}; - } - ++storedTracklets; - } - } - } - } - } - foundTrackletsRof[iCurrentLayerClusterIndex] = storedTracklets; - // if (storedTracklets >= maxTrackletsPerCluster && storedTracklets - maxTrackletsPerCluster < 5) { - // printf("gpu tracklet finder: some lines will be left behind for cluster %d in rof: %d. valid: %u max: %lu (suppressing after 5 msgs)\n", iCurrentLayerClusterIndex, rof, storedTracklets, maxTrackletsPerCluster); - // } - } - } -} - -template -GPUg() void trackletSelectionKernelSingleRof( - const Cluster* clusters0, - const Cluster* clusters1, - const size_t nClustersMiddleLayer, - Tracklet* tracklets01, - Tracklet* tracklets12, - const int* nFoundTracklet01, - const int* nFoundTracklet12, - unsigned char* usedTracklets, - Line* lines, - int* nFoundLines, - int* nExclusiveFoundLines, - const int maxTrackletsPerCluster = 1e2, - const float tanLambdaCut = 0.025f, - const float phiCut = 0.002f) -{ - for (int iCurrentLayerClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentLayerClusterIndex < nClustersMiddleLayer; iCurrentLayerClusterIndex += blockDim.x * gridDim.x) { - const int stride{iCurrentLayerClusterIndex * maxTrackletsPerCluster}; - int validTracklets{0}; - for (int iTracklet12{0}; iTracklet12 < nFoundTracklet12[iCurrentLayerClusterIndex]; ++iTracklet12) { - for (int iTracklet01{0}; iTracklet01 < nFoundTracklet01[iCurrentLayerClusterIndex] && validTracklets < maxTrackletsPerCluster; ++iTracklet01) { - const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklets01[stride + iTracklet01].tanLambda - tracklets12[stride + iTracklet12].tanLambda)}; - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(tracklets01[stride + iTracklet01].phi - tracklets12[stride + iTracklet12].phi)}; - if (!usedTracklets[stride + iTracklet01] && deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != maxTrackletsPerCluster) { - usedTracklets[stride + iTracklet01] = true; - if constexpr (!initRun) { - new (lines + nExclusiveFoundLines[iCurrentLayerClusterIndex] + validTracklets) Line{tracklets01[stride + iTracklet01], clusters0, clusters1}; - } - ++validTracklets; - } - } - } - if constexpr (initRun) { - nFoundLines[iCurrentLayerClusterIndex] = validTracklets; - if (validTracklets >= maxTrackletsPerCluster) { - printf("gpu tracklet selection: some lines will be left behind for cluster %d. valid: %d max: %d\n", iCurrentLayerClusterIndex, validTracklets, maxTrackletsPerCluster); - } - } - } -} - -template -GPUg() void trackletSelectionKernelMultipleRof( - const Cluster* clusters0, // Clusters on layer 0 - const Cluster* clusters1, // Clusters on layer 1 - const int* sizeClustersL0, // Number of clusters on layer 0 per ROF - const int* sizeClustersL1, // Number of clusters on layer 1 per ROF - Tracklet* tracklets01, // Tracklets on layer 0-1 - Tracklet* tracklets12, // Tracklets on layer 1-2 - const int* nFoundTracklets01, // Number of tracklets found on layers 0-1 - const int* nFoundTracklets12, // Number of tracklets found on layers 1-2 - unsigned char* usedTracklets, // Used tracklets - Line* lines, // Lines - int* nFoundLines, // Number of found lines - int* nExclusiveFoundLines, // Number of found lines exclusive scan - const unsigned int startRofId, // Starting ROF ID - const unsigned int rofSize, // Number of ROFs to consider - const int maxTrackletsPerCluster = 1e2, // Maximum number of tracklets per cluster - const float tanLambdaCut = 0.025f, // Cut on tan lambda - const float phiCut = 0.002f) // Cut on phi -{ - for (unsigned int iRof{blockIdx.x}; iRof < rofSize; iRof += gridDim.x) { - auto rof = iRof + startRofId; - auto* clustersL0Rof = clusters0 + (sizeClustersL0[rof] - sizeClustersL0[startRofId]); - auto clustersL1offsetRof = sizeClustersL1[rof] - sizeClustersL1[startRofId]; - auto* clustersL1Rof = clusters1 + clustersL1offsetRof; - auto nClustersL1Rof = sizeClustersL1[rof + 1] - sizeClustersL1[rof]; - auto* tracklets01Rof = tracklets01 + clustersL1offsetRof * maxTrackletsPerCluster; - auto* tracklets12Rof = tracklets12 + clustersL1offsetRof * maxTrackletsPerCluster; - auto* foundTracklets01Rof = nFoundTracklets01 + clustersL1offsetRof; - auto* foundTracklets12Rof = nFoundTracklets12 + clustersL1offsetRof; - auto* usedTrackletsRof = usedTracklets + clustersL1offsetRof * maxTrackletsPerCluster; - auto* foundLinesRof = nFoundLines + clustersL1offsetRof; - int* nExclusiveFoundLinesRof = nullptr; - if constexpr (!initRun) { - nExclusiveFoundLinesRof = nExclusiveFoundLines + clustersL1offsetRof; - } - for (int iClusterIndexLayer1 = threadIdx.x; iClusterIndexLayer1 < nClustersL1Rof; iClusterIndexLayer1 += blockDim.x) { - const int stride{iClusterIndexLayer1 * maxTrackletsPerCluster}; - int validTracklets{0}; - for (int iTracklet12{0}; iTracklet12 < foundTracklets12Rof[iClusterIndexLayer1]; ++iTracklet12) { - for (int iTracklet01{0}; iTracklet01 < foundTracklets01Rof[iClusterIndexLayer1] && validTracklets < maxTrackletsPerCluster; ++iTracklet01) { - const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklets01Rof[stride + iTracklet01].tanLambda - tracklets12Rof[stride + iTracklet12].tanLambda)}; - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(tracklets01Rof[stride + iTracklet01].phi - tracklets12Rof[stride + iTracklet12].phi)}; - if (!usedTrackletsRof[stride + iTracklet01] && deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != maxTrackletsPerCluster) { - usedTrackletsRof[stride + iTracklet01] = true; - if constexpr (!initRun) { - new (lines + nExclusiveFoundLinesRof[iClusterIndexLayer1] + validTracklets) Line{tracklets01Rof[stride + iTracklet01], clustersL0Rof, clustersL1Rof}; - } - ++validTracklets; - } - } - } - if constexpr (initRun) { - foundLinesRof[iClusterIndexLayer1] = validTracklets; - // if (validTracklets >= maxTrackletsPerCluster) { - // printf("gpu tracklet selection: some lines will be left behind for cluster %d. valid: %d max: %d\n", iClusterIndexLayer1, validTracklets, maxTrackletsPerCluster); - // } - } - } - } // rof loop -} - -GPUg() void lineClustererMultipleRof( - const int* sizeClustersL1, // Number of clusters on layer 1 per ROF - Line* lines, // Lines - int* nFoundLines, // Number of found lines - int* nExclusiveFoundLines, // Number of found lines exclusive scan - int* clusteredLines, // Clustered lines - const unsigned int startRofId, // Starting ROF ID - const unsigned int rofSize, // Number of ROFs to consider // Number of found lines exclusive scan - const float pairCut) // Selection on line pairs -{ - for (unsigned int iRof{threadIdx.x}; iRof < rofSize; iRof += blockDim.x) { - auto rof = iRof + startRofId; - auto clustersL1offsetRof = sizeClustersL1[rof] - sizeClustersL1[startRofId]; // starting cluster offset for this ROF - auto nClustersL1Rof = sizeClustersL1[rof + 1] - sizeClustersL1[rof]; // number of clusters for this ROF - auto linesOffsetRof = nExclusiveFoundLines[clustersL1offsetRof]; // starting line offset for this ROF - // auto* foundLinesRof = nFoundLines + clustersL1offsetRof; - auto nLinesRof = nExclusiveFoundLines[clustersL1offsetRof + nClustersL1Rof] - linesOffsetRof; - // printf("rof: %d -> %d lines.\n", rof, nLinesRof); - for (int iLine1 = 0; iLine1 < nLinesRof; ++iLine1) { - auto absLine1Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine1; - if (clusteredLines[absLine1Index] > -1) { - continue; - } - for (int iLine2 = iLine1 + 1; iLine2 < nLinesRof; ++iLine2) { - auto absLine2Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine2; - if (clusteredLines[absLine2Index] > -1) { - continue; - } - - if (Line::getDCA(lines[absLine1Index], lines[absLine2Index]) < pairCut) { - ClusterLinesGPU tmpClus{lines[absLine1Index], lines[absLine2Index]}; - float tmpVertex[3]; - tmpVertex[0] = tmpClus.getVertex()[0]; - tmpVertex[1] = tmpClus.getVertex()[1]; - tmpVertex[2] = tmpClus.getVertex()[2]; - if (tmpVertex[0] * tmpVertex[0] + tmpVertex[1] * tmpVertex[1] > 4.f) { // outside the beampipe, skip it - break; - } - clusteredLines[absLine1Index] = iLine1; // We set local index of first line to contribute, so we can retrieve the cluster later - clusteredLines[absLine2Index] = iLine1; - for (int iLine3 = 0; iLine3 < nLinesRof; ++iLine3) { - auto absLine3Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine3; - if (clusteredLines[absLine3Index] > -1) { - continue; - } - if (Line::getDistanceFromPoint(lines[absLine3Index], tmpVertex) < pairCut) { - clusteredLines[absLine3Index] = iLine1; - } - } - break; - } - } - } - } // rof loop -} - -GPUg() void computeCentroidsKernel( - Line* lines, - int* nFoundLines, - int* nExclusiveFoundLines, - const size_t nClustersMiddleLayer, - float* centroids, - const float lowHistX, - const float highHistX, - const float lowHistY, - const float highHistY, - const float pairCut) -{ - const int nLines = nExclusiveFoundLines[nClustersMiddleLayer - 1] + nFoundLines[nClustersMiddleLayer - 1]; - const int maxIterations{nLines * (nLines - 1) / 2}; - for (size_t currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < maxIterations; currentThreadIndex += blockDim.x * gridDim.x) { - int iFirstLine = currentThreadIndex / nLines; - int iSecondLine = currentThreadIndex % nLines; - // All unique pairs - if (iSecondLine <= iFirstLine) { - iFirstLine = nLines - iFirstLine - 2; - iSecondLine = nLines - iSecondLine - 1; - } - if (Line::getDCA(lines[iFirstLine], lines[iSecondLine]) < pairCut) { - ClusterLinesGPU cluster{lines[iFirstLine], lines[iSecondLine]}; - if (cluster.getVertex()[0] * cluster.getVertex()[0] + cluster.getVertex()[1] * cluster.getVertex()[1] < 1.98f * 1.98f) { - // printOnThread(0, "xCentr: %f, yCentr: %f \n", cluster.getVertex()[0], cluster.getVertex()[1]); - centroids[2 * currentThreadIndex] = cluster.getVertex()[0]; - centroids[2 * currentThreadIndex + 1] = cluster.getVertex()[1]; - } else { - // write values outside the histogram boundaries, - // default behaviour is not to have them added to histogram later - // (writing zeroes would be problematic) - centroids[2 * currentThreadIndex] = 2 * lowHistX; - centroids[2 * currentThreadIndex + 1] = 2 * lowHistY; - } - } else { - // write values outside the histogram boundaries, - // default behaviour is not to have them added to histogram later - // (writing zeroes would be problematic) - centroids[2 * currentThreadIndex] = 2 * highHistX; - centroids[2 * currentThreadIndex + 1] = 2 * highHistY; - } - } -} - -GPUg() void computeZCentroidsKernel( - const int nLines, - const cub::KeyValuePair* tmpVtX, - float* beamPosition, - Line* lines, - float* centroids, - const int* histX, // X - const float lowHistX, - const float binSizeHistX, - const int nBinsHistX, - const int* histY, // Y - const float lowHistY, - const float binSizeHistY, - const int nBinsHistY, - const float lowHistZ, // Z - const float pairCut, - const int binOpeningX, - const int binOpeningY) -{ - for (size_t currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < nLines; currentThreadIndex += blockDim.x * gridDim.x) { - if (tmpVtX[0].value || tmpVtX[1].value) { - float tmpX{lowHistX + tmpVtX[0].key * binSizeHistX + binSizeHistX / 2}; - int sumWX{tmpVtX[0].value}; - float wX{tmpX * tmpVtX[0].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVtX[0].key - binOpeningX)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVtX[0].key + binOpeningX + 1, nBinsHistX - 1); ++iBin) { - if (iBin != tmpVtX[0].key) { - wX += (lowHistX + iBin * binSizeHistX + binSizeHistX / 2) * histX[iBin]; - sumWX += histX[iBin]; - } - } - float tmpY{lowHistY + tmpVtX[1].key * binSizeHistY + binSizeHistY / 2}; - int sumWY{tmpVtX[1].value}; - float wY{tmpY * tmpVtX[1].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVtX[1].key - binOpeningY)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVtX[1].key + binOpeningY + 1, nBinsHistY - 1); ++iBin) { - if (iBin != tmpVtX[1].key) { - wY += (lowHistY + iBin * binSizeHistY + binSizeHistY / 2) * histY[iBin]; - sumWY += histY[iBin]; - } - } - beamPosition[0] = wX / sumWX; - beamPosition[1] = wY / sumWY; - float mockBeamPoint1[3] = {beamPosition[0], beamPosition[1], -1}; // get two points laying at different z, to create line object - float mockBeamPoint2[3] = {beamPosition[0], beamPosition[1], 1}; - Line pseudoBeam = {mockBeamPoint1, mockBeamPoint2}; - if (Line::getDCA(lines[currentThreadIndex], pseudoBeam) < pairCut) { - ClusterLinesGPU cluster{lines[currentThreadIndex], pseudoBeam}; - centroids[currentThreadIndex] = cluster.getVertex()[2]; - } else { - centroids[currentThreadIndex] = 2 * lowHistZ; - } - } - } -} - -GPUg() void computeVertexKernel( - cub::KeyValuePair* tmpVertexBins, - int* histZ, // Z - const float lowHistZ, - const float binSizeHistZ, - const int nBinsHistZ, - Vertex* vertices, - float* beamPosition, - const int vertIndex, - const int minContributors, - const int binOpeningZ) -{ - for (size_t currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < binOpeningZ; currentThreadIndex += blockDim.x * gridDim.x) { - if (currentThreadIndex == 0) { - if (tmpVertexBins[2].value > 1 && (tmpVertexBins[0].value || tmpVertexBins[1].value)) { - float z{lowHistZ + tmpVertexBins[2].key * binSizeHistZ + binSizeHistZ / 2}; - float ex{0.f}; - float ey{0.f}; - float ez{0.f}; - int sumWZ{tmpVertexBins[2].value}; - float wZ{z * tmpVertexBins[2].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVertexBins[2].key - binOpeningZ)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVertexBins[2].key + binOpeningZ + 1, nBinsHistZ - 1); ++iBin) { - if (iBin != tmpVertexBins[2].key) { - wZ += (lowHistZ + iBin * binSizeHistZ + binSizeHistZ / 2) * histZ[iBin]; - sumWZ += histZ[iBin]; - } - histZ[iBin] = 0; - } - if (sumWZ > minContributors || vertIndex == 0) { - new (vertices + vertIndex) Vertex{o2::math_utils::Point3D(beamPosition[0], beamPosition[1], wZ / sumWZ), o2::gpu::gpustd::array{ex, 0, ey, 0, 0, ez}, static_cast(sumWZ), 0}; - } else { - new (vertices + vertIndex) Vertex{}; - } - } else { - new (vertices + vertIndex) Vertex{}; - } - } - } -} -} // namespace gpu - -void VertexerTraitsGPU::updateVertexingParameters(const VertexingParameters& vrtPar, const TimeFrameGPUParameters& tfPar) -{ - mVrtParams = vrtPar; - mTfGPUParams = tfPar; - mIndexTableUtils.setTrackingParameters(vrtPar); - mVrtParams.phiSpan = static_cast(std::ceil(mIndexTableUtils.getNphiBins() * mVrtParams.phiCut / - constants::math::TwoPi)); - mVrtParams.zSpan = static_cast(std::ceil(mVrtParams.zCut * mIndexTableUtils.getInverseZCoordinate(0))); -} - -void VertexerTraitsGPU::computeTracklets() -{ - if (!mTimeFrameGPU->getClusters().size()) { - return; - } - std::vector threads(mTimeFrameGPU->getNChunks()); - for (int chunkId{0}; chunkId < mTimeFrameGPU->getNChunks(); ++chunkId) { - int rofPerChunk{mTimeFrameGPU->mNrof / (int)mTimeFrameGPU->getNChunks()}; - mTimeFrameGPU->getVerticesInChunks()[chunkId].clear(); - mTimeFrameGPU->getNVerticesInChunks()[chunkId].clear(); - mTimeFrameGPU->getLabelsInChunks()[chunkId].clear(); - auto doVertexReconstruction = [&, chunkId, rofPerChunk]() -> void { - auto offset = chunkId * rofPerChunk; - auto maxROF = offset + rofPerChunk; - while (offset < maxROF) { - auto rofs = mTimeFrameGPU->loadChunkData(chunkId, offset, maxROF); - RANGE("chunk_gpu_vertexing", 1); - gpu::trackleterKernelMultipleRof<<getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(0), // const Cluster* clustersNextLayer, // 0 2 - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(1), // const Cluster* clustersCurrentLayer, // 1 1 - mTimeFrameGPU->getDeviceROframesClusters(0), // const int* sizeNextLClusters, - mTimeFrameGPU->getDeviceROframesClusters(1), // const int* sizeCurrentLClusters, - mTimeFrameGPU->getChunk(chunkId).getDeviceIndexTables(0), // const int* nextIndexTables, - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(0), // Tracklet* Tracklets, - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(0), // int* foundTracklets, - mTimeFrameGPU->getDeviceIndexTableUtils(), // const IndexTableUtils* utils, - offset, // const unsigned int startRofId, - rofs, // const unsigned int rofSize, - mVrtParams.phiCut, // const float phiCut, - mVrtParams.maxTrackletsPerCluster); // const size_t maxTrackletsPerCluster = 1e2 - gpu::trackleterKernelMultipleRof<<getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(2), // const Cluster* clustersNextLayer, // 0 2 - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(1), // const Cluster* clustersCurrentLayer, // 1 1 - mTimeFrameGPU->getDeviceROframesClusters(2), // const int* sizeNextLClusters, - mTimeFrameGPU->getDeviceROframesClusters(1), // const int* sizeCurrentLClusters, - mTimeFrameGPU->getChunk(chunkId).getDeviceIndexTables(2), // const int* nextIndexTables, - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(1), // Tracklet* Tracklets, - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(1), // int* foundTracklets, - mTimeFrameGPU->getDeviceIndexTableUtils(), // const IndexTableUtils* utils, - offset, // const unsigned int startRofId, - rofs, // const unsigned int rofSize, - mVrtParams.phiCut, // const float phiCut, - mVrtParams.maxTrackletsPerCluster); // const size_t maxTrackletsPerCluster = 1e2 - - // Tracklet selection - gpu::trackletSelectionKernelMultipleRof<<getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(0), // const Cluster* clusters0, // Clusters on layer 0 - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(1), // const Cluster* clusters1, // Clusters on layer 1 - mTimeFrameGPU->getDeviceROframesClusters(0), // const int* sizeClustersL0, // Number of clusters on layer 0 per ROF - mTimeFrameGPU->getDeviceROframesClusters(1), // const int* sizeClustersL1, // Number of clusters on layer 1 per ROF - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(0), // Tracklet* tracklets01, // Tracklets on layer 0-1 - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(1), // Tracklet* tracklets12, // Tracklets on layer 1-2 - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(0), // const int* nFoundTracklets01, // Number of tracklets found on layers 0-1 - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(1), // const int* nFoundTracklet12, // Number of tracklets found on layers 1-2 - mTimeFrameGPU->getChunk(chunkId).getDeviceUsedTracklets(), // unsigned char* usedTracklets, // Used tracklets - mTimeFrameGPU->getChunk(chunkId).getDeviceLines(), // Line* lines, // Lines - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundLines(), // int* nFoundLines, // Number of found lines - mTimeFrameGPU->getChunk(chunkId).getDeviceNExclusiveFoundLines(), // int* nExclusiveFoundLines, // Number of found lines exclusive scan - offset, // const unsigned int startRofId, // Starting ROF ID - rofs, // const unsigned int rofSize, // Number of ROFs to consider - mVrtParams.maxTrackletsPerCluster, // const int maxTrackletsPerCluster = 1e2, // Maximum number of tracklets per cluster - mVrtParams.tanLambdaCut, // const float tanLambdaCut = 0.025f, // Cut on tan lambda - mVrtParams.phiCut); // const float phiCut = 0.002f) // Cut on phi - - discardResult(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundLines(), - mTimeFrameGPU->getChunk(chunkId).getDeviceNExclusiveFoundLines(), - mTimeFrameGPU->getTotalClustersPerROFrange(offset, rofs, 1), - mTimeFrameGPU->getStream(chunkId).get())); - - // Reset used tracklets - checkGPUError(cudaMemsetAsync(mTimeFrameGPU->getChunk(chunkId).getDeviceUsedTracklets(), - false, - sizeof(unsigned char) * mVrtParams.maxTrackletsPerCluster * mTimeFrameGPU->getTotalClustersPerROFrange(offset, rofs, 1), - mTimeFrameGPU->getStream(chunkId).get()), - __FILE__, __LINE__); - - gpu::trackletSelectionKernelMultipleRof<<getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(0), // const Cluster* clusters0, // Clusters on layer 0 - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(1), // const Cluster* clusters1, // Clusters on layer 1 - mTimeFrameGPU->getDeviceROframesClusters(0), // const int* sizeClustersL0, // Number of clusters on layer 0 per ROF - mTimeFrameGPU->getDeviceROframesClusters(1), // const int* sizeClustersL1, // Number of clusters on layer 1 per ROF - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(0), // Tracklet* tracklets01, // Tracklets on layer 0-1 - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(1), // Tracklet* tracklets12, // Tracklets on layer 1-2 - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(0), // const int* nFoundTracklets01, // Number of tracklets found on layers 0-1 - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(1), // const int* nFoundTracklet12, // Number of tracklets found on layers 1-2 - mTimeFrameGPU->getChunk(chunkId).getDeviceUsedTracklets(), // unsigned char* usedTracklets, // Used tracklets - mTimeFrameGPU->getChunk(chunkId).getDeviceLines(), // Line* lines, // Lines - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundLines(), // int* nFoundLines, // Number of found lines - mTimeFrameGPU->getChunk(chunkId).getDeviceNExclusiveFoundLines(), // int* nExclusiveFoundLines, // Number of found lines exclusive scan - offset, // const unsigned int startRofId, // Starting ROF ID - rofs, // const unsigned int rofSize, // Number of ROFs to consider - mVrtParams.maxTrackletsPerCluster, // const int maxTrackletsPerCluster = 1e2, // Maximum number of tracklets per cluster - mVrtParams.tanLambdaCut, // const float tanLambdaCut = 0.025f, // Cut on tan lambda - mVrtParams.phiCut); // const float phiCut = 0.002f) // Cut on phi - - int nClusters = mTimeFrameGPU->getTotalClustersPerROFrange(offset, rofs, 1); - int lastFoundLines; - std::vector exclusiveFoundLinesHost(nClusters + 1); - - // Obtain whole exclusive sum including nCluster+1 element (nCluster+1)th element is the total number of found lines. - checkGPUError(cudaMemcpyAsync(exclusiveFoundLinesHost.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceNExclusiveFoundLines(), (nClusters) * sizeof(int), cudaMemcpyDeviceToHost, mTimeFrameGPU->getStream(chunkId).get())); - checkGPUError(cudaMemcpyAsync(&lastFoundLines, mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundLines() + nClusters - 1, sizeof(int), cudaMemcpyDeviceToHost, mTimeFrameGPU->getStream(chunkId).get())); - exclusiveFoundLinesHost[nClusters] = exclusiveFoundLinesHost[nClusters - 1] + lastFoundLines; - - std::vector lines(exclusiveFoundLinesHost[nClusters]); - - checkGPUError(cudaMemcpyAsync(lines.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceLines(), sizeof(Line) * lines.size(), cudaMemcpyDeviceToHost, mTimeFrameGPU->getStream(chunkId).get())); - checkGPUError(cudaStreamSynchronize(mTimeFrameGPU->getStream(chunkId).get())); - - // Compute vertices - std::vector clusterLines; - std::vector usedLines; - for (int rofId{0}; rofId < rofs; ++rofId) { - auto rof = offset + rofId; - auto clustersL1offsetRof = mTimeFrameGPU->getROframeClusters(1)[rof] - mTimeFrameGPU->getROframeClusters(1)[offset]; // starting cluster offset for this ROF - auto nClustersL1Rof = mTimeFrameGPU->getROframeClusters(1)[rof + 1] - mTimeFrameGPU->getROframeClusters(1)[rof]; // number of clusters for this ROF - auto linesOffsetRof = exclusiveFoundLinesHost[clustersL1offsetRof]; // starting line offset for this ROF - auto nLinesRof = exclusiveFoundLinesHost[clustersL1offsetRof + nClustersL1Rof] - linesOffsetRof; - gsl::span linesInRof(lines.data() + linesOffsetRof, static_cast::size_type>(nLinesRof)); - - usedLines.resize(linesInRof.size(), false); - usedLines.assign(linesInRof.size(), false); - clusterLines.clear(); - clusterLines.reserve(nClustersL1Rof); - computeVerticesInRof(rof, - linesInRof, - usedLines, - clusterLines, - mTimeFrameGPU->getBeamXY(), - mTimeFrameGPU->getVerticesInChunks()[chunkId], - mTimeFrameGPU->getNVerticesInChunks()[chunkId], - mTimeFrameGPU->hasMCinformation() ? mTimeFrameGPU : nullptr, - mTimeFrameGPU->hasMCinformation() ? &mTimeFrameGPU->getLabelsInChunks()[chunkId] : nullptr); - } - offset += rofs; - } - }; - // Do work - threads[chunkId] = std::thread(doVertexReconstruction); - } - for (auto& thread : threads) { - thread.join(); - } - for (int chunkId{0}; chunkId < mTimeFrameGPU->getNChunks(); ++chunkId) { - int start{0}; - for (int rofId{0}; rofId < mTimeFrameGPU->getNVerticesInChunks()[chunkId].size(); ++rofId) { - gsl::span rofVerts{mTimeFrameGPU->getVerticesInChunks()[chunkId].data() + start, static_cast::size_type>(mTimeFrameGPU->getNVerticesInChunks()[chunkId][rofId])}; - mTimeFrameGPU->addPrimaryVertices(rofVerts); - if (mTimeFrameGPU->hasMCinformation()) { - mTimeFrameGPU->getVerticesLabels().emplace_back(); - // TODO: add MC labels - } - start += mTimeFrameGPU->getNVerticesInChunks()[chunkId][rofId]; - } - } - mTimeFrameGPU->wipe(3); -} - -void VertexerTraitsGPU::computeTrackletMatching() -{ -} - -void VertexerTraitsGPU::computeVertices() -{ -} - -void VertexerTraitsGPU::computeVerticesHist() -{ -} - -VertexerTraits* createVertexerTraitsGPU() -{ - return new VertexerTraitsGPU; -} -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/hip/.gitignore b/Detectors/ITSMFT/ITS/tracking/GPU/hip/.gitignore deleted file mode 100644 index 5854f8830a79b..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Don't track generated hip sources -*.hip.cxx \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt index cba23d5119e41..e28fe04c06772 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt @@ -10,49 +10,21 @@ # or submit itself to any jurisdiction. if(HIP_ENABLED) - # Hipify-perl to generate HIP sources - set(HIPIFY_EXECUTABLE "/opt/rocm/bin/hipify-perl") - file(GLOB CUDA_SOURCES_FULL_PATH "../cuda/*.cu") - foreach(file ${CUDA_SOURCES_FULL_PATH}) - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${file}) - get_filename_component(CUDA_SOURCE ${file} NAME) - string(REPLACE ".cu" "" CUDA_SOURCE_NAME ${CUDA_SOURCE}) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${CUDA_SOURCE_NAME}.hip.cxx - COMMAND ${HIPIFY_EXECUTABLE} --quiet-warnings ${CMAKE_CURRENT_SOURCE_DIR}/../cuda/${CUDA_SOURCE} | sed '1{/\#include \"hip\\/hip_runtime.h\"/d}' > ${CMAKE_CURRENT_SOURCE_DIR}/${CUDA_SOURCE_NAME}.hip.cxx - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../cuda/${CUDA_SOURCE} - ) - endforeach() - - install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${baseTargetName} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - - set(CMAKE_CXX_COMPILER ${hip_HIPCC_EXECUTABLE}) - set(CMAKE_CXX_EXTENSIONS OFF) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${O2_HIP_CMAKE_CXX_FLAGS} -fgpu-rdc") - message(STATUS "Building ITS HIP tracker") - o2_add_library(ITStrackingHIP - SOURCES ClusterLinesGPU.hip.cxx - Context.hip.cxx - TimeFrameGPU.hip.cxx - Stream.hip.cxx - TrackerTraitsGPU.hip.cxx - TracerGPU.hip.cxx - VertexerTraitsGPU.hip.cxx - Utils.hip.cxx + set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -fgpu-rdc") + # set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -O0 -g -ggdb -fno-inline -fno-omit-frame-pointer -D__HIP_ENABLE_DEVICE_ASSERT__") + # add_compile_definitions(ITS_MEASURE_GPU_TIME) + # add_compile_definitions(ITS_GPU_LOG) + o2_add_hipified_library(ITStrackingHIP + SOURCES ../cuda/TimeFrameGPU.cu + ../cuda/TrackerTraitsGPU.cxx + ../cuda/TrackingKernels.cu PUBLIC_INCLUDE_DIRECTORIES ../ PUBLIC_LINK_LIBRARIES O2::ITStracking + O2::GPUTracking + O2::SimulationDataFormat + O2::ReconstructionDataFormats hip::host - hip::device - hip::hipcub + PRIVATE_LINK_LIBRARIES O2::GPUTrackingHIPExternalProvider TARGETVARNAME targetName) - - target_compile_definitions( - ${targetName} PRIVATE $) - - if(O2_HIP_CMAKE_LINK_FLAGS) - # Need to add gpu target also to link flags due to gpu-rdc option - target_link_options(${targetName} PUBLIC ${O2_HIP_CMAKE_LINK_FLAGS}) - endif() endif() diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ArrayUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ArrayUtils.h deleted file mode 100644 index 971ae6a7fe83a..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ArrayUtils.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file ArrayUtils.h -/// \brief -/// - -#ifndef TRACKINGITSU_INCLUDE_ARRAYUTILS_H_ -#define TRACKINGITSU_INCLUDE_ARRAYUTILS_H_ - -#include -#include -#include - -namespace o2 -{ -namespace its -{ -namespace CA -{ - -namespace ArrayUtils -{ -template -constexpr std::array fillArray(Initializer, std::index_sequence); -template -constexpr std::array fillArray(Initializer); -} // namespace ArrayUtils - -template -constexpr std::array ArrayUtils::fillArray(Initializer initializer, std::index_sequence) -{ - return std::array{{initializer(Is)...}}; -} - -template -constexpr std::array ArrayUtils::fillArray(Initializer initializer) -{ - return ArrayUtils::fillArray(initializer, std::make_index_sequence{}); -} -} // namespace CA -} // namespace its -} // namespace o2 - -#endif /* TRACKINGITSU_INCLUDE_ARRAYUTILS_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h new file mode 100644 index 0000000000000..814e271726b06 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h @@ -0,0 +1,314 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file BoundedAllocator.h +/// \brief +/// + +#ifndef TRACKINGITSU_INCLUDE_BOUNDEDALLOCATOR_H_ +#define TRACKINGITSU_INCLUDE_BOUNDEDALLOCATOR_H_ + +#include +#include +#include +#include +#include + +#if !defined(__HIPCC__) && !defined(__CUDACC__) +#include +#include +#include "GPUCommonLogger.h" +#endif +#include "ITStracking/ExternalAllocator.h" +#include "ITStracking/Constants.h" + +namespace o2::its +{ + +// #define BOUNDED_MR_STATS +class BoundedMemoryResource final : public std::pmr::memory_resource +{ + public: + class MemoryLimitExceeded final : public std::bad_alloc + { + public: + MemoryLimitExceeded(size_t attempted, size_t used, size_t max) + { + char buf[256]; + if (attempted != 0) { + (void)snprintf(buf, sizeof(buf), "Reached set memory limit (attempted: %zu, used: %zu, max: %zu)", attempted, used, max); + } else { + (void)snprintf(buf, sizeof(buf), "New set maximum below current used (newMax: %zu, used: %zu)", max, used); + } + mMsg = buf; + } + const char* what() const noexcept final { return mMsg.c_str(); } + + private: + std::string mMsg; + }; + + BoundedMemoryResource(size_t maxBytes = std::numeric_limits::max(), + std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) + : mMaxMemory(maxBytes), mUpstream(upstream) {} + + BoundedMemoryResource(ExternalAllocator* alloc, + size_t maxBytes = std::numeric_limits::max()) + : mMaxMemory(maxBytes), + mAdaptor(std::make_unique(alloc)), + mUpstream(mAdaptor.get()) {} + + void* do_allocate(size_t bytes, size_t alignment) final + { + size_t new_used{0}; + size_t current_used{mUsedMemory.load(std::memory_order_relaxed)}; + do { + new_used = current_used + bytes; + if (new_used > mMaxMemory.load(std::memory_order_relaxed)) { + mCountThrow.fetch_add(1, std::memory_order_relaxed); + throw MemoryLimitExceeded(new_used, current_used, + mMaxMemory.load(std::memory_order_relaxed)); + } + } while (!mUsedMemory.compare_exchange_weak(current_used, new_used, + std::memory_order_acq_rel, + std::memory_order_relaxed)); + + void* p{nullptr}; + try { + p = mUpstream->allocate(bytes, alignment); + } catch (...) { + mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); +#ifdef BOUNDED_MR_STATS + mStats.upstreamFailures.fetch_add(1, std::memory_order_relaxed); +#endif + throw; + } + + size_t peak = mPeakUsedMemory.load(std::memory_order_relaxed); + while (new_used > peak && + !mPeakUsedMemory.compare_exchange_weak(peak, new_used, + std::memory_order_relaxed)) { + } + +#ifdef BOUNDED_MR_STATS + size_t statsPeak = mStats.peak.load(std::memory_order_relaxed); + while (new_used > statsPeak && + !mStats.peak.compare_exchange_weak(statsPeak, new_used, + std::memory_order_relaxed)) { + } + mStats.live.fetch_add(1, std::memory_order_relaxed); + mStats.nAlloc.fetch_add(1, std::memory_order_relaxed); + mStats.totalAlloc.fetch_add(bytes, std::memory_order_relaxed); + + size_t ma = mStats.maxAlign.load(std::memory_order_relaxed); + while (alignment > ma && !mStats.maxAlign.compare_exchange_weak(ma, alignment, std::memory_order_relaxed)) { + } +#endif + return p; + } + + void do_deallocate(void* p, size_t bytes, size_t alignment) final + { + mUpstream->deallocate(p, bytes, alignment); + mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); +#ifdef BOUNDED_MR_STATS + mStats.live.fetch_sub(1, std::memory_order_relaxed); + mStats.nFree.fetch_add(1, std::memory_order_relaxed); + mStats.totalFreed.fetch_add(bytes, std::memory_order_relaxed); +#endif + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final + { + return this == &other; + } + + [[nodiscard]] size_t getUsedMemory() const noexcept + { + return mUsedMemory.load(std::memory_order_relaxed); + } + [[nodiscard]] size_t getMaxMemory() const noexcept + { + return mMaxMemory.load(std::memory_order_relaxed); + } + [[nodiscard]] size_t getThrowCount() const noexcept + { + return mCountThrow.load(std::memory_order_relaxed); + } + [[nodiscard]] size_t getPeakMemory() const noexcept + { + return mPeakUsedMemory.load(std::memory_order_relaxed); + } + [[nodiscard]] size_t getPeakMemoryDelta() const noexcept + { + const size_t peak = mPeakUsedMemory.load(std::memory_order_relaxed); + const size_t baseline = mPeakBaselineMemory.load(std::memory_order_relaxed); + return peak > baseline ? peak - baseline : 0; + } + + void resetPeakMemory() noexcept + { + const size_t used = mUsedMemory.load(std::memory_order_acquire); + mPeakBaselineMemory.store(used, std::memory_order_release); + mPeakUsedMemory.store(used, std::memory_order_release); + } + + void setMaxMemory(size_t max) + { + size_t current = mMaxMemory.load(std::memory_order_relaxed); + if (max == current) { + return; + } + for (;;) { + size_t used = mUsedMemory.load(std::memory_order_acquire); + if (used > max) { + mCountThrow.fetch_add(1, std::memory_order_relaxed); + throw MemoryLimitExceeded(0, used, max); + } + if (mMaxMemory.compare_exchange_weak(current, max, + std::memory_order_release, + std::memory_order_relaxed)) { + return; + } + if (current == max) { + return; + } + } + } + +#if !defined(__HIPCC__) && !defined(__CUDACC__) + std::string asString() const + { + const auto throw_ = mCountThrow.load(std::memory_order_relaxed); + const auto used = static_cast(mUsedMemory.load(std::memory_order_relaxed)); + const auto peak = static_cast(mPeakUsedMemory.load(std::memory_order_relaxed)); + const auto peakDelta = static_cast(getPeakMemoryDelta()); + const auto maxm = mMaxMemory.load(std::memory_order_relaxed); + std::string ret; + if (maxm == std::numeric_limits::max()) { + ret += std::format("maxthrow={} maxmem=unbounded used={:.2f} GB stagepeak={:.2f} GB stagealloc={:.2f} GB", throw_, used / constants::GB, peak / constants::GB, peakDelta / constants::GB); + } else { + ret += std::format("maxthrow={} maxmem={:.2f} GB used={:.2f} GB ({:.2f}%) stagepeak={:.2f} GB stagealloc={:.2f} GB", throw_, (double)maxm / constants::GB, used / constants::GB, 100.0 * used / (double)maxm, peak / constants::GB, peakDelta / constants::GB); + } +#ifdef BOUNDED_MR_STATS + ret += std::format(" peak={:.2f} GB live={} nAlloc={} nFree={} totalAlloc={:.2f} GB totalFreed={:.2f} GB maxAlign={} upstreamFail={}", + (float)mStats.peak.load(std::memory_order_relaxed) / constants::GB, + mStats.live.load(std::memory_order_relaxed), + mStats.nAlloc.load(std::memory_order_relaxed), + mStats.nFree.load(std::memory_order_relaxed), + (float)mStats.totalAlloc.load(std::memory_order_relaxed) / constants::GB, + (float)mStats.totalFreed.load(std::memory_order_relaxed) / constants::GB, + mStats.maxAlign.load(std::memory_order_relaxed), + mStats.upstreamFailures.load(std::memory_order_relaxed)); +#endif + return ret; + } + + void print() const + { + LOGP(info, "{}", asString()); + } +#endif + + private: + std::atomic mMaxMemory{std::numeric_limits::max()}; + std::atomic mCountThrow{0}; + std::atomic mUsedMemory{0}; + std::atomic mPeakUsedMemory{0}; + std::atomic mPeakBaselineMemory{0}; + std::unique_ptr mAdaptor{nullptr}; + std::pmr::memory_resource* mUpstream{nullptr}; + +#ifdef BOUNDED_MR_STATS + struct Stats { + std::atomic peak{0}; + std::atomic live{0}; + std::atomic nAlloc{0}; + std::atomic nFree{0}; + std::atomic totalAlloc{0}; + std::atomic totalFreed{0}; + std::atomic maxAlign{0}; + std::atomic upstreamFailures{0}; + }; + Stats mStats{}; +#endif +}; + +template +using bounded_vector = std::pmr::vector; + +template +inline void deepVectorClear(std::vector& vec) +{ + std::vector().swap(vec); +} + +template +inline void deepVectorClear(bounded_vector& vec, std::pmr::memory_resource* mr = nullptr) +{ + std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); + vec.~bounded_vector(); + new (&vec) bounded_vector(std::pmr::polymorphic_allocator{tmr}); +} + +template +inline void deepVectorClear(std::vector>& vec, std::pmr::memory_resource* mr = nullptr) +{ + for (auto& v : vec) { + deepVectorClear(v, mr); + } +} + +template +inline void deepVectorClear(std::array, S>& arr, std::pmr::memory_resource* mr = nullptr) +{ + for (size_t i{0}; i < S; ++i) { + deepVectorClear(arr[i], mr); + } +} + +template +inline void clearResizeBoundedVector(bounded_vector& vec, size_t sz, std::pmr::memory_resource* mr = nullptr, T def = T()) +{ + std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); + vec.~bounded_vector(); + new (&vec) bounded_vector(sz, def, std::pmr::polymorphic_allocator{tmr}); +} + +template +inline void clearResizeBoundedVector(std::vector>& vec, size_t size, std::pmr::memory_resource* mr) +{ + vec.clear(); + vec.reserve(size); + for (size_t i = 0; i < size; ++i) { + vec.emplace_back(std::pmr::polymorphic_allocator>{mr}); + } +} + +template +inline void clearResizeBoundedArray(std::array, S>& arr, size_t size, std::pmr::memory_resource* mr = nullptr, T def = T()) +{ + for (size_t i{0}; i < S; ++i) { + clearResizeBoundedVector(arr[i], size, mr, def); + } +} + +template +inline std::vector toSTDVector(const bounded_vector& b) +{ + std::vector t(b.size()); + std::copy(b.cbegin(), b.cend(), t.begin()); + return t; +} + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h index 49dd34577ac65..4706977d08ba6 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h @@ -16,69 +16,162 @@ #ifndef TRACKINGITSU_INCLUDE_CACELL_H_ #define TRACKINGITSU_INCLUDE_CACELL_H_ -#ifndef GPUCA_GPUCODE_DEVICE -#include -#include -#endif +#include +#include "ITStracking/Constants.h" +#include "ITStracking/LayerMask.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "ReconstructionDataFormats/Track.h" #include "GPUCommonDef.h" -namespace o2 -{ -namespace its +namespace o2::its { -class Cell final +struct CellNeighbour { + int cellTopology{-1}; + int cell{-1}; + int nextCellTopology{-1}; + int nextCell{-1}; + int level{-1}; +}; + +template +class SeedBase : public o2::track::TrackParCovF { public: - GPUhd() Cell(); - GPUd() Cell(const int, const int, const int, const int, const int, const float); - - GPUhd() int getFirstClusterIndex() const { return mFirstClusterIndex; }; - GPUhd() int getSecondClusterIndex() const { return mSecondClusterIndex; }; - GPUhd() int getThirdClusterIndex() const { return mThirdClusterIndex; }; - GPUhd() int getFirstTrackletIndex() const { return mFirstTrackletIndex; }; - GPUhd() int getSecondTrackletIndex() const { return mSecondTrackletIndex; }; - GPUhd() float getTanLambda() const { return mTanLambda; }; + GPUhd() LayerMask getHitLayerMask() const { return LayerMask{static_cast(getUserField())}; } + GPUhd() void setHitLayerMask(LayerMask mask) { setUserField(mask.value()); } + GPUhd() int getInnerLayer() const { return getHitLayerMask().first(); } + GPUhd() int getFirstTrackletIndex() const { return mTracklets[0]; }; + GPUhd() void setFirstTrackletIndex(int trkl) { mTracklets[0] = trkl; }; + GPUhd() int getSecondTrackletIndex() const { return mTracklets[1]; }; + GPUhd() void setSecondTrackletIndex(int trkl) { mTracklets[1] = trkl; }; + GPUhd() float getChi2() const { return mChi2; }; + GPUhd() void setChi2(float chi2) { mChi2 = chi2; }; GPUhd() int getLevel() const { return mLevel; }; - GPUhd() void setLevel(const int level) { mLevel = level; }; + GPUhd() void setLevel(int level) { mLevel = level; }; GPUhd() int* getLevelPtr() { return &mLevel; } + GPUhd() auto& getTimeStamp() noexcept { return mTime; } + GPUhd() const auto& getTimeStamp() const noexcept { return mTime; } + + protected: + GPUhdDefault() SeedBase() = default; + GPUhdDefault() SeedBase(const SeedBase&) = default; + GPUhdDefault() ~SeedBase() = default; + GPUhdDefault() SeedBase(SeedBase&&) = default; + GPUhdDefault() SeedBase& operator=(const SeedBase&) = default; + GPUhdDefault() SeedBase& operator=(SeedBase&&) = default; + GPUhd() SeedBase(const o2::track::TrackParCovF& tpc, float chi2, int level, const TimeEstBC& time) + : o2::track::TrackParCovF(tpc), mChi2(chi2), mLevel(level), mTime(time) + { + } + GPUhd() auto& clustersRaw() { return mClusters; } + GPUhd() const auto& clustersRaw() const { return mClusters; } private: - const int mFirstClusterIndex; - const int mSecondClusterIndex; - const int mThirdClusterIndex; - const int mFirstTrackletIndex; - const int mSecondTrackletIndex; - const float mTanLambda; - int mLevel; + float mChi2{constants::UnsetValue}; + int mLevel{constants::UnusedIndex}; + std::array mTracklets = constants::helpers::initArray(); + std::array mClusters = constants::helpers::initArray(); + TimeEstBC mTime; }; -GPUhdi() Cell::Cell() - : mFirstClusterIndex{0}, - mSecondClusterIndex{0}, - mThirdClusterIndex{0}, - mFirstTrackletIndex{0}, - mSecondTrackletIndex{0}, - mTanLambda{0}, - mLevel{0} +/// CellSeed: connections of three clusters +class CellSeed final : public SeedBase { - // Nothing to do -} - -GPUdi() Cell::Cell(const int firstClusterIndex, const int secondClusterIndex, const int thirdClusterIndex, - const int firstTrackletIndex, const int secondTrackletIndex, const float tanL) - : mFirstClusterIndex{firstClusterIndex}, - mSecondClusterIndex{secondClusterIndex}, - mThirdClusterIndex{thirdClusterIndex}, - mFirstTrackletIndex{firstTrackletIndex}, - mSecondTrackletIndex{secondTrackletIndex}, - mTanLambda{tanL}, - mLevel{1} + using Base = SeedBase; + + public: + GPUhdDefault() CellSeed() = default; + GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, const o2::track::TrackParCovF& tpc, float chi2, const TimeEstBC& time) + : CellSeed(LayerMask(innerL, innerL + 1, innerL + 2), cl0, cl1, cl2, trkl0, trkl1, tpc, chi2, time) + { + } + GPUhd() CellSeed(LayerMask hitLayerMask, int cl0, int cl1, int cl2, int trkl0, int trkl1, const o2::track::TrackParCovF& tpc, float chi2, const TimeEstBC& time) + : Base(tpc, chi2, 1, time) + { + setHitLayerMask(hitLayerMask); + auto& clusters = this->clustersRaw(); + clusters[0] = cl0; + clusters[1] = cl1; + clusters[2] = cl2; + setFirstTrackletIndex(trkl0); + setSecondTrackletIndex(trkl1); + } + GPUhdDefault() CellSeed(const CellSeed&) = default; + GPUhdDefault() ~CellSeed() = default; + GPUhdDefault() CellSeed(CellSeed&&) = default; + GPUhdDefault() CellSeed& operator=(const CellSeed&) = default; + GPUhdDefault() CellSeed& operator=(CellSeed&&) = default; + + GPUhd() int getFirstClusterIndex() const { return this->clustersRaw()[0]; }; + GPUhd() int getSecondClusterIndex() const { return this->clustersRaw()[1]; }; + GPUhd() int getThirdClusterIndex() const { return this->clustersRaw()[2]; }; + GPUhd() auto& getClusters() { return this->clustersRaw(); } + GPUhd() const auto& getClusters() const { return this->clustersRaw(); } + /// getCluster takes an ABSOLUTE layer index. Compact cluster slots are + /// mapped to absolute layers by set-bit order in the hit-layer mask. + GPUhd() int getCluster(int layer) const + { + const int slot = getHitLayerMask().slot(layer); + return (slot >= 0 && slot < constants::ClustersPerCell) ? this->clustersRaw()[slot] : constants::UnusedIndex; + } +}; + +/// TrackSeed: full-width working representation used during road finding. +/// processNeighbours extends the cluster list inward, so we need NLayers +/// absolute-indexed slots here. +template +class TrackSeed final : public SeedBase { - // Nothing to do -} + using Base = SeedBase; + + public: + GPUhdDefault() TrackSeed() = default; + GPUhd() TrackSeed(const CellSeed& cs) + : Base(static_cast(cs), cs.getChi2(), cs.getLevel(), cs.getTimeStamp()) + { + this->setHitLayerMask(cs.getHitLayerMask()); + this->setFirstTrackletIndex(cs.getFirstTrackletIndex()); + this->setSecondTrackletIndex(cs.getSecondTrackletIndex()); + auto& clusters = this->clustersRaw(); + int slot = 0; + const auto hitMask = cs.getHitLayerMask(); + for (int layer = 0; layer < NLayers; ++layer) { + if (hitMask.has(layer)) { + clusters[layer] = cs.getClusters()[slot++]; + } + } + } + GPUhdDefault() TrackSeed(const TrackSeed&) = default; + GPUhdDefault() ~TrackSeed() = default; + GPUhdDefault() TrackSeed(TrackSeed&&) = default; + GPUhdDefault() TrackSeed& operator=(const TrackSeed&) = default; + GPUhdDefault() TrackSeed& operator=(TrackSeed&&) = default; + + GPUhd() int getFirstClusterIndex() const { return getClusterBySlot(0); } + GPUhd() int getSecondClusterIndex() const { return getClusterBySlot(1); } + GPUhd() int getThirdClusterIndex() const { return getClusterBySlot(2); } + GPUhd() auto& getClusters() { return this->clustersRaw(); } + GPUhd() const auto& getClusters() const { return this->clustersRaw(); } + GPUhd() int getCluster(int layer) const { return this->clustersRaw()[layer]; } + + private: + GPUhd() int getClusterBySlot(int requestedSlot) const + { + int slot = 0; + const auto hitMask = this->getHitLayerMask(); + for (int layer = 0; layer < NLayers; ++layer) { + if (hitMask.has(layer)) { + if (slot++ == requestedSlot) { + return this->clustersRaw()[layer]; + } + } + } + return constants::UnusedIndex; + } +}; + +} // namespace o2::its -} // namespace its -} // namespace o2 #endif /* TRACKINGITSU_INCLUDE_CACELL_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h index 4004fad2f7de2..fb5f1a13ef3d2 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h @@ -16,61 +16,59 @@ #ifndef TRACKINGITSU_INCLUDE_CACLUSTER_H_ #define TRACKINGITSU_INCLUDE_CACLUSTER_H_ -#ifndef GPUCA_GPUCODE_DEVICE #include -#endif - -#include "GPUCommonRtypes.h" -#include "ITStracking/Definitions.h" +#include "ITStracking/Constants.h" #include "ITStracking/MathUtils.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonDef.h" -namespace o2 -{ -namespace its +namespace o2::its { +template class IndexTableUtils; struct Cluster final { - Cluster() = default; - Cluster(const float x, const float y, const float z, const int idx); - Cluster(const int, const IndexTableUtils& utils, const Cluster&); - Cluster(const int, const float3&, const IndexTableUtils& utils, const Cluster&); - void Init(const int, const float3&, const IndexTableUtils& utils, const Cluster&); - bool operator==(const Cluster&) const; + GPUhdDefault() Cluster() = default; + GPUhd() Cluster(const float x, const float y, const float z, const int idx) + : xCoordinate(x), + yCoordinate(y), + zCoordinate(z), + phi(math_utils::computeNormalizedPhi(x, y)), + radius(math_utils::hypot(x, y)), + clusterId(idx), + indexTableBinIndex(0) {} GPUhd() void print() const; - float xCoordinate; // = -999.f; - float yCoordinate; // = -999.f; - float zCoordinate; // = -999.f; - float phi; // = -999.f; - float radius; // = -999.f; - int clusterId; // = -1; - int indexTableBinIndex; // = -1; + float xCoordinate{constants::UnsetValue}; + float yCoordinate{constants::UnsetValue}; + float zCoordinate{constants::UnsetValue}; + float phi{constants::UnsetValue}; + float radius{constants::UnsetValue}; + int clusterId{constants::UnusedIndex}; + int indexTableBinIndex{constants::UnusedIndex}; ClassDefNV(Cluster, 1); }; -GPUhdi() void Cluster::print() const -{ - printf("Cluster: %f %f %f %f %f %d %d\n", xCoordinate, yCoordinate, zCoordinate, phi, radius, clusterId, indexTableBinIndex); -} +struct TrackingFrameInfo final { + GPUhdDefault() TrackingFrameInfo() = default; + GPUhd() TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, const std::array& posTF, const std::array& covTF) + : xCoordinate(x), yCoordinate(y), zCoordinate(z), xTrackingFrame(xTF), alphaTrackingFrame(alpha), positionTrackingFrame(posTF), covarianceTrackingFrame(covTF) {} -struct TrackingFrameInfo { - TrackingFrameInfo() = default; - TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, GPUArray&& posTF, GPUArray&& covTF); + GPUhd() void print() const; - float xCoordinate; - float yCoordinate; - float zCoordinate; - float xTrackingFrame; - float alphaTrackingFrame; - GPUArray positionTrackingFrame = {-1., -1.}; - GPUArray covarianceTrackingFrame = {999., 999., 999.}; + float xCoordinate{constants::UnsetValue}; + float yCoordinate{constants::UnsetValue}; + float zCoordinate{constants::UnsetValue}; + float xTrackingFrame{constants::UnsetValue}; + float alphaTrackingFrame{constants::UnsetValue}; + std::array positionTrackingFrame{constants::UnsetValue, constants::UnsetValue}; + std::array covarianceTrackingFrame{constants::UnsetValue, constants::UnsetValue, constants::UnsetValue}; ClassDefNV(TrackingFrameInfo, 1); }; -} // namespace its -} // namespace o2 + +} // namespace o2::its #endif /* TRACKINGITSU_INCLUDE_CACLUSTER_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h index 41077c2f0eeb1..bcb8a98a62cab 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h @@ -9,229 +9,85 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef O2_ITSMFT_TRACKING_LINE_H_ -#define O2_ITSMFT_TRACKING_LINE_H_ +#ifndef O2_ITS_CLUSTERLINES_H +#define O2_ITS_CLUSTERLINES_H -#include +#include #include +#include +#include +#include #include "ITStracking/Cluster.h" -#include "ITStracking/Definitions.h" +#include "ITStracking/Constants.h" #include "ITStracking/Tracklet.h" -#include "GPUCommonMath.h" +#include "GPUCommonRtypes.h" -namespace o2 -{ -namespace its +namespace o2::its { struct Line final { - GPUhd() Line(); - GPUhd() Line(const Line&); - Line(std::array firstPoint, std::array secondPoint); - GPUhd() Line(const float firstPoint[3], const float secondPoint[3]); - GPUhd() Line(const Tracklet&, const Cluster*, const Cluster*); - - inline static float getDistanceFromPoint(const Line& line, const std::array& point); - GPUhd() static float getDistanceFromPoint(const Line& line, const float point[3]); - static std::array getDCAComponents(const Line& line, const std::array point); - GPUhd() static void getDCAComponents(const Line& line, const float point[3], float destArray[6]); - GPUhd() static float getDCA(const Line&, const Line&, const float precision = 1e-14); - static bool areParallel(const Line&, const Line&, const float precision = 1e-14); - GPUhd() unsigned char isEmpty() const { return (originPoint[0] == 0.f && originPoint[1] == 0.f && originPoint[2] == 0.f) && - (cosinesDirector[0] == 0.f && cosinesDirector[1] == 0.f && cosinesDirector[2] == 0.f); } - GPUhd() void print() const; - bool operator==(const Line&) const; - bool operator!=(const Line&) const; - - float originPoint[3], cosinesDirector[3]; // std::array originPoint, cosinesDirector; - float weightMatrix[6] = {1., 0., 0., 1., 0., 1.}; // std::array weightMatrix; - // weightMatrix is a symmetric matrix internally stored as - // 0 --> row = 0, col = 0 - // 1 --> 0,1 - // 2 --> 0,2 - // 3 --> 1,1 - // 4 --> 1,2 - // 5 --> 2,2 - // Debug quantities -}; - -GPUhdi() Line::Line() : weightMatrix{1., 0., 0., 1., 0., 1.} -{ -} - -GPUhdi() Line::Line(const Line& other) -{ - for (int i{0}; i < 3; ++i) { - originPoint[i] = other.originPoint[i]; - cosinesDirector[i] = other.cosinesDirector[i]; - } - for (int i{0}; i < 6; ++i) { - weightMatrix[i] = other.weightMatrix[i]; - } -} - -GPUhdi() Line::Line(const float firstPoint[3], const float secondPoint[3]) -{ - for (int i{0}; i < 3; ++i) { - originPoint[i] = firstPoint[i]; - cosinesDirector[i] = secondPoint[i] - firstPoint[i]; - } - - float inverseNorm{1.f / o2::gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + - cosinesDirector[2] * cosinesDirector[2])}; - - for (int index{0}; index < 3; ++index) { - cosinesDirector[index] *= inverseNorm; - } -} - -GPUhdi() Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters) -{ - originPoint[0] = innerClusters[tracklet.firstClusterIndex].xCoordinate; - originPoint[1] = innerClusters[tracklet.firstClusterIndex].yCoordinate; - originPoint[2] = innerClusters[tracklet.firstClusterIndex].zCoordinate; - - cosinesDirector[0] = outerClusters[tracklet.secondClusterIndex].xCoordinate - innerClusters[tracklet.firstClusterIndex].xCoordinate; - cosinesDirector[1] = outerClusters[tracklet.secondClusterIndex].yCoordinate - innerClusters[tracklet.firstClusterIndex].yCoordinate; - cosinesDirector[2] = outerClusters[tracklet.secondClusterIndex].zCoordinate - innerClusters[tracklet.firstClusterIndex].zCoordinate; - - float inverseNorm{1.f / o2::gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + - cosinesDirector[2] * cosinesDirector[2])}; - - for (int index{0}; index < 3; ++index) { - cosinesDirector[index] *= inverseNorm; - } -} - -// static functions: -inline float Line::getDistanceFromPoint(const Line& line, const std::array& point) -{ - float DCASquared{0}; - float cdelta{0}; - for (int i{0}; i < 3; ++i) { - cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); - } - for (int i{0}; i < 3; ++i) { - DCASquared += (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta) * - (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta); - } - return o2::gpu::CAMath::Sqrt(DCASquared); -} - -GPUhdi() float Line::getDistanceFromPoint(const Line& line, const float point[3]) -{ - float DCASquared{0}; - float cdelta{0}; - for (int i{0}; i < 3; ++i) { - cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); - } - for (int i{0}; i < 3; ++i) { - DCASquared += (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta) * - (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta); - } - return o2::gpu::CAMath::Sqrt(DCASquared); -} - -GPUhdi() float Line::getDCA(const Line& firstLine, const Line& secondLine, const float precision) -{ - float normalVector[3]; - normalVector[0] = firstLine.cosinesDirector[1] * secondLine.cosinesDirector[2] - - firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1]; - normalVector[1] = -firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2] + - firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]; - normalVector[2] = firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1] - - firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]; - - float norm{0.f}, distance{0.f}; - for (int i{0}; i < 3; ++i) { - norm += normalVector[i] * normalVector[i]; - distance += (secondLine.originPoint[i] - firstLine.originPoint[i]) * normalVector[i]; - } - if (norm > precision) { - return o2::gpu::CAMath::Abs(distance / o2::gpu::CAMath::Sqrt(norm)); - } else { -#if defined(__CUDACC__) || defined(__HIPCC__) - float stdOriginPoint[3]; - for (int i{0}; i < 3; ++i) { - stdOriginPoint[i] = secondLine.originPoint[1]; - } -#else - std::array stdOriginPoint = {}; - std::copy_n(secondLine.originPoint, 3, stdOriginPoint.begin()); +#if !defined(__HIPCC__) && !defined(__CUDACC__) // hide the class completely for gpu-cc + using SVector3f = ROOT::Math::SVector; + using SMatrix3f = ROOT::Math::SMatrix>; + + Line() = default; + Line(const Tracklet&, const Cluster*, const Cluster*); + bool operator==(const Line&) const = default; + + static float getDistance2FromPoint(const Line& line, const std::array& point); + static float getDistanceFromPoint(const Line& line, const std::array& point); + static SMatrix3f getDCAComponents(const Line& line, const std::array& point); + static float getDCA2(const Line&, const Line&, const float precision = constants::Tolerance); + static float getDCA(const Line&, const Line&, const float precision = constants::Tolerance); + bool isEmpty() const noexcept; + void print() const; + + SVector3f originPoint; + SVector3f cosinesDirector; + TimeEstBC mTime; + + ClassDefNV(Line, 1); #endif - return getDistanceFromPoint(firstLine, stdOriginPoint); - } -} - -GPUhdi() void Line::getDCAComponents(const Line& line, const float point[3], float destArray[6]) -{ - float cdelta{0.}; - for (int i{0}; i < 3; ++i) { - cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); - } - - destArray[0] = line.originPoint[0] - point[0] + line.cosinesDirector[0] * cdelta; - destArray[3] = line.originPoint[1] - point[1] + line.cosinesDirector[1] * cdelta; - destArray[5] = line.originPoint[2] - point[2] + line.cosinesDirector[2] * cdelta; - destArray[1] = std::sqrt(destArray[0] * destArray[0] + destArray[3] * destArray[3]); - destArray[2] = std::sqrt(destArray[0] * destArray[0] + destArray[5] * destArray[5]); - destArray[4] = std::sqrt(destArray[3] * destArray[3] + destArray[5] * destArray[5]); -} - -inline bool Line::operator==(const Line& rhs) const -{ - bool val{false}; - for (int i{0}; i < 3; ++i) { - val &= this->originPoint[i] == rhs.originPoint[i]; - } - return val; -} - -inline bool Line::operator!=(const Line& rhs) const -{ - bool val; - for (int i{0}; i < 3; ++i) { - val &= this->originPoint[i] != rhs.originPoint[i]; - } - return val; -} - -GPUhdi() void Line::print() const -{ - printf("Line: originPoint = (%f, %f, %f), cosinesDirector = (%f, %f, %f)\n", - originPoint[0], originPoint[1], originPoint[2], cosinesDirector[0], cosinesDirector[1], cosinesDirector[2]); -} +}; class ClusterLines final { +#if !defined(__HIPCC__) && !defined(__CUDACC__) // hide the class completely for gpu-cc + using SMatrix3 = ROOT::Math::SMatrix>; + using SMatrix3f = ROOT::Math::SMatrix>; + using SVector3 = ROOT::Math::SVector; + public: ClusterLines() = default; - ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine, - const bool weight = false); - ClusterLines(const Line& firstLine, const Line& secondLine); - void add(const int& lineLabel, const Line& line, const bool& weight = false); + ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine); + ClusterLines(gsl::span lineLabels, gsl::span lines); + void add(const int lineLabel, const Line& line); void computeClusterCentroid(); - inline std::vector& getLabels() - { - return mLabels; - } - inline int getSize() const { return mLabels.size(); } - inline std::array getVertex() const { return mVertex; } - inline std::array getRMS2() const { return mRMS2; } - inline float getAvgDistance2() const { return mAvgDistance2; } - - bool operator==(const ClusterLines&) const; + void accumulate(const Line& line); + bool isValid() const noexcept { return mIsValid; } + auto const& getVertex() const { return mVertex; } + const float* getRMS2() const { return mRMS2.Array(); } + float getAvgDistance2() const { return mAvgDistance2; } + auto getSize() const noexcept { return mLabels.size(); } + auto& getLabels() const noexcept { return mLabels; } + const auto& getTimeStamp() const noexcept { return mTime; } + bool operator==(const ClusterLines& rhs) const noexcept; + float getR2() const noexcept { return (mVertex[0] * mVertex[0]) + (mVertex[1] * mVertex[1]); } + float getR() const noexcept { return std::sqrt(getR2()); } protected: - std::array mAMatrix; // AX=B - std::array mBMatrix; // AX=B - std::vector mLabels; // labels - std::array mWeightMatrix = {0.f}; // weight matrix - std::array mVertex = {0.f}; // cluster centroid position - std::array mRMS2 = {0.f}; // symmetric matrix: diagonal is RMS2 - float mAvgDistance2 = 0.f; // substitute for chi2 + SMatrix3 mAMatrix; // AX=B, symmetric normal matrix + SVector3 mBMatrix; // AX=B, right-hand side + std::array mVertex = {}; // cluster centroid position + SMatrix3f mRMS2; // symmetric matrix: diagonal is RMS2 + float mAvgDistance2 = 0.f; // substitute for chi2 + bool mIsValid = false; // true if linear system was solved successfully + TimeEstBC mTime; // time stamp + std::vector mLabels; // contributing labels + + ClassDefNV(ClusterLines, 1); +#endif }; -} // namespace its -} // namespace o2 -#endif /* O2_ITSMFT_TRACKING_LINE_H_ */ +} // namespace o2::its +#endif /* O2_ITS_CLUSTERLINES_H */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index ca70607a9ed10..8e1c68fc31c6c 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -16,46 +16,77 @@ #ifndef TRACKINGITSU_INCLUDE_CONFIGURATION_H_ #define TRACKINGITSU_INCLUDE_CONFIGURATION_H_ +#include #ifndef GPUCA_GPUCODE_DEVICE -#include -#include +#include +#include #include -#include #endif +#include "CommonUtils/EnumFlags.h" #include "DetectorsBase/Propagator.h" #include "ITStracking/Constants.h" +#include "ITStracking/LayerMask.h" -namespace o2 -{ -namespace its +namespace o2::its { -template -class Configuration : public Param -{ - public: - static Configuration& getInstance() +// Steering of dedicated steps in an iteration +enum class IterationStep : uint16_t { + FirstPass = 0, + RebuildClusterLUT, + UseUPCMask, + SelectUPCVertices, + ResetVertices, + SkipROFsAboveThreshold, + MarkVerticesAsUPC, + TrackFollowerTop, + TrackFollowerBot, +}; +using IterationSteps = o2::utils::EnumFlags; + +struct TrackingParameters { + LayerMask getActiveLayerMask() const noexcept { - static Configuration instance; - return instance; + return LayerMask::span(0, NLayers - 1) & ~InactiveLayerMask; } - Configuration(const Configuration&) = delete; - const Configuration& operator=(const Configuration&) = delete; - private: - Configuration() = default; -}; + LayerMask getSeedingLayerMask() const noexcept + { + const auto activeLayers = getActiveLayerMask(); + return SeedingLayers.empty() ? activeLayers : (SeedingLayers & activeLayers); + } -struct TrackingParameters { - TrackingParameters& operator=(const TrackingParameters& t) = default; + LayerMask getNonSeedingLayerMask() const noexcept + { + return ~(getSeedingLayerMask()); + } - int CellMinimumLevel(); - int CellsPerRoad() const { return NLayers - 2; } - int TrackletsPerRoad() const { return NLayers - 1; } + int getNSeedingLayers() const noexcept + { + return getSeedingLayerMask().count(); + } + int getMinSeedingClusters() const noexcept + { + const int minClusters = MinTrackLength - (MaxHoles > 0 ? MaxHoles : 0); + const int minClustersWithCells = minClusters > constants::ClustersPerCell ? minClusters : constants::ClustersPerCell; + const int nSeedingLayers = getNSeedingLayers(); + return minClustersWithCells < nSeedingLayers ? minClustersWithCells : nSeedingLayers; + } + + int CellMinimumLevel() const noexcept + { + return getMinSeedingClusters() - constants::ClustersPerCell + 1; + } + int NeighboursPerRoad() const noexcept { return getNSeedingLayers() - 3; } + int CellsPerRoad() const noexcept { return getNSeedingLayers() - 2; } + int TrackletsPerRoad() const noexcept { return getNSeedingLayers() - 1; } + std::string asString() const; + + IterationSteps PassFlags{IterationStep::FirstPass, IterationStep::RebuildClusterLUT}; int NLayers = 7; - int DeltaROF = 0; + std::vector AddTimeError = {0, 0, 0, 0, 0, 0, 0}; std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; std::vector LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; std::vector LayerxX0 = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; @@ -66,99 +97,106 @@ struct TrackingParameters { int PhiBins{128}; bool UseDiamond = false; float Diamond[3] = {0.f, 0.f, 0.f}; + float DiamondCov[6] = {25.e-6f, 0.f, 0.f, 25.e-6f, 0.f, 36.f}; /// General parameters - int ClusterSharing = 0; int MinTrackLength = 7; + int MaxHoles = 0; + LayerMask HoleLayerMask = 0; + LayerMask InactiveLayerMask = 0; + LayerMask SeedingLayers = 0; float NSigmaCut = 5; float PVres = 1.e-2f; /// Trackleting cuts float TrackletMinPt = 0.3f; - float TrackletsPerClusterLimit = 2.f; /// Cell finding cuts float CellDeltaTanLambdaSigma = 0.007f; - float CellsPerClusterLimit = 2.f; /// Fitter parameters o2::base::PropagatorImpl::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; - unsigned long MaxMemory = 12000000000UL; float MaxChi2ClusterAttachment = 60.f; float MaxChi2NDF = 30.f; - bool UseTrackFollower = false; - bool FindShortTracks = false; + int ReseedIfShorter = 6; // reseed for the final fit track with the length shorter than this + std::vector MinPt = {0.f, 0.f, 0.f, 0.f}; + LayerMask StartLayerMask = 0x7F; + bool RepeatRefitOut = false; // repeat outward refit using inward refit as a seed + bool ShiftRefToCluster = true; // TrackFit: after update shift the linearization reference to cluster + bool PerPrimaryVertexProcessing = false; + bool SaveTimeBenchmarks = false; + bool DoUPCIteration = false; + bool FataliseUponFailure = true; + bool CreateArtefactLabels{false}; + float TrackFollowerNSigmaCutZ = 1.f; + float TrackFollowerNSigmaCutPhi = 1.f; + int TrackFollowerMaxHypotheses = 1; + bool PrintMemory = false; // print allocator usage in epilog report + size_t MaxMemory = std::numeric_limits::max(); + bool DropTFUponFailure = false; + + // Selections on tracks sharing clusters + bool AllowSharingFirstCluster = false; + float SharedClusterMaxDeltaPhi = 0.05f; // For tracks sharing clusters, maximum allowed delta phi at the cluster position + float SharedClusterMaxDeltaEta = 0.03f; // For tracks sharing clusters, maximum allowed delta eta at the cluster position + bool SharedClusterOppositeSign = false; // For tracks sharing clusters, require opposite sign of the tracklets + int SharedMaxClusters = 0; // Maximal allowed shared clusters (excluding first cluster) }; -inline int TrackingParameters::CellMinimumLevel() -{ - return MinTrackLength - constants::its::ClustersPerCell + 1; -} - struct VertexingParameters { - bool allowSingleContribClusters = false; + std::string asString() const; + + IterationSteps PassFlags{IterationStep::FirstPass, IterationStep::ResetVertices}; std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; std::vector LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; - int ZBins{1}; - int PhiBins{128}; - - float zCut = 0.002f; // 0.002f - float phiCut = 0.005f; // 0.005f - float pairCut = 0.04f; - float clusterCut = 0.8f; - float histPairCut = 0.04f; - float tanLambdaCut = 0.002f; // tanLambda = deltaZ/deltaR - float lowMultXYcut2 = 0.01f; // XY cut for low-multiplicity pile up - float maxZPositionAllowed = 25.f; - int clusterContributorsCut = 16; - int maxTrackletsPerCluster = 2e3; + int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a round + int ZBins = 1; + int PhiBins = 128; + float zCut = -1.f; + float phiCut = -1.f; + float pairCut = -1.f; + float clusterCut = -1.f; + float coarseZWindow = -1.f; + float seedDedupZCut = -1.f; + float refitDedupZCut = -1.f; + float duplicateZCut = -1.f; + float finalSelectionZCut = -1.f; + float duplicateDistance2Cut = -1.f; + float tanLambdaCut = -1.f; + float NSigmaCut = -1; + float maxZPositionAllowed = -1.f; + int clusterContributorsCut = -1; + int suppressLowMultDebris = -1; + int seedMemberRadiusTime = -1; + int seedMemberRadiusZ = -1; + int maxTrackletsPerCluster = -1; int phiSpan = -1; int zSpan = -1; + bool SaveTimeBenchmarks = false; + + bool useTruthSeeding = false; // overwrite found vertices with MC events int nThreads = 1; + bool PrintMemory = false; // print allocator usage in epilog report + size_t MaxMemory = std::numeric_limits::max(); + bool DropTFUponFailure = false; }; -struct TimeFrameGPUParameters { - TimeFrameGPUParameters() = default; - TimeFrameGPUParameters(size_t cubBufferSize, - size_t maxTrkClu, - size_t cluLayCap, - size_t cluROfCap, - size_t maxTrkCap, - size_t maxVertCap, - size_t maxROFs); - - size_t tmpCUBBufferSize = 1e5; // In average in pp events there are required 4096 bytes - size_t maxTrackletsPerCluster = 1e2; - size_t clustersPerLayerCapacity = 2.5e5; - size_t clustersPerROfCapacity = 1.5e3; - size_t trackletsCapacity = maxTrackletsPerCluster * clustersPerROfCapacity; - size_t validatedTrackletsCapacity = 1e3; - size_t cellsLUTsize = validatedTrackletsCapacity; - size_t maxNeighboursSize = 1e2; - size_t neighboursLUTsize = maxNeighboursSize; - size_t maxLinesCapacity = 1e2; - size_t maxVerticesCapacity = 5e4; - size_t nMaxROFs = 1e3; - size_t nTimeFrameChunks = 3; - int maxGPUMemoryGB = -1; +namespace TrackingMode +{ +enum Type : int8_t { + Unset = -1, // Special value to leave a default in case we want to override via Configurable Params + Sync = 0, + Async = 1, + Cosmics = 2, + Off = 3, }; -inline TimeFrameGPUParameters::TimeFrameGPUParameters(size_t cubBufferSize, - size_t maxTrkClu, - size_t cluLayCap, - size_t cluROfCap, - size_t maxTrkCap, - size_t maxVertCap, - size_t maxROFs) : tmpCUBBufferSize{cubBufferSize}, - maxTrackletsPerCluster{maxTrkClu}, - clustersPerLayerCapacity{cluLayCap}, - clustersPerROfCapacity{cluROfCap}, - maxLinesCapacity{maxTrkCap}, - maxVerticesCapacity{maxVertCap}, - nMaxROFs{maxROFs} -{ - trackletsCapacity = maxTrackletsPerCluster * clustersPerLayerCapacity; -} +Type fromString(std::string_view str); +std::string toString(Type mode); + +std::vector getTrackingParameters(Type mode); +std::vector getVertexingParameters(Type mode); + +}; // namespace TrackingMode -} // namespace its -} // namespace o2 +} // namespace o2::its #endif /* TRACKINGITSU_INCLUDE_CONFIGURATION_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h index 3eaeee9ab30c9..a85578deea9a2 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h @@ -16,108 +16,44 @@ #ifndef TRACKINGITSU_INCLUDE_CONSTANTS_H_ #define TRACKINGITSU_INCLUDE_CONSTANTS_H_ -#ifndef GPUCA_GPUCODE_DEVICE -#include -#include -#endif +#include +#include -#include "ITStracking/Definitions.h" -#include "CommonConstants/MathConstants.h" -#include "GPUCommonMath.h" -#include "GPUCommonDef.h" - -namespace o2 -{ -namespace its +namespace o2::its::constants { -namespace constants -{ -constexpr float MB = 1024.f * 1024.f; -constexpr float GB = 1024.f * 1024.f * 1024.f; +constexpr float KB = 1024.f; +constexpr float MB = KB * KB; +constexpr float GB = MB * KB; constexpr bool DoTimeBenchmarks = true; - -namespace math -{ -constexpr float Pi{3.14159265359f}; -constexpr float TwoPi{2.0f * Pi}; -constexpr float FloatMinThreshold{1e-20f}; -} // namespace math - -namespace its -{ -constexpr int LayersNumberVertexer{3}; -constexpr int ClustersPerCell{3}; -constexpr int UnusedIndex{-1}; -constexpr float Resolution{0.0005f}; - -GPUhdi() constexpr GPUArray VertexerHistogramVolume() -{ - return GPUArray{{1.98, 1.98, 40.f}}; -} -} // namespace its - -namespace its2 -{ -constexpr int LayersNumber{7}; -constexpr int TrackletsPerRoad{LayersNumber - 1}; -constexpr int CellsPerRoad{LayersNumber - 2}; - -GPUhdi() constexpr GPUArray LayersZCoordinate() -{ - constexpr double s = 1.; // safety margin - return GPUArray{{16.333f + s, 16.333f + s, 16.333f + s, 42.140f + s, 42.140f + s, 73.745f + s, 73.745f + s}}; -} -GPUhdi() constexpr GPUArray LayersRCoordinate() -{ - return GPUArray{{2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}}; -} - -constexpr int ZBins{256}; -constexpr int PhiBins{128}; -constexpr float InversePhiBinSize{PhiBins / constants::math::TwoPi}; -GPUhdi() constexpr GPUArray InverseZBinSize() -{ - constexpr auto zSize = LayersZCoordinate(); - return GPUArray{{0.5f * ZBins / (zSize[0]), 0.5f * ZBins / (zSize[1]), 0.5f * ZBins / (zSize[2]), - 0.5f * ZBins / (zSize[3]), 0.5f * ZBins / (zSize[4]), 0.5f * ZBins / (zSize[5]), - 0.5f * ZBins / (zSize[6])}}; -} -inline float getInverseZCoordinate(const int layerIndex) -{ - return 0.5f * ZBins / LayersZCoordinate()[layerIndex]; -} - -GPUhdi() int getZBinIndex(const int layerIndex, const float zCoordinate) -{ - return (zCoordinate + LayersZCoordinate()[layerIndex]) * - InverseZBinSize()[layerIndex]; -} - -GPUhdi() int getPhiBinIndex(const float currentPhi) -{ - return (currentPhi * InversePhiBinSize); -} - -GPUhdi() int getBinIndex(const int zIndex, const int phiIndex) -{ - return o2::gpu::GPUCommonMath::Min(phiIndex * ZBins + zIndex, - ZBins * PhiBins - 1); -} - -GPUhdi() constexpr int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } - -} // namespace its2 - -namespace pdgcodes -{ -constexpr int PionCode{211}; -} -} // namespace constants -#ifndef __OPENCL__ /// FIXME: this is for compatibility with OCL -typedef std::vector> index_table_t; -#endif -} // namespace its -} // namespace o2 +constexpr bool SaveTimeBenchmarks = false; +constexpr float Tolerance = 1e-12; // numerical tolerance +constexpr int ClustersPerCell = 3; // number of clusters for a cell +constexpr int UnusedIndex = -1; // global unused flag +constexpr float UnsetValue = -999.f; // global unset value +constexpr float Radl = 9.36f; // Radiation length of Si [cm] +constexpr float Rho = 2.33f; // Density of Si [g/cm^3] +constexpr int MaxIter = 4; // Max. supported iterations +constexpr int MaxSelectedTrackletsPerCluster = 100; // vertexer: max lines per cluster +constexpr int NumberOfConcurrentSeeds = 16; // default split per worker for the final track fit/extraploation step +constexpr int MinNumberOfConcurrentSeeds = (1 << 8); // minimum chunk size for a worker for the final track fit/extraploation step +constexpr int MaxNumberOfConcurrentSeeds = (1 << 12); // maximum chunk size for a worker for the final track fit/extraploation step +constexpr float MaxTrackSeedQ2Pt = 1.e3f; // maximum q/pt for track seeds +constexpr int GPUBlocks = 60; // default CUDA/HIP launch blocks +constexpr int GPUThreads = 256; // default CUDA/HIP launch threads +constexpr int GPUThreadsTotal = GPUBlocks * GPUThreads; // default CUDA/HIP total launched threads + +namespace helpers +{ + +// initialize a std::array at compile time fully with T +template +constexpr std::array initArray() +{ + return [](std::index_sequence) { return std::array{(static_cast(Is), Value)...}; }(std::make_index_sequence{}); +} + +} // namespace helpers +} // namespace o2::its::constants #endif /* TRACKINGITSU_INCLUDE_CONSTANTS_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h index a1d2fa338ba63..d79ea8c8bece8 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -15,124 +15,54 @@ #ifndef TRACKINGITS_DEFINITIONS_H_ #define TRACKINGITS_DEFINITIONS_H_ -// #define CA_DEBUG -// #define VTX_DEBUG -#define __USE_GPU_TRACER__ +#include +#include -template -void discardResult(const T&) +namespace o2::its { -} -#ifndef GPUCA_GPUCODE_DEVICE -#include -#endif - -#ifdef CA_DEBUG -#define CA_DEBUGGER(x) x -#else -#define CA_DEBUGGER(x) \ - do { \ - } while (0) -#endif - -#if defined(__CUDA_ARCH__) // ???? -#define TRACKINGITSU_GPU_DEVICE -#endif - -#if defined(__CUDACC__) || defined(__HIPCC__) -#define MATH_CEIL ceil - -#ifndef GPUCA_GPUCODE_DEVICE -#include -#endif -#include "../GPU/ITStrackingGPU/Array.h" - -template -using GPUArray = o2::its::gpu::Array; - -#ifdef __CUDACC__ -#define GPU_ARCH "CUDA" - -typedef cudaStream_t GPUStream; -inline int getGPUCores(const int major, const int minor) -{ - // Defines for GPU Architecture types (using the SM version to determine the # of cores per SM - typedef struct +enum class TrackletMode { + Layer0Layer1 = 0, + Layer1Layer2 = 2 +}; + +template +using maybe_const = typename std::conditional::type; + +// simple implemnetion of logging with exp. backoff +struct LogLogThrottler { + uint64_t evCount{0}; + uint64_t nextLog{1}; + int32_t iteration{-1}; + int32_t layer{-1}; + bool needToLog(int32_t iter, int32_t lay) { - int SM; // 0xMm (hexidecimal notation), M = SM Major version, and m = SM minor version - int Cores; - } sSMtoCores; - - sSMtoCores nGpuArchCoresPerSM[] = - { - {0x20, 32}, // Fermi Generation (SM 2.0) GF100 class - {0x21, 48}, // Fermi Generation (SM 2.1) GF10x class - {0x30, 192}, // Kepler Generation (SM 3.0) GK10x class - {0x32, 192}, // Kepler Generation (SM 3.2) GK10x class - {0x35, 192}, // Kepler Generation (SM 3.5) GK11x class - {0x37, 192}, // Kepler Generation (SM 3.7) GK21x class - {0x50, 128}, // Maxwell Generation (SM 5.0) GM10x class - {0x52, 128}, // Maxwell Generation (SM 5.2) GM20x class - {0x53, 128}, // Maxwell Generation (SM 5.3) GM20x class - {0x60, 64}, // Pascal Generation (SM 6.0) GP100 class - {0x61, 128}, // Pascal Generation (SM 6.1) GP10x class - {0x62, 128}, // Pascal Generation (SM 6.2) GP10x class - {0x70, 64}, // Volta Generation (SM 7.0) GV100 class - {0x72, 64}, // Volta Generation (SM 7.2) GV10B class - {0x75, 64}, // Turing Generation (SM 7.5) TU1xx class - {-1, -1}}; - - int index = 0; - - while (nGpuArchCoresPerSM[index].SM != -1) { - if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor)) { - return nGpuArchCoresPerSM[index].Cores; + if (iteration != iter || layer != lay) { + iteration = iter; + layer = lay; + evCount = 0; + nextLog = 1; } - - index++; + if (++evCount > nextLog) { + nextLog *= 2; + return true; + } + return false; } +}; - // If we don't find the values, we default use the previous one to run properly - return nGpuArchCoresPerSM[index - 1].Cores; -} -inline int getGPUMaxThreadsPerComputingUnit() -{ - return 8; -} - -#else // __HIPCC__ -#define GPU_ARCH "HIP" -typedef hipStream_t GPUStream; -inline int getGPUCores(const int major, const int minor) -{ - // Hardcoded result for AMD RADEON WX 9100, to be decided if and how determine this paramter - return 4096; -} - -inline int getGPUMaxThreadsPerComputingUnit() -{ - return 8; -} -#endif +struct TimingStats { + std::uint64_t calls = 0; + double totalTimeMs = 0.; -#else -#define MATH_CEIL std::ceil -#ifndef __VECTOR_TYPES_H__ -#include "GPUCommonDef.h" -#endif -#ifndef __OPENCL__ -#include -template -using GPUArray = std::array; -#else -#include "../GPU/ITStrackingGPU/Array.h" -template -using GPUArray = o2::its::gpu::Array; -#endif + void add(double timeMs) + { + ++calls; + totalTimeMs += timeMs; + } + double averageTimeMs() const { return calls ? totalTimeMs / static_cast(calls) : 0.; } +}; -typedef struct _dummyStream { -} GPUStream; -#endif +} // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h new file mode 100644 index 0000000000000..7d1e98736db2c --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h @@ -0,0 +1,86 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ExternalAllocator.h +/// \brief +/// + +#ifndef TRACKINGITSU_INCLUDE_EXTERNALALLOCATOR_H_ +#define TRACKINGITSU_INCLUDE_EXTERNALALLOCATOR_H_ + +#include +#include "GPUO2ExternalUser.h" +#include "Base/GPUMemoryResource.h" + +namespace o2::its +{ + +class ExternalAllocator +{ + using Type = std::underlying_type_t; + + public: + virtual void deallocate(char*, size_t) = 0; + virtual void* allocate(size_t) = 0; + void* allocate(size_t s, Type type) + { + auto old = mType; + mType = type; + void* p = allocate(s); + mType = old; + return p; + } + void* allocateStack(size_t s) + { + return allocate(s, (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + } + virtual void pushTagOnStack(uint64_t) = 0; + virtual void popTagOffStack(uint64_t) = 0; + + void setType(Type t) noexcept { mType = t; } + Type getType() const noexcept { return mType; } + + protected: + Type mType; +}; + +class ExternalAllocatorAdaptor final : public std::pmr::memory_resource +{ + public: + explicit ExternalAllocatorAdaptor(ExternalAllocator* alloc) : mAlloc(alloc) {} + + protected: + void* do_allocate(size_t bytes, size_t alignment) override + { + void* p = mAlloc->allocate(bytes, o2::gpu::GPUMemoryResource::MemoryType::MEMORY_HOST); + if (!p) { + throw std::bad_alloc(); + } + return p; + } + + void do_deallocate(void* p, size_t bytes, size_t) override + { + mAlloc->deallocate(static_cast(p), bytes); + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override + { + return this == &other; + } + + private: + ExternalAllocator* mAlloc; +}; + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h new file mode 100644 index 0000000000000..f94c7c2034b46 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h @@ -0,0 +1,91 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FastMultEst.h +/// \brief Fast multiplicity estimator for ITS +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_ITS_FASTMULTEST_ +#define ALICEO2_ITS_FASTMULTEST_ + +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DataFormatsITS/Vertex.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/PhysTrigger.h" +#include "ITStracking/FastMultEstConfig.h" +#include "ITStracking/ROFLookupTables.h" +#include +#include + +namespace o2::its +{ + +struct FastMultEst { + + static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; + using ROFOverlapTableN = ROFOverlapTable; + using ROFMaskTableN = ROFMaskTable; + + float mult = 0.; /// estimated signal clusters multiplicity on the selected multiplicity layer + float noisePerChip = 0.; /// imposed noise per chip (when enabled by configuration) + float cov[3] = {0.}; /// retained for compatibility; set to zero in single-layer mode + float chi2 = 0.; /// retained for compatibility; set to zero in single-layer mode + int nLayersUsed = 0; /// number of layers used by estimator (0/1 in single-layer mode) + uint32_t lastRandomSeed = 0; /// state of the gRandom before + FastMultEst(); + + static uint32_t getCurrentRandomSeed(); + int selectROFs(const std::array, NLayers>& rofs, + const std::array, NLayers>& clus, + const gsl::span trig, + uint32_t firstTForbit, + bool doStaggering, + const ROFOverlapTableN::View& overlapView, + ROFMaskTableN& sel); + void selectROFsWithVertices(const auto& vertices, const ROFOverlapTableN::View& overlapView, ROFMaskTableN& sel) const + { + const auto& multEstConf = FastMultEstConfig::Instance(); + if (!multEstConf.isVtxMultCutRequested()) { + return; + } + + for (const auto& vertex : vertices) { + if (!multEstConf.isPassingVtxMultCut(vertex.getNContributors())) { + const auto& timestamp{vertex.getTimeStamp()}; + for (int layer = 0; layer < NLayers; ++layer) { + uint32_t startROF = sel.getLayer(layer).getROF(timestamp.lower()); + uint32_t endROF = sel.getLayer(layer).getROF(timestamp.upper()); + for (uint32_t rof = startROF; rof <= endROF; ++rof) { + sel.setROFsEnabled(layer, rof, 0); + } + } + } + } + } + + int countClustersOnLayer(const gsl::span& clusters) const; + float process(int nClusters) + { + return FastMultEstConfig::Instance().imposeNoisePerChip > 0 ? processNoiseImposed(nClusters) : processNoiseFree(nClusters); + } + float processNoiseFree(int nClusters); + float processNoiseImposed(int nClusters); + float process(const gsl::span& clusters) + { + return process(countClustersOnLayer(clusters)); + } + static bool sSeedSet; +}; + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEstConfig.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEstConfig.h new file mode 100644 index 0000000000000..1ab9796aa8cf6 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEstConfig.h @@ -0,0 +1,57 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FastMultEstConfig.h +/// \brief Configuration parameters for ITS fast multiplicity estimator +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_ITS_FASTMULTESTCONF_H_ +#define ALICEO2_ITS_FASTMULTESTCONF_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" + +namespace o2::its +{ +struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper { + static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; + + /// acceptance correction per layer (cluster / track) + float accCorr[NLayers] = {2.95, 2.46, 2.19, 2.26, 2.06, 3.1, 3.1}; + int firstLayer = 3; /// 1st layer to account + int lastLayer = 6; /// last layer to account + float imposeNoisePerChip = 1.e-9 * 1024 * 512; // assumed noise, free parameter if<0 + + // cuts to reject to low or too high mult events + int cutMultClusLayer = NLayers - 1; /// layer used for cluster multiplicity selection (by default the outermost one) + float cutMultClusLow = 0; /// reject ROF with estimated cluster mult. below this value (no cut if <0) + float cutMultClusHigh = -1; /// reject ROF with estimated cluster mult. above this value (no cut if <0) + float cutMultVtxLow = -1; /// reject seed vertex if its multiplicity below this value (no cut if <0) + float cutMultVtxHigh = -1; /// reject seed vertex if its multiplicity above this value (no cut if <0) + float cutRandomFraction = -1.; /// apply random cut rejecting requested fraction + int randomSeed = 0; /// 0 - do not seet seed, >0 : set as is, <0 : use current time + bool preferTriggered = true; /// prefer ROFs with highest number of physics triggers + + bool isMultCutRequested() const noexcept { return cutMultClusLow >= 0.f && cutMultClusHigh > 0.f; }; + bool isVtxMultCutRequested() const noexcept { return cutMultVtxLow >= 0.f && cutMultVtxHigh > 0.f; }; + bool isRandCutRequested() const noexcept { return cutRandomFraction >= 0.; } + bool isRequested() const noexcept { return isMultCutRequested() || isVtxMultCutRequested() || isRandCutRequested(); } + bool isPassingRandomRejection() const; + bool isPassingMultCut(float mult) const { return mult >= cutMultClusLow && (mult <= cutMultClusHigh || cutMultClusHigh <= 0.f); } + bool isPassingVtxMultCut(int mult) const { return mult >= cutMultVtxLow && (mult <= cutMultVtxHigh || cutMultVtxHigh <= 0.f); } + + O2ParamDef(FastMultEstConfig, "fastMultConfig"); +}; + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h index 34431f1849e21..8adacdf58d74d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h @@ -16,68 +16,29 @@ #ifndef TRACKINGITSU_INCLUDE_EVENTLOADER_H_ #define TRACKINGITSU_INCLUDE_EVENTLOADER_H_ -#include -#include -#include #include -#include "DataFormatsITSMFT/ROFRecord.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/ROframe.h" -#include "ITStracking/Label.h" -#include "ITStracking/Road.h" -#include "ITStracking/TrackingConfigParam.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ReconstructionDataFormats/BaseCluster.h" -#include "ITSMFTReconstruction/ChipMappingITS.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/TopologyDictionary.h" +#include "DataFormatsITSMFT/ROFRecord.h" // TODO this is just included since the alignment code include it now -namespace o2 +namespace o2::its::ioutils { -class MCCompLabel; - -namespace dataformats -{ -template -class MCTruthContainer; -} - -namespace its -{ - -namespace ioutils -{ constexpr float DefClusErrorRow = o2::itsmft::SegmentationAlpide::PitchRow * 0.5; constexpr float DefClusErrorCol = o2::itsmft::SegmentationAlpide::PitchCol * 0.5; constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; -void loadEventData(ROframe& events, gsl::span clusters, - gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* clsLabels = nullptr); -int loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& events, gsl::span clusters, - gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* mClsLabels = nullptr); - void convertCompactClusters(gsl::span clusters, gsl::span::iterator& pattIt, std::vector>& output, const itsmft::TopologyDictionary* dict); -inline static const o2::itsmft::ChipMappingITS& getChipMappingITS() -{ - static const o2::itsmft::ChipMappingITS MP; - return MP; -} - -std::vector> loadLabels(const int, const std::string&); -void writeRoadsReport(std::ofstream&, std::ofstream&, std::ofstream&, const std::vector>&, - const std::unordered_map&); - template -o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator iter, const itsmft::TopologyDictionary* dict, T& sig2y, T& sig2z) +o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const itsmft::TopologyDictionary* dict, T& sig2y, T& sig2z) { auto pattID = c.getPatternID(); sig2y = ioutils::DefClusError2Row; @@ -99,7 +60,7 @@ o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, i // same method returning coordinates as an array (suitable for the TGeoMatrix) template -std::array extractClusterDataA(const itsmft::CompClusterExt& c, iterator iter, const itsmft::TopologyDictionary* dict, T& sig2y, T& sig2z) +std::array extractClusterDataA(const itsmft::CompClusterExt& c, iterator& iter, const itsmft::TopologyDictionary* dict, T& sig2y, T& sig2z) { auto pattID = c.getPatternID(); sig2y = ioutils::DefClusError2Row; @@ -119,8 +80,6 @@ std::array extractClusterDataA(const itsmft::CompClusterExt& c, iterator i } } -} // namespace ioutils -} // namespace its -} // namespace o2 +} // namespace o2::its::ioutils #endif /* TRACKINGITSU_INCLUDE_EVENTLOADER_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h index 9bdc3a813092d..a7b44e91d9093 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h @@ -16,16 +16,18 @@ #ifndef TRACKINGITSU_INCLUDE_INDEXTABLEUTILS_H_ #define TRACKINGITSU_INCLUDE_INDEXTABLEUTILS_H_ -#include "ITStracking/Constants.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/Definitions.h" +#include + +#include "ITStracking/Cluster.h" +#include "ITStracking/MathUtils.h" +#include "CommonConstants/MathConstants.h" #include "GPUCommonMath.h" #include "GPUCommonDef.h" -namespace o2 -{ -namespace its +namespace o2::its { + +template class IndexTableUtils { public: @@ -48,14 +50,15 @@ class IndexTableUtils int mNzBins = 0; int mNphiBins = 0; float mInversePhiBinSize = 0.f; - float mLayerZ[7] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; - float mInverseZBinSize[7] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + std::array mLayerZ{}; + std::array mInverseZBinSize{}; }; +template template -inline void IndexTableUtils::setTrackingParameters(const T& params) +inline void IndexTableUtils::setTrackingParameters(const T& params) { - mInversePhiBinSize = params.PhiBins / constants::math::TwoPi; + mInversePhiBinSize = params.PhiBins / o2::constants::math::TwoPI; mNzBins = params.ZBins; mNphiBins = params.PhiBins; for (int iLayer{0}; iLayer < params.LayerZ.size(); ++iLayer) { @@ -66,28 +69,33 @@ inline void IndexTableUtils::setTrackingParameters(const T& params) } } -inline float IndexTableUtils::getInverseZCoordinate(const int layerIndex) const +template +inline float IndexTableUtils::getInverseZCoordinate(const int layerIndex) const { return 0.5f * mNzBins / mLayerZ[layerIndex]; } -GPUhdi() int IndexTableUtils::getZBinIndex(const int layerIndex, const float zCoordinate) const +template +GPUhdi() int IndexTableUtils::getZBinIndex(const int layerIndex, const float zCoordinate) const { return (zCoordinate + mLayerZ[layerIndex]) * mInverseZBinSize[layerIndex]; } -GPUhdi() int IndexTableUtils::getPhiBinIndex(const float currentPhi) const +template +GPUhdi() int IndexTableUtils::getPhiBinIndex(const float currentPhi) const { return (currentPhi * mInversePhiBinSize); } -GPUhdi() int IndexTableUtils::getBinIndex(const int zIndex, const int phiIndex) const +template +GPUhdi() int IndexTableUtils::getBinIndex(const int zIndex, const int phiIndex) const { - return o2::gpu::GPUCommonMath::Min(phiIndex * mNzBins + zIndex, mNzBins * mNphiBins - 1); + return o2::gpu::GPUCommonMath::Min(phiIndex * mNzBins + zIndex, (mNzBins * mNphiBins) - 1); } -GPUhdi() int IndexTableUtils::countRowSelectedBins(const int* indexTable, const int phiBinIndex, - const int minZBinIndex, const int maxZBinIndex) const +template +GPUhdi() int IndexTableUtils::countRowSelectedBins(const int* indexTable, const int phiBinIndex, + const int minZBinIndex, const int maxZBinIndex) const { const int firstBinIndex{getBinIndex(minZBinIndex, phiBinIndex)}; const int maxBinIndex{firstBinIndex + maxZBinIndex - minZBinIndex + 1}; @@ -95,14 +103,48 @@ GPUhdi() int IndexTableUtils::countRowSelectedBins(const int* indexTable, const return indexTable[maxBinIndex] - indexTable[firstBinIndex]; } -GPUhdi() void IndexTableUtils::print() const +template +GPUhdi() void IndexTableUtils::print() const { printf("NzBins: %d, NphiBins: %d, InversePhiBinSize: %f\n", mNzBins, mNphiBins, mInversePhiBinSize); - for (int iLayer{0}; iLayer < 7; ++iLayer) { + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { printf("Layer %d: Z: %f, InverseZBinSize: %f\n", iLayer, mLayerZ[iLayer], mInverseZBinSize[iLayer]); } } -} // namespace its -} // namespace o2 +template +GPUhdi() int4 getBinsRect(const int layerIndex, + const float phi, + const float z, + const float maxdeltaz, + const float maxdeltaphi, + const IndexTableUtils& utils) +{ + const float zRangeMin = z - maxdeltaz; + const float phiRangeMin = (maxdeltaphi > o2::constants::math::PI) ? 0.f : phi - maxdeltaphi; + const float zRangeMax = z + maxdeltaz; + const float phiRangeMax = (maxdeltaphi > o2::constants::math::PI) ? o2::constants::math::TwoPI : phi + maxdeltaphi; + + if (zRangeMax < -utils.getLayerZ(layerIndex) || + zRangeMin > utils.getLayerZ(layerIndex) || zRangeMin > zRangeMax) { + return int4{-1, -1, -1, -1}; + } + + return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex, zRangeMin)), + utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), + o2::gpu::GPUCommonMath::Min(utils.getNzBins() - 1, utils.getZBinIndex(layerIndex, zRangeMax)), + utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; +} + +template +GPUhdi() int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, + const float z1, const float z2, const float maxdeltaz, const float maxdeltaphi, + const IndexTableUtils& utils) +{ + const float zMean = 0.5f * (z1 + z2); + const float zDelta = 0.5f * o2::gpu::GPUCommonMath::Abs(z1 - z2) + maxdeltaz; + return getBinsRect(layerIndex, currentCluster.phi, zMean, zDelta, maxdeltaphi, utils); +} + +} // namespace o2::its #endif /* TRACKINGITSU_INCLUDE_INDEXTABLEUTILS_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Label.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Label.h deleted file mode 100644 index ec45e6587a974..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Label.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Label.h -/// \brief -/// - -#ifndef TRACKINGITSU_INCLUDE_LABEL_H_ -#define TRACKINGITSU_INCLUDE_LABEL_H_ - -#include - -namespace o2 -{ -namespace its -{ - -struct Label final { - Label(const int, const float, const float, const float, const int, const int); - - int monteCarloId; - float transverseMomentum; - float phi; - float pseudorapidity; - int pdgCode; - int numberOfClusters; - - friend std::ostream& operator<<(std::ostream&, const Label&); -}; -} // namespace its -} // namespace o2 - -#endif /* TRACKINGITSU_INCLUDE_LABEL_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LayerMask.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LayerMask.h new file mode 100644 index 0000000000000..9fe9894b3b457 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LayerMask.h @@ -0,0 +1,115 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef TRACKINGITSU_INCLUDE_LAYERMASK_H_ +#define TRACKINGITSU_INCLUDE_LAYERMASK_H_ + +#include +#include + +#ifndef GPUCA_GPUCODE +#include +#include +#endif + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "ITStracking/Constants.h" + +namespace o2::its +{ + +struct LayerMask { + GPUhdDefault() constexpr LayerMask() noexcept = default; + GPUhdDefault() constexpr LayerMask(uint16_t mask) noexcept : mBits{mask} {} + GPUhdDefault() constexpr LayerMask(int layer0, int layer1, int layer2) noexcept + : mBits{static_cast((uint16_t(1) << layer0) | (uint16_t(1) << layer1) | (uint16_t(1) << layer2))} + { + } + GPUhdi() constexpr operator uint16_t() const noexcept { return mBits; } + GPUhdi() constexpr uint16_t value() const noexcept { return mBits; } + GPUhdi() constexpr void set(int layer) noexcept { mBits |= (uint16_t(1) << layer); } + + GPUhdi() LayerMask operator~() const noexcept { return LayerMask{static_cast(~mBits)}; } + GPUhdi() LayerMask operator&(LayerMask other) const noexcept { return LayerMask{static_cast(mBits & other.mBits)}; } + GPUhdi() LayerMask operator|(LayerMask other) const noexcept { return LayerMask{static_cast(mBits | other.mBits)}; } + GPUhdi() LayerMask& operator&=(LayerMask other) noexcept + { + mBits &= other.mBits; + return *this; + } + GPUhdi() LayerMask& operator|=(LayerMask other) noexcept + { + mBits |= other.mBits; + return *this; + } + + GPUhdi() bool empty() const noexcept { return mBits == 0; } + GPUhdi() bool has(int layer) const noexcept { return mBits & (uint16_t(1) << layer); } + GPUhdi() bool isSubsetOf(LayerMask allowed) const noexcept { return (*this & ~allowed).empty(); } + GPUhdi() bool isAllowedHoleMask(int maxHoles, LayerMask allowedHoleMask) const noexcept + { + const int allowedHoles = maxHoles > 0 ? maxHoles : 0; + return count() <= allowedHoles && isSubsetOf(allowedHoleMask); + } + GPUhdi() bool isAllowed(int maxHoles, LayerMask allowedHoleMask) const noexcept + { + return holeMask().isAllowedHoleMask(maxHoles, allowedHoleMask); + } + GPUhdi() int length() const noexcept { return empty() ? 0 : last() - first() + 1; } + GPUhdi() int count() const noexcept { return static_cast(o2::gpu::GPUCommonMath::Popcount(mBits)); } + GPUhdi() int first() const noexcept { return mBits ? static_cast(o2::gpu::GPUCommonMath::Ctz(mBits)) : constants::UnusedIndex; } + GPUhdi() int last() const noexcept { return mBits ? 31 - static_cast(o2::gpu::GPUCommonMath::Clz(mBits)) : constants::UnusedIndex; } + GPUhdi() LayerMask holeMask() const noexcept + { + return empty() ? LayerMask{0} : (span(first(), last()) & ~(*this)); + } + + GPUhdi() int slot(int layer) const noexcept + { + if (!has(layer)) { + return constants::UnusedIndex; + } + const uint32_t lowerLayers = (uint32_t(1) << layer) - 1; + return static_cast(o2::gpu::GPUCommonMath::Popcount(static_cast(mBits) & lowerLayers)); + } + + static GPUhdi() LayerMask span(int fromLayer, int toLayer) noexcept + { + if (fromLayer > toLayer) { + return 0; + } + const uint32_t upper = (uint32_t(1) << (toLayer + 1)) - 1; + const uint32_t lower = (uint32_t(1) << fromLayer) - 1; + return static_cast(upper & ~lower); + } + + static GPUhdi() LayerMask skipped(int fromLayer, int toLayer) noexcept + { + return (toLayer - fromLayer <= 1) ? LayerMask{0} : span(fromLayer + 1, toLayer - 1); + } + +#ifndef GPUCA_GPUCODE + std::string asString() const { return fmt::format("{:016b}", mBits); } +#endif + + private: + uint16_t mBits{0}; +}; + +static_assert(std::is_standard_layout_v); +static_assert(std::is_trivially_copyable_v); +static_assert(sizeof(LayerMask) == sizeof(uint16_t)); +static_assert(alignof(LayerMask) == alignof(uint16_t)); + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h new file mode 100644 index 0000000000000..0e3807aba8efb --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h @@ -0,0 +1,46 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS_TRACKING_LINE_VERTEXER_HELPERS_H_ +#define O2_ITS_TRACKING_LINE_VERTEXER_HELPERS_H_ + +#include +#include + +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ClusterLines.h" + +namespace o2::its::line_vertexer +{ + +struct Settings { + float beamX = 0.f; + float beamY = 0.f; + float pairCut = 0.f; + float pairCut2 = 0.f; + float clusterCut = 0.f; + float coarseZWindow = 0.f; + float seedDedupZCut = 0.f; + float refitDedupZCut = 0.f; + float duplicateZCut = 0.f; + float duplicateDistance2Cut = 0.f; + float finalSelectionZCut = 0.f; + float maxZ = 0.f; + int seedMemberRadiusTime = 1; + int seedMemberRadiusZ = 2; + std::shared_ptr memoryPool; +}; + +bounded_vector buildClusters(std::span lines, const Settings& settings); + +} // namespace o2::its::line_vertexer + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h index b9a2b599f0ba8..5599755c52e64 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -13,86 +13,122 @@ /// \brief /// -#ifndef TRACKINGITSU_INCLUDE_CAUTILS_H_ -#define TRACKINGITSU_INCLUDE_CAUTILS_H_ +#ifndef O2_ITS_TRACKING_MATHUTILS_H_ +#define O2_ITS_TRACKING_MATHUTILS_H_ -#ifndef GPUCA_GPUCODE_DEVICE -#include -#include -#include -#include -#endif - -#include "MathUtils/Utils.h" +#include "CommonConstants/MathConstants.h" #include "ITStracking/Constants.h" +#include "MathUtils/Utils.h" #include "GPUCommonMath.h" #include "GPUCommonDef.h" -namespace o2 +namespace o2::its::math_utils +{ + +GPUhdi() constexpr float computePhi(float x, float y) { -namespace its + return o2::math_utils::fastATan2(-y, -x) + o2::constants::math::PI; +} + +GPUhdi() constexpr float hypot(float x, float y) +{ + return o2::gpu::CAMath::Hypot(x, y); +} + +GPUhdi() constexpr float getNormalizedPhi(float phi) { + phi -= o2::constants::math::TwoPI * o2::gpu::CAMath::Floor(phi * (1.f / o2::constants::math::TwoPI)); + return phi; +} -namespace math_utils +GPUhdi() constexpr float computeNormalizedPhi(float x, float y) { -GPUhdni() float computePhi(const float, const float); -GPUhdni() float hypot(const float, const float); -GPUhdni() constexpr float getNormalizedPhi(const float); -GPUhdni() constexpr float3 crossProduct(const float3&, const float3&); -GPUhdni() float computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3); -GPUhdni() float computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3); -GPUhdni() float computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2); + return getNormalizedPhi(computePhi(x, y)); +} -} // namespace math_utils +GPUhdi() float computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3) +{ + // in case the triangle is degenerate we return infinite curvature. + const float area = ((x2 - x1) * (y3 - y1)) - ((x3 - x1) * (y2 - y1)); + if (o2::gpu::CAMath::Abs(area) < constants::Tolerance) { + return o2::constants::math::Almost0; + } + const float dx1 = x2 - x1, dy1 = y2 - y1; + const float dx2 = x3 - x2, dy2 = y3 - y2; + const float dx3 = x1 - x3, dy3 = y1 - y3; + const float d1 = o2::gpu::CAMath::Sqrt((dx1 * dx1) + (dy1 * dy1)); + const float d2 = o2::gpu::CAMath::Sqrt((dx2 * dx2) + (dy2 * dy2)); + const float d3 = o2::gpu::CAMath::Sqrt((dx3 * dx3) + (dy3 * dy3)); + return -2.f * area / (d1 * d2 * d3); +} -GPUhdi() float math_utils::computePhi(const float x, const float y) +GPUhdi() float computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3) { - //return o2::gpu::CAMath::ATan2(-yCoordinate, -xCoordinate) + constants::math::Pi; - return o2::math_utils::fastATan2(-y, -x) + constants::math::Pi; + // in case the triangle is degenerate we return set the centre to infinity. + float dx21 = x2 - x1, dx32 = x3 - x2; + if (o2::gpu::CAMath::Abs(dx21) < o2::its::constants::Tolerance || + o2::gpu::CAMath::Abs(dx32) < o2::its::constants::Tolerance) { // add small offset + x2 += 1e-4; + dx21 = x2 - x1; + dx32 = x3 - x2; + } + const float k1 = (y2 - y1) / dx21, k2 = (y3 - y2) / dx32; + if (o2::gpu::CAMath::Abs(k2 - k1) < o2::its::constants::Tolerance) { + return o2::constants::math::VeryBig; + } + return 0.5f * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1); } -GPUhdi() float math_utils::hypot(const float x, const float y) +GPUhdi() float computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2) { - return o2::gpu::CAMath::Sqrt(x * x + y * y); + // in case the points vertically align we go to pos/neg infinity. + const float d = o2::gpu::CAMath::Hypot(x1 - x2, y1 - y2); + if (o2::gpu::CAMath::Abs(d) < o2::its::constants::Tolerance) { + return ((z1 > z2) ? -1.f : 1.f) * o2::constants::math::VeryBig; + } + return (z1 - z2) / d; } -GPUhdi() constexpr float math_utils::getNormalizedPhi(const float phi) +GPUhdi() float smallestAngleDifference(float a, float b) { - return (phi < 0) ? phi + constants::math::TwoPi : (phi > constants::math::TwoPi) ? phi - constants::math::TwoPi - : phi; + return o2::gpu::CAMath::Remainderf(b - a, o2::constants::math::TwoPI); } -GPUhdi() constexpr float3 math_utils::crossProduct(const float3& firstVector, const float3& secondVector) +GPUhdi() bool isPhiDifferenceBelow(const float phiA, const float phiB, const float phiCut) { + const float deltaPhi = o2::gpu::CAMath::Abs(phiA - phiB); + return deltaPhi < phiCut || deltaPhi > o2::constants::math::TwoPI - phiCut; +} - return float3{(firstVector.y * secondVector.z) - (firstVector.z * secondVector.y), - (firstVector.z * secondVector.x) - (firstVector.x * secondVector.z), - (firstVector.x * secondVector.y) - (firstVector.y * secondVector.x)}; +GPUhdi() constexpr float Sq(float v) +{ + return v * v; } -GPUhdi() float math_utils::computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3) +GPUhdi() constexpr float SqSum(float v, float w) { - const float d = (x2 - x1) * (y3 - y2) - (x3 - x2) * (y2 - y1); - const float a = - 0.5f * ((y3 - y2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1) - (y2 - y1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2)); - const float b = - 0.5f * ((x2 - x1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2) - (x3 - x2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1)); + return Sq(v) + Sq(w); +} - return -1.f * d / o2::gpu::CAMath::Sqrt((d * x1 - a) * (d * x1 - a) + (d * y1 - b) * (d * y1 - b)); +GPUhdi() constexpr float SqSum(float u, float v, float w) +{ + return Sq(u) + SqSum(v, w); } -GPUhdi() float math_utils::computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3) +GPUhdi() constexpr float SqDiff(float x, float y) { - const float k1 = (y2 - y1) / (x2 - x1), k2 = (y3 - y2) / (x3 - x2); - return 0.5f * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1); + return Sq(x - y); } -GPUhdi() float math_utils::computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2) +GPUhdi() float MSangle(float mass, float p, float xX0) { - return (z1 - z2) / o2::gpu::CAMath::Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + if (xX0 <= 0.f) { + return 0.f; + } + float beta = p / o2::gpu::CAMath::Hypot(mass, p); + return 0.0136f * o2::gpu::CAMath::Sqrt(xX0) * (1.f + 0.038f * o2::gpu::CAMath::Log(xX0)) / (beta * p); } -} // namespace its -} // namespace o2 +} // namespace o2::its::math_utils -#endif /* TRACKINGITSU_INCLUDE_CAUTILS_H_ */ +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h new file mode 100644 index 0000000000000..e6259ee576f10 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h @@ -0,0 +1,894 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef TRACKINGITSU_INCLUDE_ROFOVERLAPTABLE_H_ +#define TRACKINGITSU_INCLUDE_ROFOVERLAPTABLE_H_ + +#include +#include +#include +#include +#include +#include + +#ifndef GPUCA_GPUCODE +#include +#include "Framework/Logger.h" +#endif + +#include "CommonConstants/LHCConstants.h" +#include "CommonDataFormat/RangeReference.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "DataFormatsITS/Vertex.h" +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" + +namespace o2::its +{ + +// Layer timing definition +struct LayerTiming { + using BCType = TimeStampType; + using BCRange = dataformats::RangeReference; + BCType mNROFsTF{0}; // number of ROFs per timeframe + BCType mROFLength{0}; // ROF length in BC + BCType mROFDelay{0}; // delay of ROFs wrt start of first orbit in TF in BC + BCType mROFBias{0}; // bias wrt to the LHC clock in BC + BCType mROFAddTimeErr{0}; // additionally imposed uncertainty on ROF time in BC + + // return start of ROF in BC + // this does not account for the opt. error! + GPUhdi() BCType getROFStartInBC(BCType rofId) const noexcept + { + assert(rofId < mNROFsTF && rofId >= 0); + return (mROFLength * rofId) + mROFDelay + mROFBias; + } + + // return end of ROF in BCs + // this does not account for the opt. error! + GPUhdi() BCType getROFEndInBC(BCType rofId) const noexcept + { + assert(rofId < mNROFsTF); + return getROFStartInBC(rofId) + mROFLength; + } + + // return (clamped) time-interval of rof + GPUhdi() TimeEstBC getROFTimeBounds(BCType rofId, bool withError = false) const noexcept + { + if (withError) { + int64_t start = getROFStartInBC(rofId); + int64_t end = getROFEndInBC(rofId); + start = o2::gpu::CAMath::Max(start - mROFAddTimeErr, int64_t(0)); + end += mROFAddTimeErr; + return {static_cast(start), static_cast(end - start)}; + } + return {getROFStartInBC(rofId), static_cast(mROFLength)}; + } + + // return which ROF this BC belongs to + GPUhdi() BCType getROF(BCType bc) const noexcept + { + const BCType offset = mROFDelay + mROFBias; + if (bc <= offset) { + return 0; + } + return (bc - offset) / mROFLength; + } + + // return which ROF this timestamp belongs by its lower edge + GPUhdi() BCType getROF(TimeStamp ts) const noexcept + { + const BCType offset = mROFDelay + mROFBias; + const BCType bc = (ts.getTimeStamp() < ts.getTimeStampError()) ? BCType(0) : static_cast(o2::gpu::CAMath::Floor(ts.getTimeStamp() - ts.getTimeStampError())); + if (bc <= offset) { + return 0; + } + return (bc - offset) / mROFLength; + } + + // return which ROF this floating point (number of BCs) time belongs + GPUhdi() BCType getROF(float time) const noexcept + { + const float offset = static_cast(mROFDelay + mROFBias); + if (time <= offset) { + return 0; + } + return static_cast((time - offset) / mROFLength); + } + + GPUhdi() bool intersectROF(BCType rof, float lower, float upper) const noexcept + { + const auto rofTS = getROFTimeBounds(rof, true); + return static_cast(rofTS.upper()) > lower && upper > static_cast(rofTS.lower()); + } + + // return clamped ROF range with strictly positive overlap with timestamp interval + GPUhdi() BCRange getROFRange(TimeStamp ts) const noexcept + { + const float lower = ts.getTimeStamp() - ts.getTimeStampError(); + const float upper = ts.getTimeStamp() + ts.getTimeStampError(); + return getROFRange(lower, upper); + } + + GPUhdi() BCRange getROFRange(TimeEstBC ts) const noexcept + { + return getROFRange(static_cast(ts.lower()), static_cast(ts.upper())); + } + + GPUhdi() BCRange getROFRange(float lower, float upper) const noexcept + { + const BCType maxROF = mNROFsTF - 1; + BCType first = o2::gpu::CAMath::Clamp(getROF(lower - mROFAddTimeErr), BCType{0}, maxROF); + BCType last = o2::gpu::CAMath::Clamp(getROF(upper + mROFAddTimeErr), BCType{0}, maxROF); + + if (first <= last && !intersectROF(first, lower, upper)) { + ++first; + } + if (last >= first && !intersectROF(last, lower, upper)) { + --last; + } + return {first, first <= last ? static_cast(last - first + 1) : BCType{0}}; + } + +#ifndef GPUCA_GPUCODE + GPUh() std::string asString() const + { + return std::format("NROFsPerTF {:4} ROFLength {:4} ({:4} per Orbit) ROFDelay {:4} ROFBias {:4} ROFAddTimeErr {:4}", mNROFsTF, mROFLength, (o2::constants::lhc::LHCMaxBunches / mROFLength), mROFDelay, mROFBias, mROFAddTimeErr); + } + + GPUh() void print() const + { + LOG(info) << asString(); + } +#endif +}; + +// Base class for lookup to define layers +template +class LayerTimingBase +{ + protected: + LayerTiming mLayers[NLayers]; + + public: + using T = LayerTiming::BCType; + LayerTimingBase() = default; + + GPUh() void defineLayer(int32_t layer, T nROFsTF, T rofLength, T rofDelay, T rofBias, T rofTE) + { + assert(layer >= 0 && layer < NLayers); + mLayers[layer] = {nROFsTF, rofLength, rofDelay, rofBias, rofTE}; + } + + GPUh() void defineLayer(int32_t layer, const LayerTiming& timing) + { + assert(layer >= 0 && layer < NLayers); + mLayers[layer] = timing; + } + + GPUhdi() const LayerTiming& getLayer(int32_t layer) const + { + assert(layer >= 0 && layer < NLayers); + return mLayers[layer]; + } + + GPUhdi() constexpr int32_t getEntries() noexcept { return NLayers; } + +#ifndef GPUCA_GPUCODE + GPUh() void print() const + { + LOGP(info, "Imposed time structure:"); + for (int32_t iL{0}; iL < NLayers; ++iL) { + LOGP(info, "\tLayer:{} {}", iL, mLayers[iL].asString()); + } + } +#endif +}; + +// GPU friendly view of the table below +template +struct ROFOverlapTableView { + const TableEntry* mFlatTable{nullptr}; + const TableIndex* mIndices{nullptr}; + const LayerTiming* mLayers{nullptr}; + + GPUhdi() const LayerTiming& getLayer(int32_t layer) const noexcept + { + assert(layer >= 0 && layer < NLayers); + return mLayers[layer]; + } + + GPUh() int32_t getClock() const noexcept + { + // we take the fastest layer as clock + int32_t fastest = 0; + uint32_t maxNROFs{0}; + for (int32_t iL{0}; iL < NLayers; ++iL) { + const auto& layer = getLayer(iL); + // by definition the fastest layer has the most ROFs + // this also solves the problem of a delay large than ROFLength + // if mNROFsTF is correct + if (layer.mNROFsTF > maxNROFs) { + fastest = iL; + maxNROFs = layer.mNROFsTF; + } + } + return fastest; + } + + GPUh() const LayerTiming& getClockLayer() const noexcept + { + return mLayers[getClock()]; + } + + GPUhdi() const TableEntry& getOverlap(int32_t from, int32_t to, size_t rofIdx) const noexcept + { + assert(from < NLayers && to < NLayers); + const size_t linearIdx = (from * NLayers) + to; + const auto& idx = mIndices[linearIdx]; + assert(rofIdx < idx.getEntries()); + return mFlatTable[idx.getFirstEntry() + rofIdx]; + } + + GPUhdi() bool doROFsOverlap(int32_t layer0, size_t rof0, int32_t layer1, size_t rof1) const noexcept + { + if (layer0 == layer1) { // layer is compatible with itself + return rof0 == rof1; + } + + assert(layer0 < NLayers && layer1 < NLayers); + const size_t linearIdx = (layer0 * NLayers) + layer1; + const auto& idx = mIndices[linearIdx]; + + if (rof0 >= idx.getEntries()) { + return false; + } + + const auto& overlap = mFlatTable[idx.getFirstEntry() + rof0]; + + if (overlap.getEntries() == 0) { + return false; + } + + const size_t firstCompatible = overlap.getFirstEntry(); + const size_t lastCompatible = firstCompatible + overlap.getEntries() - 1; + return rof1 >= firstCompatible && rof1 <= lastCompatible; + } + + GPUhdi() TimeEstBC getTimeStamp(int32_t layer0, size_t rof0, int32_t layer1, size_t rof1) const noexcept + { + assert(layer0 < NLayers && layer1 < NLayers); + assert(doROFsOverlap(layer0, rof0, layer1, rof1)); + // retrieves the combined timestamp + // e.g., taking one cluster from rof0 and one from rof1 + // and constructing a tracklet (doublet) what is its time + // this assumes that the rofs overlap, e.g. doROFsOverlap -> true + // get timestamp including margins from rof0 and rof1 + const auto t0 = mLayers[layer0].getROFTimeBounds(rof0, true); + const auto t1 = mLayers[layer1].getROFTimeBounds(rof1, true); + return t0 + t1; + } + +#ifndef GPUCA_GPUCODE + /// Print functions + GPUh() void printAll() const + { + for (int32_t i = 0; i < NLayers; ++i) { + for (int32_t j = 0; j < NLayers; ++j) { + if (i != j) { + printMapping(i, j); + } + } + } + printSummary(); + } + + GPUh() void printMapping(int32_t from, int32_t to) const + { + if (from == to) { + LOGP(error, "No self-lookup supported"); + return; + } + + constexpr int w_index = 10; + constexpr int w_first = 12; + constexpr int w_last = 12; + constexpr int w_count = 10; + + LOGF(info, "Overlap mapping: Layer %d -> Layer %d", from, to); + LOGP(info, "From: {}", mLayers[from].asString()); + LOGP(info, "To : {}", mLayers[to].asString()); + LOGF(info, "%*s | %*s | %*s | %*s", w_index, "ROF.index", w_first, "First.ROF", w_last, "Last.ROF", w_count, "Count"); + LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_index, "----------", w_first, "------------", w_last, "------------", w_count, "----------"); + + const size_t linearIdx = (from * NLayers) + to; + const auto& idx = mIndices[linearIdx]; + for (int32_t i = 0; i < idx.getEntries(); ++i) { + const auto& overlap = getOverlap(from, to, i); + LOGF(info, "%*d | %*d | %*d | %*d", w_index, i, w_first, overlap.getFirstEntry(), w_last, overlap.getEntriesBound() - 1, w_count, overlap.getEntries()); + } + } + + GPUh() void printSummary() const + { + uint32_t totalEntries{0}; + size_t flatTableSize{0}; + + for (int32_t i = 0; i < NLayers; ++i) { + for (int32_t j = 0; j < NLayers; ++j) { + if (i != j) { + const size_t linearIdx = (i * NLayers) + j; + const auto& idx = mIndices[linearIdx]; + totalEntries += idx.getEntries(); + flatTableSize += idx.getEntries(); + } + } + } + + for (int32_t i = 0; i < NLayers; ++i) { + mLayers[i].print(); + } + + const uint32_t totalBytes = (flatTableSize * sizeof(TableEntry)) + (static_cast(NLayers * NLayers) * sizeof(TableIndex)); + LOGF(info, "------------------------------------------------------------"); + LOGF(info, "Total overlap table size: %u entries", totalEntries); + LOGF(info, "Flat table size: %zu entries", flatTableSize); + LOGF(info, "Total view size: %u bytes", totalBytes); + LOGF(info, "------------------------------------------------------------"); + } +#endif +}; + +// Precalculated lookup table to find overlapping ROFs in another layer given a ROF index in the current layer +template +class ROFOverlapTable : public LayerTimingBase +{ + public: + using T = LayerTimingBase::T; + using TableEntry = dataformats::RangeReference; + using TableIndex = dataformats::RangeReference; + + using View = ROFOverlapTableView; + ROFOverlapTable() = default; + + GPUh() void init() + { + std::vector table[NLayers][NLayers]; + for (int32_t i{0}; i < NLayers; ++i) { + for (int32_t j{0}; j < NLayers; ++j) { + if (i != j) { // we do not need self-lookup + buildMapping(i, j, table[i][j]); + } + } + } + flatten(table); + } + + GPUh() View getView() const + { + View view; + view.mFlatTable = mFlatTable.data(); + view.mIndices = mIndices; + view.mLayers = this->mLayers; + return view; + } + + GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const + { + View view; + view.mFlatTable = deviceFlatTablePtr; + view.mIndices = deviceIndicesPtr; + view.mLayers = deviceLayerTimingPtr; + return view; + } + + GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); } + static GPUh() constexpr size_t getIndicesSize() { return static_cast(NLayers * NLayers); } + + private: + GPUh() void buildMapping(int32_t from, int32_t to, std::vector& table) + { + const auto& layerFrom = this->mLayers[from]; + const auto& layerTo = this->mLayers[to]; + table.resize(layerFrom.mNROFsTF); + + for (int32_t iROF{0}; iROF < layerFrom.mNROFsTF; ++iROF) { + int64_t fromStart = o2::gpu::CAMath::Max((int64_t)layerFrom.getROFStartInBC(iROF) - (int64_t)layerFrom.mROFAddTimeErr, int64_t(0)); + int64_t fromEnd = (int64_t)layerFrom.getROFEndInBC(iROF) + layerFrom.mROFAddTimeErr; + + int32_t firstROFTo = o2::gpu::CAMath::Max(0, (int32_t)((fromStart - (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias) / (int64_t)layerTo.mROFLength)); + auto lastROFTo = (int32_t)((fromEnd + (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias - 1) / (int64_t)layerTo.mROFLength); + firstROFTo = o2::gpu::CAMath::Max(0, firstROFTo); + lastROFTo = o2::gpu::CAMath::Min((int32_t)layerTo.mNROFsTF - 1, lastROFTo); + + while (firstROFTo <= lastROFTo) { + int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(firstROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0)); + int64_t toEnd = (int64_t)layerTo.getROFEndInBC(firstROFTo) + layerTo.mROFAddTimeErr; + if (toEnd > fromStart && toStart < fromEnd) { + break; + } + ++firstROFTo; + } + while (lastROFTo >= firstROFTo) { + int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(lastROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0)); + int64_t toEnd = (int64_t)layerTo.getROFEndInBC(lastROFTo) + layerTo.mROFAddTimeErr; + if (toEnd > fromStart && toStart < fromEnd) { + break; + } + --lastROFTo; + } + int32_t count = (firstROFTo <= lastROFTo) ? (lastROFTo - firstROFTo + 1) : 0; + table[iROF] = {static_cast(firstROFTo), static_cast(count)}; + } + } + + GPUh() void flatten(const std::vector table[NLayers][NLayers]) + { + size_t total{0}; + for (int32_t i{0}; i < NLayers; ++i) { + for (int32_t j{0}; j < NLayers; ++j) { + if (i != j) { // we do not need self-lookup + total += table[i][j].size(); + } + } + } + + mFlatTable.reserve(total); + + for (int32_t i{0}; i < NLayers; ++i) { + for (int32_t j{0}; j < NLayers; ++j) { + size_t idx = (i * NLayers) + j; + if (i != j) { + mIndices[idx].setFirstEntry(static_cast(mFlatTable.size())); + mIndices[idx].setEntries(static_cast(table[i][j].size())); + mFlatTable.insert(mFlatTable.end(), table[i][j].begin(), table[i][j].end()); + } else { + mIndices[idx] = {0, 0}; + } + } + } + } + + TableIndex mIndices[NLayers * NLayers]; + std::vector mFlatTable; +}; + +// GPU friendly view of the table below +template +struct ROFVertexLookupTableView { + const TableEntry* mFlatTable{nullptr}; + const TableIndex* mIndices{nullptr}; + const LayerTiming* mLayers{nullptr}; + + GPUhdi() const LayerTiming& getLayer(int32_t layer) const noexcept + { + assert(layer >= 0 && layer < NLayers); + return mLayers[layer]; + } + + GPUhdi() const TableEntry& getVertices(int32_t layer, size_t rofIdx) const noexcept + { + assert(layer < NLayers); + const auto& idx = mIndices[layer]; + assert(rofIdx < idx.getEntries()); + return mFlatTable[idx.getFirstEntry() + rofIdx]; + } + + GPUh() int32_t getMaxVerticesPerROF() const noexcept + { + int32_t maxCount = 0; + for (int32_t layer = 0; layer < NLayers; ++layer) { + const auto& idx = mIndices[layer]; + for (int32_t i = 0; i < idx.getEntries(); ++i) { + const auto& entry = mFlatTable[idx.getFirstEntry() + i]; + maxCount = o2::gpu::CAMath::Max(maxCount, static_cast(entry.getEntries())); + } + } + return maxCount; + } + + // Check if a specific vertex is compatible with a given ROF + GPUhdi() bool isVertexCompatible(int32_t layer, size_t rofIdx, const Vertex& vertex) const noexcept + { + assert(layer < NLayers); + const auto& layerDef = mLayers[layer]; + int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(rofIdx) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); + int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(rofIdx) + layerDef.mROFAddTimeErr; + auto vLower = (int64_t)vertex.getTimeStamp().lower(); + auto vUpper = (int64_t)vertex.getTimeStamp().upper(); + return vUpper >= rofLower && vLower < rofUpper; + } + +#ifndef GPUCA_GPUCODE + GPUh() void printAll() const + { + for (int32_t i = 0; i < NLayers; ++i) { + printLayer(i); + } + printSummary(); + } + + GPUh() void printLayer(int32_t layer) const + { + constexpr int w_rof = 10; + constexpr int w_first = 12; + constexpr int w_last = 12; + constexpr int w_count = 10; + + LOGF(info, "Vertex lookup: Layer %d", layer); + LOGF(info, "%*s | %*s | %*s | %*s", w_rof, "ROF.index", w_first, "First.Vtx", w_last, "Last.Vtx", w_count, "Count"); + LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_rof, "----------", w_first, "------------", w_last, "------------", w_count, "----------"); + + const auto& idx = mIndices[layer]; + for (int32_t i = 0; i < idx.getEntries(); ++i) { + const auto& entry = mFlatTable[idx.getFirstEntry() + i]; + int first = entry.getFirstEntry(); + int count = entry.getEntries(); + int last = first + count - 1; + LOGF(info, "%*d | %*d | %*d | %*d", w_rof, i, w_first, first, w_last, last, w_count, count); + } + } + + GPUh() void printSummary() const + { + uint32_t totalROFs{0}; + uint32_t totalVertexRefs{0}; + + for (int32_t i = 0; i < NLayers; ++i) { + const auto& idx = mIndices[i]; + totalROFs += idx.getEntries(); + + for (int32_t j = 0; j < idx.getEntries(); ++j) { + const auto& entry = mFlatTable[idx.getFirstEntry() + j]; + totalVertexRefs += entry.getEntries(); + } + } + + const uint32_t totalBytes = (totalROFs * sizeof(TableEntry)) + (NLayers * sizeof(TableIndex)); + LOGF(info, "------------------------------------------------------------"); + LOGF(info, "Total ROFs in table: %u", totalROFs); + LOGF(info, "Total vertex references: %u", totalVertexRefs); + LOGF(info, "Total view size: %u bytes", totalBytes); + LOGF(info, "------------------------------------------------------------"); + } +#endif +}; + +// Precalculated lookup table to find vertices compatible with ROFs +// Given a layer and ROF index, returns the range of vertices that overlap in time. +// The vertex time is defined as symmetrical [t0-e,t0+e] +// It needs to be guaranteed that the input vertices are sorted by their lower-bound! +// additionally compatibliyty has to be queried per vertex! +template +class ROFVertexLookupTable : public LayerTimingBase +{ + public: + using T = LayerTimingBase::T; + using BCType = LayerTiming::BCType; + using TableEntry = dataformats::RangeReference; + using TableIndex = dataformats::RangeReference; + using View = ROFVertexLookupTableView; + + ROFVertexLookupTable() = default; + + GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); } + static GPUh() constexpr size_t getIndicesSize() { return NLayers; } + + // Build the lookup table given a sorted array of vertices + // vertices must be sorted by timestamp, then by error (secondary) + GPUh() void init(const Vertex* vertices, size_t nVertices) + { + if (nVertices > std::numeric_limits::max()) { + LOGF(fatal, "too many vertices %zu, max supported is %u", nVertices, std::numeric_limits::max()); + } + + std::vector table[NLayers]; + for (int32_t layer{0}; layer < NLayers; ++layer) { + buildMapping(layer, vertices, nVertices, table[layer]); + } + flatten(table); + } + + // Pre-allocated needed memory, then use update(...) + GPUh() void init() + { + size_t total{0}; + for (int32_t layer{0}; layer < NLayers; ++layer) { + total += this->mLayers[layer].mNROFsTF; + } + mFlatTable.resize(total, {0, 0}); + size_t offset = 0; + for (int32_t layer{0}; layer < NLayers; ++layer) { + size_t nROFs = this->mLayers[layer].mNROFsTF; + mIndices[layer].setFirstEntry(static_cast(offset)); + mIndices[layer].setEntries(static_cast(nROFs)); + offset += nROFs; + } + } + + // Recalculate lookup table with new vertices + GPUh() void update(const Vertex* vertices, size_t nVertices) + { + size_t offset = 0; + for (int32_t layer{0}; layer < NLayers; ++layer) { + const auto& idx = mIndices[layer]; + size_t nROFs = idx.getEntries(); + for (size_t iROF = 0; iROF < nROFs; ++iROF) { + updateROFMapping(layer, iROF, vertices, nVertices, offset + iROF); + } + offset += nROFs; + } + } + + GPUh() View getView() const + { + View view; + view.mFlatTable = mFlatTable.data(); + view.mIndices = mIndices; + view.mLayers = this->mLayers; + return view; + } + + GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const + { + View view; + view.mFlatTable = deviceFlatTablePtr; + view.mIndices = deviceIndicesPtr; + view.mLayers = deviceLayerTimingPtr; + return view; + } + + private: + // Build the mapping for one layer + GPUh() void buildMapping(int32_t layer, const Vertex* vertices, size_t nVertices, std::vector& table) + { + const auto& layerDef = this->mLayers[layer]; + table.resize(layerDef.mNROFsTF); + size_t vertexSearchStart = 0; + for (int32_t iROF{0}; iROF < layerDef.mNROFsTF; ++iROF) { + int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); + int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr; + size_t lastVertex = binarySearchFirst(vertices, nVertices, vertexSearchStart, rofUpper); + size_t firstVertex = vertexSearchStart; + while (firstVertex < lastVertex) { + auto vUpper = (int64_t)vertices[firstVertex].getTimeStamp().upper(); + if (vUpper > rofLower) { + break; + } + ++firstVertex; + } + size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0; + table[iROF] = {static_cast(firstVertex), static_cast(count)}; + vertexSearchStart = firstVertex; + } + } + + // Update a single ROF's vertex mapping + GPUh() void updateROFMapping(int32_t layer, size_t iROF, const Vertex* vertices, size_t nVertices, size_t flatTableIdx) + { + const auto& layerDef = this->mLayers[layer]; + int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); + int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr; + size_t lastVertex = binarySearchFirst(vertices, nVertices, 0, rofUpper); + size_t firstVertex = 0; + while (firstVertex < lastVertex) { + int64_t vUpper = (int64_t)vertices[firstVertex].getTimeStamp().getTimeStamp() + + (int64_t)vertices[firstVertex].getTimeStamp().getTimeStampError(); + if (vUpper > rofLower) { + break; + } + ++firstVertex; + } + size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0; + mFlatTable[flatTableIdx].setFirstEntry(static_cast(firstVertex)); + mFlatTable[flatTableIdx].setEntries(static_cast(count)); + } + + // Binary search for first vertex where lowerBC >= targetBC + GPUh() size_t binarySearchFirst(const Vertex* vertices, size_t nVertices, size_t searchStart, BCType targetBC) const + { + size_t left = searchStart; + size_t right = nVertices; + while (left < right) { + size_t mid = left + ((right - left) / 2); + int64_t lower = (int64_t)vertices[mid].getTimeStamp().lower(); + if (lower < targetBC) { + left = mid + 1; + } else { + right = mid; + } + } + return left; + } + + // Compress the temporary table into a single flat table + GPUh() void flatten(const std::vector table[NLayers]) + { + // Count total entries + size_t total{0}; + for (int32_t i{0}; i < NLayers; ++i) { + total += table[i].size(); + } + + mFlatTable.reserve(total); + + // Build flat table and indices + for (int32_t i{0}; i < NLayers; ++i) { + mIndices[i].setFirstEntry(static_cast(mFlatTable.size())); + mIndices[i].setEntries(static_cast(table[i].size())); + mFlatTable.insert(mFlatTable.end(), table[i].begin(), table[i].end()); + } + } + + TableIndex mIndices[NLayers]; + std::vector mFlatTable; +}; + +// GPU-friendly view of the ROF mask table +template +struct ROFMaskTableView { + const TableEntry* mFlatMask{nullptr}; + const TableIndex* mLayerROFOffsets{nullptr}; // size NLayers+1 + + GPUhdi() bool isROFEnabled(int32_t layer, int32_t rofId) const noexcept + { + assert(layer >= 0 && layer < NLayers); + return mFlatMask[mLayerROFOffsets[layer] + rofId] != 0u; + } + +#ifndef GPUCA_GPUCODE + GPUh() void printAll() const + { + for (int32_t i = 0; i < NLayers; ++i) { + printLayer(i); + } + } + + GPUh() void printLayer(int32_t layer) const + { + constexpr int w_rof = 10; + constexpr int w_active = 10; + int32_t nROFs = mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]; + LOGF(info, "Mask table: Layer %d", layer); + LOGF(info, "%*s | %*s", w_rof, "ROF", w_active, "Enabled"); + LOGF(info, "%.*s-+-%.*s", w_rof, "----------", w_active, "----------"); + for (int32_t i = 0; i < nROFs; ++i) { + LOGF(info, "%*d | %*d", w_rof, i, w_active, (int)isROFEnabled(layer, i)); + } + } + + GPUh() std::string asString(int32_t layer) const + { + int32_t nROFs = mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]; + int32_t enabledROFs = 0; + for (int32_t j = 0; j < nROFs; ++j) { + if (isROFEnabled(layer, j)) { + ++enabledROFs; + } + } + return std::format("ROFMask on Layer {} ROFs enabled: {}/{}", layer, enabledROFs, nROFs); + } + + GPUh() void print(int32_t layer) const + { + LOG(info) << asString(layer); + } +#endif +}; + +// Per-ROF per-layer boolean mask (uint8_t for GPU compatibility). +template +class ROFMaskTable : public LayerTimingBase +{ + public: + using T = LayerTimingBase::T; + using BCRange = dataformats::RangeReference; + using TableIndex = uint32_t; + using TableEntry = uint8_t; + using View = ROFMaskTableView; + + ROFMaskTable() = default; + GPUh() explicit ROFMaskTable(const LayerTimingBase& timingBase) : LayerTimingBase(timingBase) { init(); } + + GPUh() void init() + { + int32_t totalROFs = 0; + for (int32_t layer{0}; layer < NLayers; ++layer) { + mLayerROFOffsets[layer] = totalROFs; + totalROFs += this->getLayer(layer).mNROFsTF; + } + mLayerROFOffsets[NLayers] = totalROFs; // sentinel + mFlatMask.resize(totalROFs, 0u); + } + + GPUh() size_t getFlatMaskSize() const noexcept { return mFlatMask.size(); } + + GPUh() void setROFEnabled(int32_t layer, int32_t rofId, uint8_t state = 1) noexcept + { + assert(layer >= 0 && layer < NLayers); + assert(rofId >= 0 && rofId < mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]); + mFlatMask[mLayerROFOffsets[layer] + rofId] = state; + } + + GPUh() void setROFsEnabled(int32_t layer, int32_t firstRof, int32_t nRofs, uint8_t state = 1) noexcept + { + assert(layer >= 0 && layer < NLayers); + assert(firstRof >= 0); + assert(firstRof + nRofs <= mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]); + std::memset(mFlatMask.data() + mLayerROFOffsets[layer] + firstRof, state, nRofs); + } + + // Enable all ROFs in all layers that are time-compatible with the given BC range + GPUh() void selectROF(const BCRange& t) + { + const int32_t bcStart = t.getFirstEntry(); + const int32_t bcEnd = t.getEntriesBound(); + for (int32_t layer{0}; layer < NLayers; ++layer) { + const auto& lay = this->getLayer(layer); + const int32_t offset = mLayerROFOffsets[layer]; + for (int32_t rofId{0}; rofId < lay.mNROFsTF; ++rofId) { + if (static_cast(lay.getROFStartInBC(rofId)) < bcEnd && + static_cast(lay.getROFEndInBC(rofId)) > bcStart) { + mFlatMask[offset + rofId] = 1u; + } + } + } + } + + // Reset mask to 0, then enable all ROFs compatible with any of the given BC ranges + GPUh() void selectROFs(const std::vector& ts) + { + resetMask(); + for (const auto& t : ts) { + selectROF(t); + } + } + + GPUh() void resetMask(uint8_t s = 0u) + { + std::memset(mFlatMask.data(), s, mFlatMask.size()); + } + + GPUh() void invertMask() + { + std::ranges::transform(mFlatMask, mFlatMask.begin(), [](uint8_t x) { return 1 - x; }); + } + + GPUh() void swap(ROFMaskTable& other) noexcept + { + std::swap(mFlatMask, other.mFlatMask); + std::swap(mLayerROFOffsets, other.mLayerROFOffsets); + } + + GPUh() View getView() const + { + View view; + view.mFlatMask = mFlatMask.data(); + view.mLayerROFOffsets = mLayerROFOffsets; + return view; + } + + GPUh() View getDeviceView(const TableEntry* deviceFlatMaskPtr, const TableIndex* deviceOffsetPtr) const + { + View view; + view.mFlatMask = deviceFlatMaskPtr; + view.mLayerROFOffsets = deviceOffsetPtr; + return view; + } + + private: + TableIndex mLayerROFOffsets[NLayers + 1] = {0}; + std::vector mFlatMask; +}; + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROframe.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROframe.h deleted file mode 100644 index a39ee1b96c72a..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROframe.h +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file ROframe.h -/// \brief -/// - -#ifndef TRACKINGITSU_INCLUDE_ROFRAME_H_ -#define TRACKINGITSU_INCLUDE_ROFRAME_H_ - -#include -#include -#include -#include -#include - -#include "ITStracking/Cluster.h" -#include "ITStracking/Constants.h" - -#include "ReconstructionDataFormats/Vertex.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -namespace o2 -{ -namespace its -{ - -using Vertex = o2::dataformats::Vertex>; - -class ROframe final -{ - public: - ROframe(int ROframeId, int nLayers); - int getROFrameId() const; - const float3& getPrimaryVertex(const int) const; - int getPrimaryVerticesNum() const; - void addPrimaryVertex(const float, const float, const float); - void addPrimaryVertices(std::vector vertices); - void addPrimaryReconstructedVertex(const float, const float, const float); - void printPrimaryVertices() const; - int getTotalClusters() const; - bool empty() const; - - const auto& getClusters() const { return mClusters; } - const std::vector& getClustersOnLayer(int layerId) const; - const std::vector& getTrackingFrameInfoOnLayer(int layerId) const; - const auto& getTrackingFrameInfo() const { return mTrackingFrameInfo; } - - const TrackingFrameInfo& getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const; - const MCCompLabel& getClusterFirstLabel(int layerId, const Cluster& cl) const; - const MCCompLabel& getClusterFirstLabel(int layerId, const int clId) const; - gsl::span getClusterLabels(int layerId, const int clId) const; - gsl::span getClusterLabels(int layerId, const Cluster& cl) const; - int getClusterExternalIndex(int layerId, const int clId) const; - std::vector getTracksId(const int layerId, const std::vector& cl); - - template - void addClusterToLayer(int layer, T&&... args); - template - void addTrackingFrameInfoToLayer(int layer, T&&... args); - void setMClabelsContainer(const dataformats::MCTruthContainer* ptr); - void addClusterExternalIndexToLayer(int layer, const int idx); - bool hasMCinformation() const; - - void clear(); - - private: - const int mROframeId; - o2::dataformats::MCTruthContainer* mMClabels = nullptr; - std::vector mPrimaryVertices; - std::vector> mClusters; - std::vector> mTrackingFrameInfo; - std::vector> mClusterExternalIndices; -}; - -inline int ROframe::getROFrameId() const { return mROframeId; } - -inline const float3& ROframe::getPrimaryVertex(const int vertexIndex) const { return mPrimaryVertices[vertexIndex]; } - -inline int ROframe::getPrimaryVerticesNum() const { return mPrimaryVertices.size(); } - -inline bool ROframe::empty() const { return getTotalClusters() == 0; } - -inline const std::vector& ROframe::getClustersOnLayer(int layerId) const -{ - return mClusters[layerId]; -} - -inline const std::vector& ROframe::getTrackingFrameInfoOnLayer(int layerId) const -{ - return mTrackingFrameInfo[layerId]; -} - -inline const TrackingFrameInfo& ROframe::getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const -{ - return mTrackingFrameInfo[layerId][cl.clusterId]; -} - -inline const MCCompLabel& ROframe::getClusterFirstLabel(int layerId, const Cluster& cl) const -{ - return getClusterFirstLabel(layerId, cl.clusterId); -} - -inline const MCCompLabel& ROframe::getClusterFirstLabel(int layerId, const int clId) const -{ - return *(mMClabels->getLabels(getClusterExternalIndex(layerId, clId)).begin()); -} - -inline gsl::span ROframe::getClusterLabels(int layerId, const int clId) const -{ - return mMClabels->getLabels(getClusterExternalIndex(layerId, clId)); -} - -inline gsl::span ROframe::getClusterLabels(int layerId, const Cluster& cl) const -{ - return getClusterLabels(layerId, cl.clusterId); -} - -inline int ROframe::getClusterExternalIndex(int layerId, const int clId) const -{ - return mClusterExternalIndices[layerId][clId]; -} - -inline std::vector ROframe::getTracksId(const int layerId, const std::vector& cl) -{ - std::vector tracksId; - for (auto& cluster : cl) { - tracksId.push_back(getClusterFirstLabel(layerId, cluster).isNoise() ? -1 : getClusterFirstLabel(layerId, cluster).getTrackID()); - } - return tracksId; -} - -template -void ROframe::addClusterToLayer(int layer, T&&... values) -{ - mClusters[layer].emplace_back(std::forward(values)...); -} - -template -void ROframe::addTrackingFrameInfoToLayer(int layer, T&&... values) -{ - mTrackingFrameInfo[layer].emplace_back(std::forward(values)...); -} - -inline void ROframe::setMClabelsContainer(const dataformats::MCTruthContainer* ptr) -{ - mMClabels = const_cast*>(ptr); -} - -inline void ROframe::addClusterExternalIndexToLayer(int layer, const int idx) -{ - mClusterExternalIndices[layer].push_back(idx); -} - -inline void ROframe::clear() -{ - for (unsigned int iL = 0; iL < mClusters.size(); ++iL) { - mClusters[iL].clear(); - mTrackingFrameInfo[iL].clear(); - // mClusterLabels[iL].clear(); - mClusterExternalIndices[iL].clear(); - } - mPrimaryVertices.clear(); - mMClabels = nullptr; -} - -inline bool ROframe::hasMCinformation() const -{ - // for (const auto& vect : mClusterLabels) { - // if (!vect.empty()) { - // return true; - // } - // } - // return false; - return mMClabels; -} - -} // namespace its -} // namespace o2 - -#endif /* TRACKINGITSU_INCLUDE_ROFRAME_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h deleted file mode 100644 index ceb799cc82899..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Road.h -/// \brief -/// - -#ifndef TRACKINGITSU_INCLUDE_ROAD_H_ -#define TRACKINGITSU_INCLUDE_ROAD_H_ - -#ifndef GPUCA_GPUCODE_DEVICE -#include -#endif - -#include "ITStracking/Constants.h" -#include "GPUCommonDef.h" - -namespace o2 -{ -namespace its -{ - -class Road final -{ - public: - Road(); - Road(int, int); - - int getRoadSize() const; - int getLabel() const; - void setLabel(const int); - bool isFakeRoad() const; - void setFakeRoad(const bool); - GPUhdni() int& operator[](const int&); - - void resetRoad(); - void addCell(int, int); - - static constexpr int mMaxRoadSize = 13; - - private: - int mCellIds[mMaxRoadSize]; - int mRoadSize; - int mLabel; - bool mIsFakeRoad; -}; - -inline int Road::getRoadSize() const { return mRoadSize; } - -inline int Road::getLabel() const { return mLabel; } - -inline void Road::setLabel(const int label) { mLabel = label; } - -GPUhdi() int& Road::operator[](const int& i) { return mCellIds[i]; } - -inline bool Road::isFakeRoad() const { return mIsFakeRoad; } - -inline void Road::setFakeRoad(const bool isFakeRoad) { mIsFakeRoad = isFakeRoad; } -} // namespace its -} // namespace o2 - -#endif /* TRACKINGITSU_INCLUDE_ROAD_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h deleted file mode 100644 index 2dcd521797837..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -/// \file Smoother.h -/// \brief Class to handle Kalman smoothing for ITS tracking. -/// Its instance stores the state of the track to the level we want to smooth to avoid multiple re-propagations when testing different clusters. -/// - -#include "ReconstructionDataFormats/Track.h" -#include "DataFormatsITS/TrackITS.h" -#include "DetectorsBase/Propagator.h" -#include "ITStracking/ROframe.h" - -namespace o2 -{ -namespace its -{ - -template -class Smoother -{ - public: - Smoother(TrackITSExt& track, size_t layer, const ROframe& event, float bZ, o2::base::PropagatorF::MatCorrType corr); - ~Smoother(); - - bool isValidInit() const - { - return mInitStatus; - } - bool testCluster(const int clusterId, const ROframe& event); - bool getSmoothedTrack(); - float getChi2() const { return mBestChi2; } - float getLastChi2() const { return mLastChi2; } - - private: - float computeSmoothedPredictedChi2(const o2::track::TrackParCov& outwTrack, - const o2::track::TrackParCov& inwTrack, - const std::array& cls, - const std::array& clCov); - bool smoothTrack(); - - private: - size_t mLayerToSmooth; // Layer to compute smoothing optimization - float mBz; // Magnetic field along Z - bool mInitStatus; // State after the initialization - o2::base::PropagatorF::MatCorrType mCorr; // Type of correction to use - TrackITSExt mInwardsTrack; // outwards track: from innermost cluster to outermost - TrackITSExt mOutwardsTrack; // inwards track: from outermost cluster to innermost - float mBestChi2; // Best value of local smoothed chi2 - float mLastChi2 = 1e8; // Latest computed chi2 -}; -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index 4a1f770786d6c..ae466a32bfc89 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -16,32 +16,34 @@ #include #include #include +#include #include -#include #include -#include -#include -#include #include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" #include "ITStracking/Cell.h" #include "ITStracking/Cluster.h" #include "ITStracking/Configuration.h" -#include "ITStracking/Constants.h" #include "ITStracking/ClusterLines.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/Road.h" #include "ITStracking/Tracklet.h" #include "ITStracking/IndexTableUtils.h" - +#include "ITStracking/ExternalAllocator.h" +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ROFLookupTables.h" +#include "ITStracking/TrackingTopology.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "ReconstructionDataFormats/Vertex.h" +#include "DetectorsBase/Propagator.h" namespace o2 { +namespace gpu +{ +class GPUChainITS; +} namespace itsmft { @@ -53,557 +55,576 @@ class ROFRecord; namespace its { -using Vertex = o2::dataformats::Vertex>; - -struct lightVertex { - lightVertex(float x, float y, float z, std::array rms2, int cont, float avgdis2, int stamp); - float mX; - float mY; - float mZ; - std::array mRMS2; - float mAvgDistance2; - int mContributors; - int mTimeStamp; -}; - -class TimeFrame -{ - public: - TimeFrame(int nLayers = 7); - const Vertex& getPrimaryVertex(const int) const; - gsl::span getPrimaryVertices(int tf) const; - gsl::span getPrimaryVertices(int romin, int romax) const; - gsl::span> getPrimaryVerticesLabels(const int rof) const; - gsl::span> getPrimaryVerticesXAlpha(int tf) const; - void fillPrimaryVerticesXandAlpha(); - int getPrimaryVerticesNum(int rofID = -1) const; - void addPrimaryVertices(const std::vector& vertices); - void addPrimaryVertices(const gsl::span& vertices); - void addPrimaryVertices(const std::vector&); - void removePrimaryVerticesInROf(const int rofId); - int loadROFrameData(const o2::itsmft::ROFRecord& rof, gsl::span clusters, - const dataformats::MCTruthContainer* mcLabels = nullptr); - - int loadROFrameData(gsl::span rofs, - gsl::span clusters, - gsl::span::iterator& pattIt, - const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* mcLabels = nullptr); +namespace gpu +{ +template +class TimeFrameGPU; +} + +template +struct TimeFrame { + using IndexTableUtilsN = IndexTableUtils; + using ROFOverlapTableN = ROFOverlapTable; + using ROFVertexLookupTableN = ROFVertexLookupTable; + using ROFMaskTableN = ROFMaskTable; + using TrackingTopologyN = TrackingTopology; + using TrackSeedN = TrackSeed; + friend class gpu::TimeFrameGPU; + + TimeFrame() = default; + virtual ~TimeFrame() = default; + + const Vertex& getPrimaryVertex(const int ivtx) const { return mPrimaryVertices[ivtx]; } + auto& getPrimaryVertices() { return mPrimaryVertices; }; + auto getPrimaryVerticesNum() { return mPrimaryVertices.size(); }; + const auto& getPrimaryVertices() const { return mPrimaryVertices; }; + auto& getPrimaryVerticesLabels() { return mPrimaryVerticesLabels; }; + gsl::span getPrimaryVertices(int layer, int rofId) const; + void addPrimaryVertex(const Vertex& vertex); + void addPrimaryVertexLabel(const VertexLabel& label) { mPrimaryVerticesLabels.push_back(label); } + + // read-in data + void loadROFrameData(gsl::span rofs, + gsl::span clusters, + gsl::span::iterator& pattIt, + const itsmft::TopologyDictionary* dict, + int layer, + const dataformats::MCTruthContainer* mcLabels = nullptr); + void resetROFrameData(int iLayer); + void prepareROFrameData(gsl::span clusters, int layer); int getTotalClusters() const; - bool empty() const; - - int getSortedIndex(int rof, int layer, int i) const; - int getSortedStartIndex(const int, const int) const; - int getNrof() const; + bool empty() const { return getTotalClusters() == 0; } + int getSortedIndex(int rofId, int layer, int idx) const { return mROFramesClusters[layer][rofId] + idx; } + int getSortedStartIndex(const int rofId, const int layer) const { return mROFramesClusters[layer][rofId]; } + int getNrof(int layer) const { return mROFramesClusters[layer].size() - 1; } void resetBeamXY(const float x, const float y, const float w = 0); void setBeamPosition(const float x, const float y, const float s2, const float base = 50.f, const float systematic = 0.f) { isBeamPositionOverridden = true; - resetBeamXY(x, y, s2 / std::sqrt(base * base + systematic)); + resetBeamXY(x, y, s2 / o2::gpu::CAMath::Sqrt((base * base) + systematic)); } - float getBeamX() const; - float getBeamY() const; + float getBeamX() const { return mBeamPos[0]; } + float getBeamY() const { return mBeamPos[1]; } + std::array& getBeamXY() { return mBeamPos; } + auto& getMinRs() { return mMinR; } + auto& getMaxRs() { return mMaxR; } float getMinR(int layer) const { return mMinR[layer]; } float getMaxR(int layer) const { return mMaxR[layer]; } - float getMSangle(int layer) const { return mMSangles[layer]; } - float getPhiCut(int layer) const { return mPhiCuts[layer]; } + float getLinkPhiCut(int linkId) const { return mLinkPhiCuts[linkId]; } + float getLinkMSAngle(int linkId) const { return mLinkMSAngles[linkId]; } + auto& getLinkPhiCuts() { return mLinkPhiCuts; } + auto& getLinkMSAngles() { return mLinkMSAngles; } float getPositionResolution(int layer) const { return mPositionResolution[layer]; } + auto& getPositionResolutions() { return mPositionResolution; } gsl::span getClustersOnLayer(int rofId, int layerId); gsl::span getClustersOnLayer(int rofId, int layerId) const; gsl::span getClustersPerROFrange(int rofMin, int range, int layerId) const; gsl::span getUnsortedClustersOnLayer(int rofId, int layerId) const; - gsl::span getROframesClustersPerROFrange(int rofMin, int range, int layerId) const; - gsl::span getROframeClusters(int layerId) const; + gsl::span getUsedClustersROF(int rofId, int layerId); + gsl::span getUsedClustersROF(int rofId, int layerId) const; + gsl::span getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const; + gsl::span getROFrameClusters(int layerId) const; gsl::span getNClustersROFrange(int rofMin, int range, int layerId) const; - gsl::span getIndexTablePerROFrange(int rofMin, int range, int layerId) const; gsl::span getIndexTable(int rofId, int layerId); - std::vector& getIndexTableWhole(int layerId) { return mIndexTables[layerId]; } - const std::vector& getTrackingFrameInfoOnLayer(int layerId) const; - - const TrackingFrameInfo& getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const; - const gsl::span getClusterLabels(int layerId, const Cluster& cl) const; - const gsl::span getClusterLabels(int layerId, const int clId) const; - int getClusterExternalIndex(int layerId, const int clId) const; - - std::vector& getTrackletsLabel(int layer) { return mTrackletLabels[layer]; } - std::vector& getCellsLabel(int layer) { return mCellLabels[layer]; } - - bool hasMCinformation() const; - void initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers = 7); - void resetRofPV() + const auto& getTrackingFrameInfoOnLayer(int layerId) const { return mTrackingFrameInfo[layerId]; } + + // navigation tables + const auto& getIndexTableUtils() const { return mIndexTableUtils; } + const auto& getROFOverlapTable() const { return mROFOverlapTable; } + const auto& getROFOverlapTableView() const { return mROFOverlapTableView; } + const auto& getTrackerTopologies() const { return mTrackerTopologies; } + const auto& getTrackingTopologyView() const { return mTrackingTopologyView; } + void setROFOverlapTable(ROFOverlapTableN table) { - mPrimaryVertices.clear(); - mROframesPV.resize(1, 0); - }; - - bool isClusterUsed(int layer, int clusterId) const; - void markUsedCluster(int layer, int clusterId); - - std::vector>& getTracklets(); - std::vector>& getTrackletsLookupTable(); + mROFOverlapTable = std::move(table); + mROFOverlapTableView = mROFOverlapTable.getView(); + } + const auto& getROFVertexLookupTable() const { return mROFVertexLookupTable; } + const auto& getROFVertexLookupTableView() const { return mROFVertexLookupTableView; } + void setROFVertexLookupTable(ROFVertexLookupTableN table) + { + mROFVertexLookupTable = std::move(table); + mROFVertexLookupTableView = mROFVertexLookupTable.getView(); + } + void updateROFVertexLookupTable() { mROFVertexLookupTable.update(mPrimaryVertices.data(), mPrimaryVertices.size()); } + void setMultiplicityCutMask(ROFMaskTableN cutMask) + { + mMultiplicityCutMask = std::move(cutMask); + mROFMaskView = mROFMask->getView(); + } + void useMultiplictyMask() noexcept + { + mROFMask = &mMultiplicityCutMask; + mROFMaskView = mROFMask->getView(); + } + void setUPCCutMask(ROFMaskTableN cutMask) { mUPCCutMask = std::move(cutMask); } + void useUPCMask() noexcept + { + mROFMask = &mUPCCutMask; + mROFMaskView = mROFMask->getView(); + } + const auto& getROFMaskView() const { return mROFMaskView; } - std::vector>& getClusters(); - std::vector>& getUnsortedClusters(); + const TrackingFrameInfo& getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const; + gsl::span getClusterLabels(int layerId, const Cluster& cl) const { return getClusterLabels(layerId, cl.clusterId); } + gsl::span getClusterLabels(int layerId, const int clId) const { return mClusterLabels[((mIsStaggered) ? layerId : 0)]->getLabels(mClusterExternalIndices[layerId][clId]); } + int getClusterExternalIndex(int layerId, const int clId) const { return mClusterExternalIndices[layerId][clId]; } + int getClusterSize(int layer, int clusterId) const { return mClusterSize[layer][clusterId]; } + void setClusterSize(int layer, bounded_vector& v) { mClusterSize[layer] = std::move(v); } + + auto& getTrackletsLabel(int layer) { return mTrackletLabels[layer]; } + auto& getCellsLabel(int layer) { return mCellLabels[layer]; } + + bool hasMCinformation() const { return mClusterLabels[0] != nullptr; } + void initVertexingTopology(const TrackingParameters& trkParam); + void initDefaultTrackingTopology(const TrackingParameters& trkParam, const int maxLayers = NLayers); + void initTrackerTopologies(gsl::span trkParams, const int maxLayers = NLayers); + void initialise(const TrackingParameters& trkParam, const int maxLayers = NLayers, const int iteration = constants::UnusedIndex); + + bool isClusterUsed(int layer, int clusterId) const { return mUsedClusters[layer][clusterId]; } + void markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } + gsl::span getUsedClusters(const int layer); + + auto& getTracklets() { return mTracklets; } + auto& getTrackletsLookupTable() { return mTrackletsLookupTable; } + + auto& getClusters() { return mClusters; } + auto& getUnsortedClusters() { return mUnsortedClusters; } int getClusterROF(int iLayer, int iCluster); - std::vector>& getCells(); - std::vector>& getCellsLookupTable(); - std::vector>>& getCellsNeighbours(); - std::vector& getRoads(); - std::vector& getTracks(int rof) { return mTracks[rof]; } - std::vector& getTracksLabel(const int rof) { return mTracksLabel[rof]; } - std::vector& getLinesLabel(const int rof) { return mLinesLabels[rof]; } - std::vector>& getVerticesLabels() { return mVerticesLabels; } - - int getNumberOfClusters() const; - int getNumberOfCells() const; - int getNumberOfTracklets() const; - int getNumberOfTracks() const; - + auto& getCells() { return mCells; } + + auto& getCellsLookupTable() { return mCellsLookupTable; } + auto& getCellsNeighbours() { return mCellsNeighbours; } + auto& getCellsNeighboursTopology() { return mCellsNeighboursTopology; } + auto& getCellsNeighboursLUT() { return mCellsNeighboursLUT; } + auto& getTracks() { return mTracks; } + auto& getTracksLabel() { return mTracksLabel; } + auto& getLinesLabel(const int rofId) { return mLinesLabels[rofId]; } + + size_t getNumberOfClusters() const; + virtual size_t getNumberOfCells() const; + virtual size_t getNumberOfTracklets() const; + virtual size_t getNumberOfNeighbours() const; + size_t getNumberOfTracks() const; + size_t getNumberOfUsedClusters() const; + void resetTrackExtensionCounters(); + void addTrackExtensionCounters(size_t nTracks, size_t nClusters); + size_t getNExtendedTracks() const { return mNExtendedTracks; } + size_t getNExtendedClusters() const { return mNExtendedClusters; } + + /// memory management + void setMemoryPool(std::shared_ptr pool); + auto& getMemoryPool() const noexcept { return mMemoryPool; } bool checkMemory(unsigned long max) { return getArtefactsMemory() < max; } - unsigned long getArtefactsMemory(); - int getROfCutClusterMult() const { return mCutClusterMult; }; - int getROfCutVertexMult() const { return mCutVertexMult; }; - int getROfCutAllMult() const { return mCutClusterMult + mCutVertexMult; } + unsigned long getArtefactsMemory() const; + void printArtefactsMemory() const; + + /// staggering + void setIsStaggered(bool b) noexcept { mIsStaggered = b; } // Vertexer - void computeTrackletsScans(const int nThreads = 1); - int& getNTrackletsROf(int tf, int combId); - std::vector& getLines(int tf); - std::vector& getTrackletClusters(int tf); + void computeTrackletsPerROFScans(); + void computeTracletsPerClusterScans(); + int& getNTrackletsROF(int rofId, int combId) { return mNTrackletsPerROF[combId][rofId]; } + auto& getLines(int rofId) { return mLines[rofId]; } + int getNLinesTotal() const noexcept { return mTotalLines; } + void setNLinesTotal(uint32_t a) noexcept { mTotalLines = a; } + auto& getTrackletClusters(int rofId) { return mTrackletClusters[rofId]; } gsl::span getFoundTracklets(int rofId, int combId) const; gsl::span getFoundTracklets(int rofId, int combId); gsl::span getLabelsFoundTracklets(int rofId, int combId) const; gsl::span getNTrackletsCluster(int rofId, int combId); + gsl::span getExclusiveNTrackletsCluster(int rofId, int combId); uint32_t getTotalTrackletsTF(const int iLayer) { return mTotalTracklets[iLayer]; } int getTotalClustersPerROFrange(int rofMin, int range, int layerId) const; - std::array& getBeamXY() { return mBeamPos; } // \Vertexer - void initialiseRoadLabels(); - void setRoadLabel(int i, const unsigned long long& lab, bool fake); - const unsigned long long& getRoadLabel(int i) const; - bool isRoadFake(int i) const; - - void setMultiplicityCutMask(const std::vector& cutMask) { mMultiplicityCutMask = cutMask; } - int hasBogusClusters() const { return std::accumulate(mBogusClusters.begin(), mBogusClusters.end(), 0); } void setBz(float bz) { mBz = bz; } float getBz() const { return mBz; } + /// State if memory will be externally managed by the GPU framework + ExternalAllocator* mExternalAllocator{nullptr}; + std::shared_ptr mExtMemoryPool; // host memory pool managed by the framework + auto getFrameworkAllocator() { return mExternalAllocator; }; + void setFrameworkAllocator(ExternalAllocator* ext); + bool hasFrameworkAllocator() const noexcept { return mExternalAllocator != nullptr; } + std::pmr::memory_resource* getMaybeFrameworkHostResource(bool forceHost = false) { return (hasFrameworkAllocator() && !forceHost) ? mExtMemoryPool.get() : mMemoryPool.get(); } + + // Propagator + const o2::base::PropagatorImpl* getDevicePropagator() const { return mPropagatorDevice; } + virtual void setDevicePropagator(const o2::base::PropagatorImpl* /*unused*/) {}; + template void addClusterToLayer(int layer, T&&... args); template void addTrackingFrameInfoToLayer(int layer, T&&... args); - void addClusterExternalIndexToLayer(int layer, const int idx); - - /// Debug and printing - void checkTrackletLUTs(); - void printROFoffsets(); - void printNClsPerROF(); - void printVertices(); - void printTrackletLUTonLayer(int i); - void printCellLUTonLayer(int i); - void printTrackletLUTs(); - void printCellLUTs(); - - IndexTableUtils mIndexTableUtils; - - bool mIsGPU = false; - std::vector> mClusters; - std::vector> mTrackingFrameInfo; - std::vector> mClusterExternalIndices; - std::vector> mROframesClusters; - const dataformats::MCTruthContainer* mClusterLabels = nullptr; - std::array, 2> mNTrackletsPerCluster; // TODO: remove in favour of mNTrackletsPerROf - std::vector> mNClustersPerROF; - std::vector> mIndexTables; - std::vector> mTrackletsLookupTable; - std::vector> mUsedClusters; - int mNrof = 0; - std::vector mROframesPV = {0}; - std::vector mPrimaryVertices; - - private: + void addClusterExternalIndexToLayer(int layer, const int idx) { mClusterExternalIndices[layer].push_back(idx); } + + std::array, NLayers> mClusters; + std::array, NLayers> mTrackingFrameInfo; + std::array, NLayers> mClusterExternalIndices; + std::array, NLayers> mROFramesClusters; + std::array*, NLayers> mClusterLabels{nullptr}; + std::array, 2> mNTrackletsPerCluster; + std::array, 2> mNTrackletsPerClusterSum; + std::array, NLayers> mNClustersPerROF; + std::array, NLayers> mIndexTables; + std::vector> mTrackletsLookupTable; + std::array, NLayers> mUsedClusters; + + std::array, NLayers> mUnsortedClusters; + std::vector> mTracklets; + std::vector> mCells; + bounded_vector mTracks; + bounded_vector mTracksLabel; + size_t mNExtendedTracks = 0; + size_t mNExtendedClusters = 0; + std::vector> mCellsNeighbours; + std::vector> mCellsNeighboursTopology; + std::vector> mCellsLookupTable; + + const o2::base::PropagatorImpl* mPropagatorDevice = nullptr; // Needed only for GPU + + virtual void wipe(); + + // interface + virtual bool isGPU() const noexcept { return false; } + virtual const char* getName() const noexcept { return "CPU"; } + + protected: + void prepareClusters(const TrackingParameters& trkParam, const int maxLayers = NLayers); float mBz = 5.; + unsigned int mNTotalLowPtVertices = 0; int mBeamPosWeight = 0; std::array mBeamPos = {0.f, 0.f}; bool isBeamPositionOverridden = false; - std::vector mMinR; - std::vector mMaxR; - std::vector mMSangles; - std::vector mPhiCuts; - std::vector mPositionResolution; - std::vector mMultiplicityCutMask; - std::vector> mPValphaX; /// PV x and alpha for track propagation - std::vector> mUnsortedClusters; - std::vector> mTrackletLabels; - std::vector> mCellLabels; - std::vector> mCells; - std::vector> mCellsLookupTable; - std::vector>> mCellsNeighbours; - std::vector mRoads; - std::vector> mTracksLabel; - std::vector> mTracks; - std::vector mBogusClusters; /// keep track of clusters with wild coordinates - - std::vector> mTracklets; - - std::vector> mRoadLabels; - int mCutClusterMult; - int mCutVertexMult; + std::array mMinR; + std::array mMaxR; + bounded_vector mLinkPhiCuts; + bounded_vector mLinkMSAngles; + bounded_vector mPositionResolution; + std::array, NLayers> mClusterSize; + + bounded_vector> mPValphaX; /// PV x and alpha for track propagation + std::vector> mTrackletLabels; + std::vector> mCellLabels; + std::vector> mCellsNeighboursLUT; + bounded_vector mBogusClusters; /// keep track of clusters with wild coordinates // Vertexer - std::vector> mNTrackletsPerROf; - std::vector> mLines; - std::vector> mTrackletClusters; - std::vector> mTrackletsIndexROf; - std::vector> mLinesLabels; - std::vector> mVerticesLabels; + bounded_vector mPrimaryVertices; + bounded_vector mPrimaryVerticesLabels; + std::vector> mNTrackletsPerROF; + std::vector> mLines; + std::vector> mTrackletClusters; + std::array, 2> mTrackletsIndexROF; + std::vector> mLinesLabels; std::array mTotalTracklets = {0, 0}; + uint32_t mTotalLines = 0; // \Vertexer -}; - -inline const Vertex& TimeFrame::getPrimaryVertex(const int vertexIndex) const { return mPrimaryVertices[vertexIndex]; } - -inline gsl::span TimeFrame::getPrimaryVertices(int rof) const -{ - const int start = mROframesPV[rof]; - const int stop_idx = rof >= mNrof - 1 ? mNrof : rof + 1; - int delta = mMultiplicityCutMask[rof] ? mROframesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&mPrimaryVertices[start], static_cast::size_type>(delta)}; -} - -inline gsl::span> TimeFrame::getPrimaryVerticesLabels(const int rof) const -{ - const int start = mROframesPV[rof]; - const int stop_idx = rof >= mNrof - 1 ? mNrof : rof + 1; - int delta = mMultiplicityCutMask[rof] ? mROframesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&mVerticesLabels[start], static_cast::size_type>(delta)}; -} -inline gsl::span TimeFrame::getPrimaryVertices(int romin, int romax) const -{ - return {&mPrimaryVertices[mROframesPV[romin]], static_cast::size_type>(mROframesPV[romax + 1] - mROframesPV[romin])}; -} + // lookup tables + IndexTableUtilsN mIndexTableUtils; + ROFOverlapTableN mROFOverlapTable; + ROFOverlapTableN::View mROFOverlapTableView; + TrackingTopologyN mVertexingTopology; + TrackingTopologyN mDefaultTrackingTopology; + std::vector mTrackerTopologies; + typename TrackingTopologyN::View mTrackingTopologyView; + ROFVertexLookupTableN mROFVertexLookupTable; + ROFVertexLookupTableN::View mROFVertexLookupTableView; + ROFMaskTableN mMultiplicityCutMask; + ROFMaskTableN mUPCCutMask; + ROFMaskTableN* mROFMask = &mMultiplicityCutMask; + ROFMaskTableN::View mROFMaskView; + + bool mIsStaggered{false}; + + std::shared_ptr mMemoryPool; +}; -inline gsl::span> TimeFrame::getPrimaryVerticesXAlpha(int rof) const +template +gsl::span TimeFrame::getPrimaryVertices(int layer, int rofId) const { - const int start = mROframesPV[rof]; - const int stop_idx = rof >= mNrof - 1 ? mNrof : rof + 1; - int delta = mMultiplicityCutMask[rof] ? mROframesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&(mPValphaX[start]), static_cast>::size_type>(delta)}; + if (rofId < 0 || rofId >= getNrof(layer)) { + return {}; + } + const auto& entry = mROFVertexLookupTableView.getVertices(layer, rofId); + return {&mPrimaryVertices[entry.getFirstEntry()], static_cast::size_type>(entry.getEntries())}; } -inline int TimeFrame::getPrimaryVerticesNum(int rofID) const -{ - return rofID < 0 ? mPrimaryVertices.size() : mROframesPV[rofID + 1] - mROframesPV[rofID]; -} - -inline bool TimeFrame::empty() const { return getTotalClusters() == 0; } - -inline int TimeFrame::getSortedIndex(int rof, int layer, int index) const { return mROframesClusters[layer][rof] + index; } - -inline int TimeFrame::getSortedStartIndex(const int rof, const int layer) const { return mROframesClusters[layer][rof]; } - -inline int TimeFrame::getNrof() const { return mNrof; } - -inline void TimeFrame::resetBeamXY(const float x, const float y, const float w) +template +inline void TimeFrame::resetBeamXY(const float x, const float y, const float w) { mBeamPos[0] = x; mBeamPos[1] = y; mBeamPosWeight = w; } -inline float TimeFrame::getBeamX() const { return mBeamPos[0]; } - -inline float TimeFrame::getBeamY() const { return mBeamPos[1]; } - -inline gsl::span TimeFrame::getROframeClusters(int layerId) const +template +inline gsl::span TimeFrame::getROFrameClusters(int layerId) const { - return {&mROframesClusters[layerId][0], static_cast::size_type>(mROframesClusters[layerId].size())}; + return {&mROFramesClusters[layerId][0], static_cast::size_type>(mROFramesClusters[layerId].size())}; } -inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) +template +inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) { - if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + if (rofId < 0 || rofId >= getNrof(layerId)) { + return {}; } - int startIdx{mROframesClusters[layerId][rofId]}; - return {&mClusters[layerId][startIdx], static_cast::size_type>(mROframesClusters[layerId][rofId + 1] - startIdx)}; + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) const +template +inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) const { - if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + if (rofId < 0 || rofId >= getNrof(layerId)) { + return {}; } - int startIdx{mROframesClusters[layerId][rofId]}; - return {&mClusters[layerId][startIdx], static_cast::size_type>(mROframesClusters[layerId][rofId + 1] - startIdx)}; + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -inline gsl::span TimeFrame::getClustersPerROFrange(int rofMin, int range, int layerId) const +template +inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) { - if (rofMin < 0 || rofMin >= mNrof) { - return gsl::span(); + if (rofId < 0 || rofId >= getNrof(layerId)) { + return {}; } - int startIdx{mROframesClusters[layerId][rofMin]}; // First cluster of rofMin - int endIdx{mROframesClusters[layerId][std::min(rofMin + range, mNrof)]}; - return {&mClusters[layerId][startIdx], static_cast::size_type>(endIdx - startIdx)}; + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mUsedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -inline gsl::span TimeFrame::getROframesClustersPerROFrange(int rofMin, int range, int layerId) const +template +inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) const { - int chkdRange{std::min(range, mNrof - rofMin)}; - return {&mROframesClusters[layerId][rofMin], static_cast::size_type>(chkdRange)}; -} - -inline gsl::span TimeFrame::getNClustersROFrange(int rofMin, int range, int layerId) const -{ - int chkdRange{std::min(range, mNrof - rofMin)}; - return {&mNClustersPerROF[layerId][rofMin], static_cast::size_type>(chkdRange)}; -} - -inline int TimeFrame::getTotalClustersPerROFrange(int rofMin, int range, int layerId) const -{ - int startIdx{rofMin}; // First cluster of rofMin - int endIdx{std::min(rofMin + range, mNrof)}; - return mROframesClusters[layerId][endIdx] - mROframesClusters[layerId][startIdx]; -} - -inline gsl::span TimeFrame::getIndexTablePerROFrange(int rofMin, int range, int layerId) const -{ - const int iTableSize{mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1}; - int chkdRange{std::min(range, mNrof - rofMin)}; - return {&mIndexTables[layerId][rofMin * iTableSize], static_cast::size_type>(chkdRange * iTableSize)}; -} - -inline int TimeFrame::getClusterROF(int iLayer, int iCluster) -{ - return std::lower_bound(mROframesClusters[iLayer].begin(), mROframesClusters[iLayer].end(), iCluster + 1) - mROframesClusters[iLayer].begin() - 1; -} - -inline gsl::span TimeFrame::getUnsortedClustersOnLayer(int rofId, int layerId) const -{ - if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + if (rofId < 0 || rofId >= getNrof(layerId)) { + return {}; } - int startIdx{mROframesClusters[layerId][rofId]}; - return {&mUnsortedClusters[layerId][startIdx], static_cast::size_type>(mROframesClusters[layerId][rofId + 1] - startIdx)}; + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mUsedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -inline const std::vector& TimeFrame::getTrackingFrameInfoOnLayer(int layerId) const +template +inline gsl::span TimeFrame::getClustersPerROFrange(int rofMin, int range, int layerId) const { - return mTrackingFrameInfo[layerId]; + if (rofMin < 0 || rofMin >= getNrof(layerId)) { + return {}; + } + int startIdx{mROFramesClusters[layerId][rofMin]}; // First cluster of rofMin + int endIdx{mROFramesClusters[layerId][o2::gpu::CAMath::Min(rofMin + range, getNrof(layerId))]}; + return {&mClusters[layerId][startIdx], static_cast::size_type>(endIdx - startIdx)}; } -inline const TrackingFrameInfo& TimeFrame::getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const +template +inline gsl::span TimeFrame::getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const { - return mTrackingFrameInfo[layerId][cl.clusterId]; + int chkdRange{o2::gpu::CAMath::Min(range, getNrof(layerId) - rofMin)}; + return {&mROFramesClusters[layerId][rofMin], static_cast::size_type>(chkdRange)}; } -inline const gsl::span TimeFrame::getClusterLabels(int layerId, const Cluster& cl) const +template +inline gsl::span TimeFrame::getNClustersROFrange(int rofMin, int range, int layerId) const { - return getClusterLabels(layerId, cl.clusterId); + int chkdRange{o2::gpu::CAMath::Min(range, getNrof(layerId) - rofMin)}; + return {&mNClustersPerROF[layerId][rofMin], static_cast::size_type>(chkdRange)}; } -inline const gsl::span TimeFrame::getClusterLabels(int layerId, int clId) const +template +inline int TimeFrame::getTotalClustersPerROFrange(int rofMin, int range, int layerId) const { - return mClusterLabels->getLabels(mClusterExternalIndices[layerId][clId]); + int startIdx{rofMin}; // First cluster of rofMin + int endIdx{o2::gpu::CAMath::Min(rofMin + range, getNrof(layerId))}; + return mROFramesClusters[layerId][endIdx] - mROFramesClusters[layerId][startIdx]; } -inline int TimeFrame::getClusterExternalIndex(int layerId, const int clId) const +template +inline int TimeFrame::getClusterROF(int iLayer, int iCluster) { - return mClusterExternalIndices[layerId][clId]; + return std::lower_bound(mROFramesClusters[iLayer].begin(), mROFramesClusters[iLayer].end(), iCluster + 1) - mROFramesClusters[iLayer].begin() - 1; } -inline gsl::span TimeFrame::getIndexTable(int rofId, int layer) +template +inline gsl::span TimeFrame::getUnsortedClustersOnLayer(int rofId, int layerId) const { - if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + if (rofId < 0 || rofId >= getNrof(layerId)) { + return {}; } - return {&mIndexTables[layer][rofId * (mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1)], - static_cast::size_type>(mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1)}; -} - -inline std::vector& TimeFrame::getLines(int rof) -{ - return mLines[rof]; + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mUnsortedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -inline std::vector& TimeFrame::getTrackletClusters(int rof) +template +inline gsl::span TimeFrame::getIndexTable(int rofId, int layer) { - return mTrackletClusters[rof]; + if (rofId < 0 || rofId >= getNrof(layer)) { + return {}; + } + const int tableSize = mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1; + return {&mIndexTables[layer][rofId * tableSize], static_cast::size_type>(tableSize)}; } +template template -void TimeFrame::addClusterToLayer(int layer, T&&... values) +void TimeFrame::addClusterToLayer(int layer, T&&... values) { mUnsortedClusters[layer].emplace_back(std::forward(values)...); } +template template -void TimeFrame::addTrackingFrameInfoToLayer(int layer, T&&... values) +void TimeFrame::addTrackingFrameInfoToLayer(int layer, T&&... values) { mTrackingFrameInfo[layer].emplace_back(std::forward(values)...); } -inline void TimeFrame::addClusterExternalIndexToLayer(int layer, const int idx) -{ - mClusterExternalIndices[layer].push_back(idx); -} - -inline bool TimeFrame::hasMCinformation() const -{ - return mClusterLabels; -} - -inline bool TimeFrame::isClusterUsed(int layer, int clusterId) const -{ - return mUsedClusters[layer][clusterId]; -} - -inline void TimeFrame::markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } - -inline std::vector>& TimeFrame::getTracklets() -{ - return mTracklets; -} - -inline std::vector>& TimeFrame::getTrackletsLookupTable() -{ - return mTrackletsLookupTable; -} - -inline void TimeFrame::initialiseRoadLabels() -{ - mRoadLabels.clear(); - mRoadLabels.resize(mRoads.size()); -} - -inline void TimeFrame::setRoadLabel(int i, const unsigned long long& lab, bool fake) +template +inline gsl::span TimeFrame::getUsedClusters(const int layer) { - mRoadLabels[i].first = lab; - mRoadLabels[i].second = fake; + return {&mUsedClusters[layer][0], static_cast::size_type>(mUsedClusters[layer].size())}; } -inline const unsigned long long& TimeFrame::getRoadLabel(int i) const +template +inline gsl::span TimeFrame::getNTrackletsCluster(int rofId, int combId) { - return mRoadLabels[i].first; + if (rofId < 0 || rofId >= getNrof(1)) { + return {}; + } + auto startIdx{mROFramesClusters[1][rofId]}; + return {&mNTrackletsPerCluster[combId][startIdx], static_cast::size_type>(mROFramesClusters[1][rofId + 1] - startIdx)}; } -inline gsl::span TimeFrame::getNTrackletsCluster(int rofId, int combId) +template +inline gsl::span TimeFrame::getExclusiveNTrackletsCluster(int rofId, int combId) { - if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + if (rofId < 0 || rofId >= getNrof(1)) { + return {}; } - auto startIdx{mROframesClusters[1][rofId]}; - return {&mNTrackletsPerCluster[combId][startIdx], static_cast::size_type>(mROframesClusters[1][rofId + 1] - startIdx)}; -} + auto clusStartIdx{mROFramesClusters[1][rofId]}; -inline int& TimeFrame::getNTrackletsROf(int rof, int combId) -{ - return mNTrackletsPerROf[combId][rof]; + return {&mNTrackletsPerClusterSum[combId][clusStartIdx], static_cast::size_type>(mROFramesClusters[1][rofId + 1] - clusStartIdx)}; } -inline bool TimeFrame::isRoadFake(int i) const +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) { - return mRoadLabels[i].second; + if (rofId < 0 || rofId >= getNrof(1) || mTracklets[combId].empty()) { + return {}; + } + auto startIdx{mNTrackletsPerROF[combId][rofId]}; + return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; } -inline std::vector>& TimeFrame::getClusters() +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) const { - return mClusters; + if (rofId < 0 || rofId >= getNrof(1)) { + return {}; + } + auto startIdx{mNTrackletsPerROF[combId][rofId]}; + return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; } -inline std::vector>& TimeFrame::getUnsortedClusters() +template +inline gsl::span TimeFrame::getLabelsFoundTracklets(int rofId, int combId) const { - return mUnsortedClusters; + if (rofId < 0 || rofId >= getNrof(1) || !hasMCinformation()) { + return {}; + } + auto startIdx{mNTrackletsPerROF[combId][rofId]}; + return {&mTrackletLabels[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; } -inline std::vector>& TimeFrame::getCells() { return mCells; } - -inline std::vector>& TimeFrame::getCellsLookupTable() +template +inline int TimeFrame::getTotalClusters() const { - return mCellsLookupTable; + size_t totalClusters{0}; + for (const auto& clusters : mUnsortedClusters) { + totalClusters += clusters.size(); + } + return int(totalClusters); } -inline std::vector>>& TimeFrame::getCellsNeighbours() +template +inline size_t TimeFrame::getNumberOfClusters() const { - return mCellsNeighbours; + size_t nClusters{0}; + for (const auto& layer : mClusters) { + nClusters += layer.size(); + } + return nClusters; } -inline std::vector& TimeFrame::getRoads() { return mRoads; } - -inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) +template +inline size_t TimeFrame::getNumberOfCells() const { - if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + size_t nCells{0}; + for (const auto& layer : mCells) { + nCells += layer.size(); } - auto startIdx{mNTrackletsPerROf[combId][rofId]}; - return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROf[combId][rofId + 1] - startIdx)}; + return nCells; } -inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) const +template +inline size_t TimeFrame::getNumberOfTracklets() const { - if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + size_t nTracklets{0}; + for (const auto& layer : mTracklets) { + nTracklets += layer.size(); } - auto startIdx{mNTrackletsPerROf[combId][rofId]}; - return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROf[combId][rofId + 1] - startIdx)}; + return nTracklets; } -inline gsl::span TimeFrame::getLabelsFoundTracklets(int rofId, int combId) const +template +inline size_t TimeFrame::getNumberOfNeighbours() const { - if (rofId < 0 || rofId >= mNrof || !hasMCinformation()) { - return gsl::span(); + size_t neigh{0}; + for (const auto& l : mCellsNeighbours) { + neigh += l.size(); } - auto startIdx{mNTrackletsPerROf[combId][rofId]}; - return {&mTrackletLabels[combId][startIdx], static_cast::size_type>(mNTrackletsPerROf[combId][rofId + 1] - startIdx)}; + return neigh; } -inline int TimeFrame::getNumberOfClusters() const +template +inline size_t TimeFrame::getNumberOfTracks() const { - int nClusters = 0; - for (auto& layer : mClusters) { - nClusters += layer.size(); - } - return nClusters; + return mTracks.size(); } -inline int TimeFrame::getNumberOfCells() const +template +inline size_t TimeFrame::getNumberOfUsedClusters() const { - int nCells = 0; - for (auto& layer : mCells) { - nCells += layer.size(); + size_t nClusters = 0; + for (const auto& layer : mUsedClusters) { + nClusters += std::count(layer.begin(), layer.end(), true); } - return nCells; + return nClusters; } -inline int TimeFrame::getNumberOfTracklets() const +template +inline void TimeFrame::resetTrackExtensionCounters() { - int nTracklets = 0; - for (auto& layer : mTracklets) { - nTracklets += layer.size(); - } - return nTracklets; + mNExtendedTracks = 0; + mNExtendedClusters = 0; } -inline int TimeFrame::getNumberOfTracks() const +template +inline void TimeFrame::addTrackExtensionCounters(size_t nTracks, size_t nClusters) { - int nTracks = 0; - for (auto& t : mTracks) { - nTracks += t.size(); - } - return nTracks; + mNExtendedTracks += nTracks; + mNExtendedClusters += nClusters; } } // namespace its } // namespace o2 -#endif /* TRACKINGITSU_INCLUDE_TimeFrame_H_ */ +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackExtensionHypothesis.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackExtensionHypothesis.h new file mode 100644 index 0000000000000..afa24c34afd4f --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackExtensionHypothesis.h @@ -0,0 +1,56 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef TRACKINGITSU_INCLUDE_TRACKEXTENSIONHYPOTHESIS_H_ +#define TRACKINGITSU_INCLUDE_TRACKEXTENSIONHYPOTHESIS_H_ + +#include + +#include "GPUCommonDef.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "ITStracking/Constants.h" +#include "ITStracking/TrackITSInternal.h" +#include "ReconstructionDataFormats/Track.h" + +namespace o2::its +{ + +template +struct TrackExtensionHypothesis { + TrackExtensionHypothesis() = default; + GPUhdi() TrackExtensionHypothesis(const TrackITSInternal& track, bool outward) + { + initialiseFromTrack(track, outward); + } + + GPUhdi() void initialiseFromTrack(const TrackITSInternal& track, bool outward) + { + param = outward ? track.paramOut : track.paramIn; + time = track.time; + chi2 = track.getChi2(); + nClusters = track.getNClusters(); + edgeLayer = outward ? track.getLastClusterLayer() : track.getFirstClusterLayer(); + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + clusters[iLayer] = track.getClusterIndex(iLayer); + } + } + + o2::track::TrackParCov param; + std::array clusters{}; + TimeEstBC time; + float chi2{0.f}; + int nClusters{0}; + int edgeLayer{constants::UnusedIndex}; +}; + +} // namespace o2::its + +#endif /* TRACKINGITSU_INCLUDE_TRACKEXTENSIONHYPOTHESIS_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackFollower.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackFollower.h new file mode 100644 index 0000000000000..703b01e014dd7 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackFollower.h @@ -0,0 +1,308 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TrackFollower.h +/// \brief Hypothesis search used by CPU and GPU track extension. + +#ifndef TRACKINGITSU_INCLUDE_TRACKFOLLOWER_H_ +#define TRACKINGITSU_INCLUDE_TRACKFOLLOWER_H_ + +#include + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "DetectorsBase/Propagator.h" + +#include "ITStracking/Cluster.h" +#include "ITStracking/Constants.h" +#include "ITStracking/IndexTableUtils.h" +#include "ITStracking/MathUtils.h" +#include "ITStracking/ROFLookupTables.h" +#include "ITStracking/TrackExtensionHypothesis.h" +#include "ITStracking/TrackHelpers.h" + +namespace o2::its +{ + +template +GPUhdi() void keepTrackExtensionHypothesis(const TrackExtensionHypothesis& hypo, + TrackExtensionHypothesis* keptHypotheses, + int& nKeptHypotheses, + const int maxHypotheses) +{ + if (nKeptHypotheses < maxHypotheses) { + keptHypotheses[nKeptHypotheses++] = hypo; + return; + } + + int worst{0}; + for (int i{1}; i < nKeptHypotheses; ++i) { + if (track::isBetter(keptHypotheses[worst].nClusters, keptHypotheses[worst].chi2, keptHypotheses[i].nClusters, keptHypotheses[i].chi2)) { + worst = i; + } + } + if (track::isBetter(hypo.nClusters, hypo.chi2, keptHypotheses[worst].nClusters, keptHypotheses[worst].chi2)) { + keptHypotheses[worst] = hypo; + } +} + +template +GPUhdi() void updateTrackFromExtensionHypothesis(const TrackExtensionHypothesis& hypo, + const bool outward, + const int nLayers, + TrackITSInternal& track) +{ + if (outward) { + track.paramOut = hypo.param; + } else { + track.paramIn = hypo.param; + } + track.time = hypo.time; + track.setChi2(hypo.chi2); + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex && hypo.clusters[iLayer] != constants::UnusedIndex) { + track.setClusterIndex(iLayer, hypo.clusters[iLayer]); + } + } +} + +// Search-specific inputs for track extension: cluster/ROF/index tables, layer +// radii, and phi/z search cuts. Kept separate from the fit context so that +// refit-only callers don't have to carry these fields. +template +struct TrackFollowContext { + const IndexTableUtils* utils{nullptr}; + typename ROFMaskTable::View rofMask; + typename ROFOverlapTable::View rofOverlaps; + const Cluster* const* clusters{nullptr}; + const unsigned char* const* usedClusters{nullptr}; + const int* const* clustersIndexTables{nullptr}; + const int* const* ROFClusters{nullptr}; + const float* layerRadii{nullptr}; + int phiBins{0}; + int maxHypotheses{0}; + float nSigmaCutPhi{0.f}; + float nSigmaCutZ{0.f}; +}; + +template +struct TrackExtensionBestTrial { + GPUdi() TrackExtensionBestTrial(uint32_t backupPattern, const track::TrackFitContext& fit) + : backupPattern{backupPattern}, fit{fit} + { + } + + GPUdi() void update(TrackITSInternal& trial, TrackITSInternal& best, uint32_t& bestDiff) const + { + const auto diff = (trial.getPattern() & ~backupPattern) & TrackITS::getLayerPatternMask(); + if (!diff || !track::refitTrack(trial, fit)) { + return; + } + if (track::isBetter(trial, best)) { + best = trial; + bestDiff = diff; + } + } + + uint32_t backupPattern{0}; + const track::TrackFitContext& fit; +}; + +template +GPUdi() void followTrackExtensionBranches(const TrackITSInternal& backup, + const bool extendTop, + const bool extendBot, + const int nLayers, + FollowDirection& followDirection, + BestTrial& bestTrial, + TrackITSInternal& best, + uint32_t& bestDiff) +{ + const uint32_t lastLayer = static_cast(nLayers - 1); + TrackITSInternal topResult; + TrackITSInternal botResult; + bool hasTopResult{false}; + bool hasBotResult{false}; + + if (extendTop && backup.getLastClusterLayer() != lastLayer) { + auto candidate = backup; + if (followDirection(candidate, true)) { + topResult = candidate; + hasTopResult = true; + bestTrial.update(candidate, best, bestDiff); + } + } + if (extendBot && backup.getFirstClusterLayer() != 0) { + auto candidate = backup; + if (followDirection(candidate, false)) { + botResult = candidate; + hasBotResult = true; + bestTrial.update(candidate, best, bestDiff); + } + } + if (extendTop && extendBot) { + if (hasTopResult && topResult.getFirstClusterLayer() != 0) { + auto candidate = topResult; + if (followDirection(candidate, false)) { + bestTrial.update(candidate, best, bestDiff); + } + } + if (hasBotResult && botResult.getLastClusterLayer() != lastLayer) { + auto candidate = botResult; + if (followDirection(candidate, true)) { + bestTrial.update(candidate, best, bestDiff); + } + } + } +} + +template +GPUhdi() bool followTrackExtensionDirection(const TrackExtensionHypothesis& startHypothesis, + const track::TrackFitContext& fit, + const TrackFollowContext& ctx, + const bool outward, + TrackExtensionHypothesis* activeHypotheses, + TrackExtensionHypothesis* nextHypotheses, + TrackExtensionHypothesis& bestHypothesis) +{ + const auto& utils = *ctx.utils; + const int step = outward ? 1 : -1; + const int end = outward ? fit.nLayers - 1 : 0; + const int maxHypotheses = o2::gpu::CAMath::Max(ctx.maxHypotheses, 1); + int nActive{1}; + int nNext{0}; + activeHypotheses[0] = startHypothesis; + + const int tableSize = utils.getNphiBins() * utils.getNzBins() + 1; + for (int iLayer = activeHypotheses[0].edgeLayer + step; nActive > 0; iLayer += step) { + if ((step > 0 && iLayer > end) || (step < 0 && iLayer < end)) { + break; + } + nNext = 0; + for (int iHypo{0}; iHypo < nActive; ++iHypo) { + auto hypo = activeHypotheses[iHypo]; + const float r = ctx.layerRadii[iLayer]; + float x{-999.f}; + if (!hypo.param.getXatLabR(r, x, fit.bz, o2::track::DirAuto) || x <= 0.f) { + continue; + } + + if (!fit.propagator->propagateToX(hypo.param, x, fit.bz, o2::base::PropagatorF::MAX_SIN_PHI, + o2::base::PropagatorF::MAX_STEP, fit.matCorrType)) { + continue; + } + if (fit.matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE && + !hypo.param.correctForMaterial(fit.layerxX0[iLayer], fit.layerxX0[iLayer] * constants::Radl * constants::Rho, true)) { + continue; + } + + const float ePhi{o2::gpu::CAMath::Sqrt(hypo.param.getSigmaSnp2() / hypo.param.getCsp2())}; + const float eZ{o2::gpu::CAMath::Sqrt(hypo.param.getSigmaZ2())}; + const int4 selectedBins = getBinsRect(iLayer, hypo.param.getPhi(), hypo.param.getZ(), ctx.nSigmaCutZ * eZ, ctx.nSigmaCutPhi * ePhi, utils); + if (selectedBins.x < 0) { + continue; + } + + int phiBinsNum = selectedBins.w - selectedBins.y + 1; + if (phiBinsNum < 0) { + phiBinsNum += ctx.phiBins; + } + + const auto rofRange = ctx.rofOverlaps.getLayer(iLayer).getROFRange(hypo.time); + for (int rof = rofRange.getFirstEntry(); rof < rofRange.getEntriesBound(); ++rof) { + if (!ctx.rofMask.isROFEnabled(iLayer, rof)) { + continue; + } + const int rofStart = ctx.ROFClusters[iLayer][rof]; + const int nLayerClusters = ctx.ROFClusters[iLayer][rof + 1] - rofStart; + if (nLayerClusters <= 0) { + continue; + } + const Cluster* layerClusters = ctx.clusters[iLayer] + rofStart; + const int* indexTable = ctx.clustersIndexTables[iLayer] + rof * tableSize; + const int zBinRange = selectedBins.z - selectedBins.x + 1; + for (int iPhiCount = 0; iPhiCount < phiBinsNum; ++iPhiCount) { + const int iPhiBin = (selectedBins.y + iPhiCount) % ctx.phiBins; + const int firstBinIndex = utils.getBinIndex(selectedBins.x, iPhiBin); + const int maxBinIndex = firstBinIndex + zBinRange; + const int firstRowClusterIndex = indexTable[firstBinIndex]; + const int maxRowClusterIndex = indexTable[maxBinIndex]; + for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { + if (iNextCluster >= nLayerClusters) { + break; + } + const Cluster& nextCluster = layerClusters[iNextCluster]; + if (ctx.usedClusters[iLayer][nextCluster.clusterId]) { + continue; + } + + const TrackingFrameInfo& trackingHit = fit.tfInfos[iLayer][nextCluster.clusterId]; + auto updated = hypo; + if (!updated.param.rotate(trackingHit.alphaTrackingFrame) || + !fit.propagator->propagateToX(updated.param, trackingHit.xTrackingFrame, fit.bz, + o2::base::PropagatorF::MAX_SIN_PHI, + o2::base::PropagatorF::MAX_STEP, + fit.matCorrType)) { + continue; + } + + const auto predChi2 = updated.param.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame); + if (predChi2 < 0.f || predChi2 > fit.maxChi2ClusterAttachment) { + continue; + } + if (!updated.param.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { + continue; + } + updated.chi2 += predChi2; + updated.clusters[iLayer] = nextCluster.clusterId; + ++updated.nClusters; + updated.edgeLayer = iLayer; + updated.time += ctx.rofOverlaps.getLayer(iLayer).getROFTimeBounds(rof, true); + keepTrackExtensionHypothesis(updated, nextHypotheses, nNext, maxHypotheses); + } + } + } + keepTrackExtensionHypothesis(hypo, nextHypotheses, nNext, maxHypotheses); + } + if (nNext == 0) { + break; + } + for (int iHypo{0}; iHypo < nNext; ++iHypo) { + activeHypotheses[iHypo] = nextHypotheses[iHypo]; + } + nActive = nNext; + } + + const TrackExtensionHypothesis* bestHypo{nullptr}; + for (int iHypo{0}; iHypo < nActive; ++iHypo) { + const auto& hypo = activeHypotheses[iHypo]; + if (hypo.nClusters == startHypothesis.nClusters) { + continue; + } + const float maxChi2 = fit.maxChi2NDF * static_cast(hypo.nClusters * 2 - 5); + if (hypo.chi2 >= maxChi2) { + continue; + } + if (!bestHypo || track::isBetter(hypo.nClusters, hypo.chi2, bestHypo->nClusters, bestHypo->chi2)) { + bestHypo = &hypo; + } + } + if (!bestHypo) { + return false; + } + + bestHypothesis = *bestHypo; + return true; +} + +} // namespace o2::its + +#endif // TRACKINGITSU_INCLUDE_TRACKFOLLOWER_H_ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackHelpers.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackHelpers.h new file mode 100644 index 0000000000000..b9924a8197d4c --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackHelpers.h @@ -0,0 +1,321 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file TrackHelpers.h +/// \brief Shared host/device helpers for ITS tracker trait implementations +/// + +#ifndef O2_ITS_TRACKING_TRACKHELPERS_H_ +#define O2_ITS_TRACKING_TRACKHELPERS_H_ + +#include "CommonConstants/MathConstants.h" +#include "DataFormatsITS/TrackITS.h" +#include "ITStracking/Cell.h" +#include "ITStracking/Cluster.h" +#include "ITStracking/Constants.h" +#include "ITStracking/LayerMask.h" +#include "ITStracking/MathUtils.h" +#include "ITStracking/TrackITSInternal.h" +#include "DetectorsBase/Propagator.h" +#include "ReconstructionDataFormats/Track.h" + +namespace o2::its::track +{ + +GPUhdi() bool isBetter(const int nClustersA, const float chi2A, const int nClustersB, const float chi2B) +{ + return (nClustersA > nClustersB) || (nClustersA == nClustersB && chi2A < chi2B); +} + +GPUhdi() bool isBetter(const auto& a, const auto& b) +{ + return isBetter(a.getNumberOfClusters(), a.getChi2(), b.getNumberOfClusters(), b.getChi2()); +} + +template +struct TrackSeedSelector { + float maxQ2Pt; + float maxChi2; + int maxHoles; + int minTrackLength; + LayerMask holeLayerMask; + LayerMask nonSeedingLayerMask; + + GPUhd() TrackSeedSelector(float maxQ2Pt, float maxChi2NDF, int startLevel, int maxHoles, int minTrackLength, LayerMask holeLayerMask, LayerMask nonSeedingLayerMask) + : maxQ2Pt{maxQ2Pt}, maxChi2{maxChi2NDF * ((startLevel + 2) * 2 - 5)}, maxHoles{maxHoles}, minTrackLength{minTrackLength}, holeLayerMask{holeLayerMask}, nonSeedingLayerMask{nonSeedingLayerMask} + { + } + + static GPUhdi() int getEffectiveTrackLength(LayerMask hitLayerMask, LayerMask excludedLayerMask) + { + if (hitLayerMask.empty()) { + return 0; + } + return hitLayerMask.length() - (LayerMask::span(hitLayerMask.first(), hitLayerMask.last()) & excludedLayerMask).count(); + } + + static GPUhdi() LayerMask getEffectiveHoleMask(LayerMask hitLayerMask, LayerMask excludedLayerMask) + { + return hitLayerMask.holeMask() & ~excludedLayerMask; + } + + GPUhd() bool operator()(const TrackSeed& seed) const + { + const auto hitLayerMask = seed.getHitLayerMask(); + return !(seed.getQ2Pt() > maxQ2Pt || seed.getChi2() > maxChi2) && + getEffectiveTrackLength(hitLayerMask, nonSeedingLayerMask) >= minTrackLength && + getEffectiveHoleMask(hitLayerMask, nonSeedingLayerMask).isAllowedHoleMask(maxHoles, holeLayerMask); + } +}; + +// Find the populated interior layer closest to the radial midpoint. +// If no layer can be found, return constants::UnusedIndex. +// Should minimize the sagitta bias. +template +GPUdi() int selectReseedMidLayer(int minLayer, int maxLayer, const float* layerRadii, const TrackSeed& seed) +{ + int midLayer = constants::UnusedIndex; + float distanceToMidR = layerRadii[NLayers - 1]; // midpoint cannot be last layer + const float midR = 0.5f * (layerRadii[maxLayer] + layerRadii[minLayer]); + for (int iLayer = minLayer + 1; iLayer < maxLayer; ++iLayer) { + if (seed.getCluster(iLayer) != constants::UnusedIndex) { + const float distance = o2::gpu::CAMath::Abs(midR - layerRadii[iLayer]); + if (distance < distanceToMidR) { // keep the smaller-radius layer on ties + midLayer = iLayer; + distanceToMidR = distance; + } + } + } + return midLayer; +} + +GPUdi() void resetTrackCovariance(o2::track::TrackParCov& track) +{ + track.resetCovariance(); + track.setCov(track.getQ2Pt() * track.getQ2Pt() * track.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); +} + +GPUdi() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, + const Cluster& cluster2, + const TrackingFrameInfo& tf3, + const float bz, + const bool reverse = false) +{ + float ca = constants::UnsetValue, sa = constants::UnsetValue, snp = constants::UnsetValue, q2pt = constants::UnsetValue, q2pt2 = constants::UnsetValue; + o2::gpu::CAMath::SinCos(tf3.alphaTrackingFrame, sa, ca); + const float sign = reverse ? -1.f : 1.f; + const float x1 = (cluster1.xCoordinate * ca) + (cluster1.yCoordinate * sa); + const float y1 = (-cluster1.xCoordinate * sa) + (cluster1.yCoordinate * ca); + const float x2 = (cluster2.xCoordinate * ca) + (cluster2.yCoordinate * sa); + const float y2 = (-cluster2.xCoordinate * sa) + (cluster2.yCoordinate * ca); + const float x3 = tf3.xTrackingFrame; + const float y3 = tf3.positionTrackingFrame[0]; + if (o2::gpu::CAMath::Abs(bz) < 0.01f) { // zero field + const float dx = x3 - x1; + const float dy = y3 - y1; + snp = sign * dy / o2::gpu::CAMath::Hypot(dx, dy); + q2pt = 1.f / o2::track::kMostProbablePt; + q2pt2 = 1.f; + } else { + const float crv = math_utils::computeCurvature(x3, y3, x2, y2, x1, y1); + snp = sign * crv * (x3 - math_utils::computeCurvatureCentreX(x3, y3, x2, y2, x1, y1)); + q2pt = sign * crv / (bz * o2::constants::math::B2C); + q2pt2 = crv * crv; + } + const float tgl = -0.5f * sign * (math_utils::computeTanDipAngle(x1, y1, x2, y2, cluster1.zCoordinate, cluster2.zCoordinate) + math_utils::computeTanDipAngle(x2, y2, x3, y3, cluster2.zCoordinate, tf3.positionTrackingFrame[1])); + const float sg2q2pt = o2::track::kC1Pt2max * o2::gpu::CAMath::Clamp(q2pt2, 0.0005f, 1.0f); + return {x3, tf3.alphaTrackingFrame, {y3, tf3.positionTrackingFrame[1], snp, tgl, q2pt}, {tf3.covarianceTrackingFrame[0], tf3.covarianceTrackingFrame[1], tf3.covarianceTrackingFrame[2], 0.f, 0.f, o2::track::kCSnp2max, 0.f, 0.f, 0.f, o2::track::kCTgl2max, 0.f, 0.f, 0.f, 0.f, sg2q2pt}}; +} + +template +GPUdi() TrackITSInternal seedTrackForRefit(const TrackSeed& seed, + const TrackingFrameInfo* const* foundTrackingFrameInfo, + const Cluster* const* unsortedClusters, + const float* layerRadii, + const float bz, + const int reseedIfShorter) +{ + TrackITSInternal temporaryTrack; + temporaryTrack.paramIn = static_cast(seed); + int lrMin = NLayers; + int lrMax = 0; + for (int iL{0}; iL < NLayers; ++iL) { + const int idx = seed.getCluster(iL); + temporaryTrack.setClusterIndex(iL, idx); + if (idx != constants::UnusedIndex) { + lrMin = o2::gpu::CAMath::Min(lrMin, iL); + lrMax = o2::gpu::CAMath::Max(lrMax, iL); + } + } + + const int ncl = temporaryTrack.getNClusters(); + if (ncl < reseedIfShorter && ncl > 2) { + const int lrMid = selectReseedMidLayer(lrMin, lrMax, layerRadii, seed); + if (lrMid != constants::UnusedIndex) { + const auto& cluster0TF = foundTrackingFrameInfo[lrMin][seed.getCluster(lrMin)]; + const auto& cluster1GL = unsortedClusters[lrMid][seed.getCluster(lrMid)]; + const auto& cluster2GL = unsortedClusters[lrMax][seed.getCluster(lrMax)]; + temporaryTrack.paramIn = buildTrackSeed(cluster2GL, cluster1GL, cluster0TF, bz, true); + } + } + + resetTrackCovariance(temporaryTrack.paramIn); + return temporaryTrack; +} + +// Inputs shared by fit/refit calls within a tracking pass. +template +struct TrackFitContext { + const TrackingFrameInfo* const* tfInfos{nullptr}; + const float* layerxX0{nullptr}; + int nLayers{0}; + float bz{0.f}; + float maxChi2ClusterAttachment{0.f}; + float maxChi2NDF{0.f}; + const o2::base::Propagator* propagator{nullptr}; + o2::base::PropagatorF::MatCorrType matCorrType{o2::base::PropagatorF::MatCorrType::USEMatCorrNONE}; + bool shiftRefToCluster{false}; + bool repeatRefitOut{false}; +}; + +template +GPUdi() bool fitTrack(TrackITSInternal& trk, + o2::track::TrackParCov& param, + int start, + int end, + int step, + float maxQoverPt, + int nCl, + const TrackFitContext& ctx, + o2::track::TrackPar* linRef = nullptr) +{ + for (int iLayer{start}; iLayer != end; iLayer += step) { + const int clsIdx = trk.getClusterIndex(iLayer); + if (clsIdx == constants::UnusedIndex) { + continue; + } + + const TrackingFrameInfo& trackingHit = ctx.tfInfos[iLayer][clsIdx]; + if (linRef) { + if (!param.o2::track::TrackParCovF::rotate(trackingHit.alphaTrackingFrame, *linRef, ctx.bz)) { + return false; + } + if (!ctx.propagator->propagateToX(param, *linRef, trackingHit.xTrackingFrame, ctx.bz, + o2::base::PropagatorImpl::MAX_SIN_PHI, + o2::base::PropagatorImpl::MAX_STEP, + ctx.matCorrType)) { + return false; + } + if (ctx.matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!param.correctForMaterial(*linRef, ctx.layerxX0[iLayer], ctx.layerxX0[iLayer] * constants::Radl * constants::Rho, true)) { + continue; + } + } + } else { + if (!param.o2::track::TrackParCovF::rotate(trackingHit.alphaTrackingFrame)) { + return false; + } + if (!ctx.propagator->propagateToX(param, trackingHit.xTrackingFrame, ctx.bz, + o2::base::PropagatorImpl::MAX_SIN_PHI, + o2::base::PropagatorImpl::MAX_STEP, + ctx.matCorrType)) { + return false; + } + if (ctx.matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!param.correctForMaterial(ctx.layerxX0[iLayer], ctx.layerxX0[iLayer] * constants::Radl * constants::Rho, true)) { + continue; + } + } + } + + const auto predChi2{param.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; + if ((nCl >= 3 && predChi2 > ctx.maxChi2ClusterAttachment) || predChi2 < 0.f) { + return false; + } + trk.setChi2(trk.getChi2() + predChi2); + if (!param.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { + return false; + } + if (linRef && ctx.shiftRefToCluster) { + linRef->setY(trackingHit.positionTrackingFrame[0]); + linRef->setZ(trackingHit.positionTrackingFrame[1]); + } + nCl++; + } + + return o2::gpu::CAMath::Abs(param.getQ2Pt()) < maxQoverPt && trk.getChi2() < ctx.maxChi2NDF * (float)((nCl * 2) - 5); +} + +template +GPUdi() bool refitTrack(TrackITSInternal& track, + const TrackFitContext& ctx, + const float minPt = -1.f) +{ + o2::track::TrackPar linRef{track.paramIn}; + resetTrackCovariance(track.paramIn); + track.setChi2(0); + bool fitSuccess = fitTrack(track, track.paramIn, 0, ctx.nLayers, 1, + o2::constants::math::VeryBig, 0, ctx, &linRef); + if (!fitSuccess) { + return false; + } + + track.paramOut = track.paramIn; + linRef = track.paramOut; + resetTrackCovariance(track.paramIn); + track.setChi2(0); + fitSuccess = fitTrack(track, track.paramIn, ctx.nLayers - 1, -1, -1, + 50.f, 0, ctx, &linRef); + if (!fitSuccess) { + return false; + } + if (minPt > 0.f && track.getPt() < minPt) { + return false; + } + if (ctx.repeatRefitOut) { // repeat outward refit seeding and linearizing with the stable inward fit result + o2::track::TrackParCov saveInw{track.paramIn}; + linRef = saveInw; // use refitted track as lin.reference + float saveChi2 = track.getChi2(); + track.paramOut = saveInw; + track::resetTrackCovariance(track.paramOut); + track.setChi2(0); + fitSuccess = fitTrack(track, track.paramOut, 0, ctx.nLayers, 1, + o2::constants::math::VeryBig, 0, ctx, &linRef); + if (!fitSuccess) { + return false; + } + track.paramIn = saveInw; + track.setChi2(saveChi2); + } + return true; +} + +template +GPUdi() bool refitTrackSeed(const TrackSeed& trackSeed, + TrackITSInternal& temporaryTrack, + const TrackFitContext& ctx, + const Cluster* const* clusters, + const float* layerRadii, + const float* minPt, + const int reseedIfShorter) +{ + temporaryTrack = seedTrackForRefit(trackSeed, + ctx.tfInfos, + clusters, + layerRadii, + ctx.bz, + reseedIfShorter); + return refitTrack(temporaryTrack, ctx, minPt[NLayers - temporaryTrack.getNClusters()]); +} + +} // namespace o2::its::track + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackITSInternal.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackITSInternal.h new file mode 100644 index 0000000000000..28a1117324550 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackITSInternal.h @@ -0,0 +1,113 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef TRACKINGITSU_INCLUDE_TRACKITSINTERNAL_H_ +#define TRACKINGITSU_INCLUDE_TRACKITSINTERNAL_H_ + +#include + +#include "GPUCommonDef.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "ITStracking/Constants.h" +#include "ReconstructionDataFormats/Track.h" + +namespace o2::its +{ + +template +struct TrackITSInternal { + GPUhdi() TrackITSInternal() { resetClusters(); } + + GPUhdi() void resetClusters() + { + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + clusters[iLayer] = constants::UnusedIndex; + } + nClusters = 0; + } + + GPUhdi() int getClusterIndex(int layer) const { return clusters[layer]; } + + GPUhdi() void setClusterIndex(int layer, int cluster) + { + if (clusters[layer] == constants::UnusedIndex && cluster != constants::UnusedIndex) { + ++nClusters; + } else if (clusters[layer] != constants::UnusedIndex && cluster == constants::UnusedIndex) { + --nClusters; + } + clusters[layer] = cluster; + } + + GPUhdi() int getNClusters() const { return nClusters; } + GPUhdi() int getNumberOfClusters() const { return nClusters; } + GPUhdi() float getChi2() const { return chi2; } + GPUhdi() void setChi2(float value) { chi2 = value; } + GPUdi() float getPt() const { return paramIn.getPt(); } + + GPUhdi() uint32_t getPattern() const + { + uint32_t pattern{0}; + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + if (clusters[iLayer] != constants::UnusedIndex) { + pattern |= (0x1u << iLayer); + } + } + return pattern; + } + + GPUhdi() int getFirstClusterLayer() const + { + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + if (clusters[iLayer] != constants::UnusedIndex) { + return iLayer; + } + } + return constants::UnusedIndex; + } + + GPUhdi() int getLastClusterLayer() const + { + for (int iLayer{NLayers - 1}; iLayer >= 0; --iLayer) { + if (clusters[iLayer] != constants::UnusedIndex) { + return iLayer; + } + } + return constants::UnusedIndex; + } + + o2::track::TrackParCov paramIn; + o2::track::TrackParCov paramOut; + std::array clusters{}; + TimeEstBC time; + float chi2{0.f}; + int nClusters{0}; +}; + +template +GPUhdi() TrackITSExt makeTrackITSExt(const TrackITSInternal& track) +{ + TrackITSExt out; + out.getParamIn() = track.paramIn; + out.getParamOut() = track.paramOut; + out.setChi2(track.chi2); + out.getTimeStamp() = track.time.makeSymmetrical(); + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + if (track.clusters[iLayer] != constants::UnusedIndex) { + out.setExternalClusterIndex(iLayer, track.clusters[iLayer], true); + } + } + return out; +} + +} // namespace o2::its + +#endif /* TRACKINGITSU_INCLUDE_TRACKITSINTERNAL_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index e5e830c717521..c60f280de5307 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -18,24 +18,22 @@ #include #include -#include #include #include #include #include +#include #include #include +#include + +#include #include "ITStracking/Configuration.h" -#include "CommonConstants/MathConstants.h" #include "ITStracking/Definitions.h" -#include "ITStracking/ROframe.h" -#include "ITStracking/MathUtils.h" #include "ITStracking/TimeFrame.h" -#include "ITStracking/Road.h" - -#include "DataFormatsITS/TrackITS.h" -#include "SimulationDataFormat/MCCompLabel.h" +#include "ITStracking/TrackerTraits.h" +#include "ITStracking/BoundedAllocator.h" namespace o2 { @@ -46,89 +44,113 @@ class GPUChainITS; } namespace its { -class TrackerTraits; +template class Tracker { + using LogFunc = std::function; public: - Tracker(TrackerTraits* traits); + Tracker(TrackerTraits* traits); - Tracker(const Tracker&) = delete; - Tracker& operator=(const Tracker&) = delete; - ~Tracker(); + void adoptTimeFrame(TimeFrame& tf); - void adoptTimeFrame(TimeFrame& tf); + float clustersToTracks( + const LogFunc& = [](const std::string& s) { std::cout << s << '\n'; }, + const LogFunc& = [](const std::string& s) { std::cerr << s << '\n'; }); - void clustersToTracks( - std::function = [](std::string s) { std::cout << s << std::endl; }, std::function = [](std::string s) { std::cerr << s << std::endl; }); - std::vector& getTracks(); - - void setParameters(const std::vector&); + void setParameters(const std::vector& p) { mTrkParams = p; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } std::vector& getParameters() { return mTrkParams; } - void getGlobalConfiguration(); - void setBz(float); - void setCorrType(const o2::base::PropagatorImpl::MatCorrType type); - bool isMatLUT() const; - void setNThreads(int n); - int getNThreads() const; + void setBz(float bz) { mTraits->setBz(bz); } + void setTimeSlice(size_t slice) noexcept { mTimeSlice = slice; } + void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } + void printSummary() const; + void computeTracksMClabels(); private: - void initialiseTimeFrame(int& iteration); - void computeTracklets(int& iteration); - void computeCells(int& iteration); - void findCellsNeighbours(int& iteration); - void findRoads(int& iteration); - void findShortPrimaries(); - void findTracks(); - void extendTracks(int& iteration); - - // MC interaction - void computeRoadsMClabels(); - void computeTracksMClabels(); + void initialiseTimeFrame(int iteration) { mTraits->initialiseTimeFrame(iteration); } + void computeTracklets(int iteration, int iVertex) { mTraits->computeLayerTracklets(iteration, iVertex); } + void computeCells(int iteration) { mTraits->computeLayerCells(iteration); } + void findCellsNeighbours(int iteration) { mTraits->findCellsNeighbours(iteration); } + void findRoads(int iteration) { mTraits->findRoads(iteration); } + void rectifyClusterIndices(); + void sortTracks(); - template - float evaluateTask(void (Tracker::*)(T...), const char*, std::function logger, T&&... args); + template + float evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, const LogFunc& logger, F&&... args); - TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class - TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class + TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class + TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class std::vector mTrkParams; - std::uint32_t mTimeFrameCounter = 0; o2::gpu::GPUChainITS* mRecoChain = nullptr; - unsigned int mNumberOfRuns{0}; + size_t mTimeSlice{0}; // current timeslice + unsigned int mNumberOfDroppedTFs{0}; + unsigned int mTimeFrameCounter{0}; + double mTotalTime{0}; + std::shared_ptr mMemoryPool; + + enum Steps { + TFInit = 0, + Trackleting, + Celling, + Neighbouring, + Roading, + Extending, + NSteps, + }; + Steps mCurStep{TFInit}; + static constexpr std::array StateNames{"TimeFrame initialisation", "Tracklet finding", "Cell finding", "Neighbour finding", "Road finding", "Track extending"}; + std::vector> mTimingStats; + void addTimingStatCurStep(int iteration, double timeMs); }; -inline void Tracker::setParameters(const std::vector& trkPars) -{ - mTrkParams = trkPars; -} - -template -float Tracker::evaluateTask(void (Tracker::*task)(T...), const char* taskName, std::function logger, - T&&... args) +template +template +float Tracker::evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, const LogFunc& logger, F&&... args) { float diff{0.f}; - if (constants::DoTimeBenchmarks) { + if (mTrkParams[iteration].PrintMemory) { + mMemoryPool->resetPeakMemory(); + } + + if constexpr (constants::DoTimeBenchmarks) { auto start = std::chrono::high_resolution_clock::now(); - (this->*task)(std::forward(args)...); + (this->*task)(std::forward(args)...); auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration diff_t{end - start}; diff = diff_t.count(); std::stringstream sstream; - if (taskName == nullptr) { + if (taskName.empty()) { sstream << diff << "\t"; } else { sstream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms"; } logger(sstream.str()); + + if (mTrkParams[iteration].SaveTimeBenchmarks) { + std::string taskNameStr(taskName); + std::transform(taskNameStr.begin(), taskNameStr.end(), taskNameStr.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::replace(taskNameStr.begin(), taskNameStr.end(), ' ', '_'); + if (std::ofstream file{"its_time_benchmarks.txt", std::ios::app}) { + file << "trk:" << iteration << '\t' << taskNameStr << '\t' << diff << '\n'; + } + addTimingStatCurStep(iteration, diff); + } + } else { - (this->*task)(std::forward(args)...); + (this->*task)(std::forward(args)...); + } + + if (mTrkParams[iteration].PrintMemory) { + LOGP(info, "iter:{}:{}: {}", iteration, StateNames[mCurStep], mMemoryPool->asString()); } return diff; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index 03ec737250f50..4d6378aded0e8 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -16,23 +16,17 @@ #ifndef TRACKINGITSU_INCLUDE_TRACKERTRAITS_H_ #define TRACKINGITSU_INCLUDE_TRACKERTRAITS_H_ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include "DetectorsBase/Propagator.h" -#include "DetectorsBase/MatLayerCylSet.h" #include "ITStracking/Configuration.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/MathUtils.h" +#include "ITStracking/IndexTableUtils.h" #include "ITStracking/TimeFrame.h" -#include "ITStracking/Road.h" +#include "ITStracking/Cell.h" +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/TrackExtensionHypothesis.h" +#include "ITStracking/TrackITSInternal.h" // #define OPTIMISATION_OUTPUT @@ -44,122 +38,81 @@ class GPUChainITS; } namespace its { - class TrackITSExt; -typedef std::function& roads, std::vector&, std::vector&, const std::vector>&, std::vector&)> FuncRunITSTrackFit_t; +template class TrackerTraits { public: + using IndexTableUtilsN = IndexTableUtils; + using TrackSeedN = TrackSeed; + virtual ~TrackerTraits() = default; - virtual void adoptTimeFrame(TimeFrame* tf); - virtual void initialiseTimeFrame(const int iteration); - virtual void computeLayerTracklets(const int iteration); + virtual void adoptTimeFrame(TimeFrame* tf) { mTimeFrame = tf; } + virtual void initialiseTimeFrame(const int iteration) { mTimeFrame->initialise(mTrkParams[iteration], mTrkParams[iteration].NLayers, iteration); } + + virtual void computeLayerTracklets(const int iteration, int iVertex); virtual void computeLayerCells(const int iteration); virtual void findCellsNeighbours(const int iteration); virtual void findRoads(const int iteration); - virtual void findTracks(); - virtual void extendTracks(const int iteration); - virtual void findShortPrimaries(); - virtual void refitTracks(const int iteration, const std::vector>&, std::vector&); - virtual void setBz(float bz); - virtual bool trackFollowing(TrackITSExt* track, int rof, bool outward, const int iteration); - void UpdateTrackingParameters(const std::vector& trkPars); - TimeFrame* getTimeFrame() { return mTimeFrame; } + template + void processNeighbours(int iteration, int defaultCellTopologyId, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, const bounded_vector& currentCellTopologyId, bounded_vector& updatedCellSeed, bounded_vector& updatedCellId, bounded_vector& updatedCellTopologyId); - void setIsGPU(const unsigned char isgpu) { mIsGPU = isgpu; }; - float getBz() const; - void setCorrType(const o2::base::PropagatorImpl::MatCorrType type) { mCorrType = type; } - bool isMatLUT() const; + void acceptTracks(int iteration, bounded_vector& tracks, const bounded_vector& trackIndices, bounded_vector>& firstClusters); + void markTracks(int iteration); - // Others - GPUhd() static constexpr int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } - const int4 getBinsRect(const Cluster&, int layer, float z1, float z2, float maxdeltaz, float maxdeltaphi); - const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z, float maxdeltaz); - const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz); - void SetRecoChain(o2::gpu::GPUChainITS* chain, FuncRunITSTrackFit_t&& funcRunITSTrackFit) + void updateTrackingParameters(const std::vector& trkPars) { - mChainRunITSTrackFit = funcRunITSTrackFit; - mChain = chain; + mTrkParams = trkPars; } - void setSmoothing(bool v) { mApplySmoothing = v; } - bool getSmoothing() const { return mApplySmoothing; } - void setNThreads(int n); - int getNThreads() const { return mNThreads; } + TimeFrame* getTimeFrame() { return mTimeFrame; } - // TimeFrame information forwarding - virtual int getTFNumberOfClusters() const; - virtual int getTFNumberOfTracklets() const; - virtual int getTFNumberOfCells() const; - - float mBz = 5.f; + virtual void setBz(float bz); + float getBz() const { return mBz; } + virtual const char* getName() const noexcept { return "CPU"; } + virtual bool isGPU() const noexcept { return false; } + void setMemoryPool(std::shared_ptr pool) noexcept { mMemoryPool = pool; } + auto getMemoryPool() const noexcept { return mMemoryPool; } - private: - void traverseCellsTree(const int, const int); - track::TrackParCov buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const Cluster& cluster3, const TrackingFrameInfo& tf3); - bool fitTrack(TrackITSExt& track, int start, int end, int step, const float chi2clcut = o2::constants::math::VeryBig, const float chi2ndfcut = o2::constants::math::VeryBig, const float maxQoverPt = o2::constants::math::VeryBig); + // Others + void setNThreads(int n, std::shared_ptr& arena); + int getNThreads() { return mTaskArena->max_concurrency(); } - int mNThreads = 1; - bool mApplySmoothing = false; - o2::base::PropagatorImpl::MatCorrType mCorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; + // TimeFrame information forwarding + virtual int getTFNumberOfClusters() const { return mTimeFrame->getNumberOfClusters(); } + virtual int getTFNumberOfTracklets() const { return mTimeFrame->getNumberOfTracklets(); } + virtual int getTFNumberOfCells() const { return mTimeFrame->getNumberOfCells(); } - // virtual bool checkTFMemory(const int iteration); + private: + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; protected: - TimeFrame* mTimeFrame; - std::vector mTrkParams; - bool mIsGPU = false; - o2::gpu::GPUChainITS* mChain = nullptr; - FuncRunITSTrackFit_t mChainRunITSTrackFit; -}; + struct TrackFollowerScratch { + explicit TrackFollowerScratch(std::pmr::memory_resource* memoryResource) + : activeHypotheses(memoryResource), nextHypotheses(memoryResource) + { + } + + bounded_vector> activeHypotheses; + bounded_vector> nextHypotheses; + }; + + bool finaliseTrackSeed(const TrackSeedN& seed, + TrackITSExt& track, + const int iteration, + const TrackingFrameInfo* const* tfInfos, + const Cluster* const* unsortedClusters, + const o2::base::Propagator* propagator); -inline float TrackerTraits::getBz() const -{ - return mBz; -} - -inline void TrackerTraits::UpdateTrackingParameters(const std::vector& trkPars) -{ - mTrkParams = trkPars; -} - -inline const int4 TrackerTraits::getBinsRect(const int layerIndex, float phi, float maxdeltaphi, float z, float maxdeltaz) -{ - return getBinsRect(layerIndex, phi, maxdeltaphi, z, z, maxdeltaz); -} - -inline const int4 TrackerTraits::getBinsRect(const Cluster& currentCluster, int layerIndex, float z1, float z2, float maxdeltaz, float maxdeltaphi) -{ - return getBinsRect(layerIndex, currentCluster.phi, maxdeltaphi, z1, z2, maxdeltaz); -} - -inline void TrackerTraits::initialiseTimeFrame(const int iteration) -{ - mTimeFrame->initialise(iteration, mTrkParams[iteration], 7); - setIsGPU(false); -} - -inline const int4 TrackerTraits::getBinsRect(const int layerIndex, float phi, float maxdeltaphi, - float z1, float z2, float maxdeltaz) -{ - const float zRangeMin = o2::gpu::GPUCommonMath::Min(z1, z2) - maxdeltaz; - const float phiRangeMin = phi - maxdeltaphi; - const float zRangeMax = o2::gpu::GPUCommonMath::Max(z1, z2) + maxdeltaz; - const float phiRangeMax = phi + maxdeltaphi; - - if (zRangeMax < -mTrkParams[0].LayerZ[layerIndex + 1] || - zRangeMin > mTrkParams[0].LayerZ[layerIndex + 1] || zRangeMin > zRangeMax) { + o2::gpu::GPUChainITS* mChain = nullptr; + TimeFrame* mTimeFrame; + std::vector mTrkParams; - return getEmptyBinsRect(); - } + float mBz{-999.f}; +}; - const IndexTableUtils& utils{mTimeFrame->mIndexTableUtils}; - return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), - o2::gpu::GPUCommonMath::Min(mTrkParams[0].ZBins - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), // /!\ trkParams can potentially change across iterations - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; -} } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 6512740389a50..ee67b7234450d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -12,47 +12,70 @@ #ifndef ALICEO2_ITSDPLTRACKINGPARAM_H_ #define ALICEO2_ITSDPLTRACKINGPARAM_H_ +#include #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" +#include "ITStracking/Constants.h" -namespace o2 -{ -namespace its +namespace o2::its { struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { + bool saveTimeBenchmarks = false; // dump metrics on file - bool allowSingleContribClusters = false; + int nIterations = 1; // Number of vertexing passes to perform. + int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a iteration. - // geometrical cuts - float zCut = 0.002f; // 0.002f - float phiCut = 0.005f; // 0.005f + // geometrical cuts for tracklet selection for Pb-Pb + float zCut = 0.002f; + float phiCut = 0.005f; float pairCut = 0.04f; - float clusterCut = 0.8f; - float histPairCut = 0.04f; + float clusterCut = 0.170048f; + float coarseZWindow = 0.055458f; + float seedDedupZCut = 0.116685f; + float refitDedupZCut = 0.039855f; + float duplicateZCut = 0.200097f; + float finalSelectionZCut = 0.034535f; + float duplicateDistance2Cut = 0.005117f; float tanLambdaCut = 0.002f; // tanLambda = deltaZ/deltaR - float lowMultXYcut2 = 0.25f; // XY cut for low-multiplicity pile up - float baseBeamError = 0.005f; + float nSigmaCut = 0.0164651f; float maxZPositionAllowed = 25.f; // 4x sZ of the beam - int clusterContributorsCut = 16; - int maxTrackletsPerCluster = 1e2; + + // Artefacts selections + int clusterContributorsCut = 2; // minimum number of contributors for an accepted final vertex + int suppressLowMultDebris = 16; // suppress all vertices below this threshold if a vertex was already found in a rof + int seedMemberRadiusTime = 0; + int seedMemberRadiusZ = 2; + int maxTrackletsPerCluster = 100; int phiSpan = -1; int zSpan = -1; - int ZBins = 1; - int PhiBins = 128; + int ZBins = 1; // z-phi index table configutation: number of z bins + int PhiBins = 128; // z-phi index table configutation: number of phi bins + + bool useTruthSeeding{false}; // overwrite seeding vertices with MC truth int nThreads = 1; + bool printMemory = false; + size_t maxMemory = std::numeric_limits::max(); + bool dropTFUponFailure = false; O2ParamDef(VertexerParamConfig, "ITSVertexerParam"); }; struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper { + static const int MinTrackLength = 4; + static const int MaxTrackLength = 7; - // Use TGeo for mat. budget - bool useMatCorrTGeo = false; - bool useFastMaterial = false; - float sysErrY2[7] = {0}; // systematic error^2 in Y per layer - float sysErrZ2[7] = {0}; // systematic error^2 in Z per layer + bool useMatCorrTGeo = false; // use full geometry to corect for material budget accounting in the fits. Default is to use the material budget LUT. + bool useFastMaterial = false; // use faster material approximation for material budget accounting in the fits. + int addTimeError[7] = {0}; // configure the width of the window in BC to be considered for the tracking. + int minTrackLgtIter[constants::MaxIter] = {}; // minimum track length at each iteration, used only if >0, otherwise use code defaults + uint8_t startLayerMask[constants::MaxIter] = {}; // mask of start layer for this iteration (if >0) + int maxHolesIter[constants::MaxIter] = {}; // maximum number of missing internal layers allowed in the CA topology for each iteration + uint16_t holeLayerMaskIter[constants::MaxIter] = {}; // layers that may be skipped by the CA topology for each iteration + float minPtIterLgt[constants::MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults + float sysErrY2[7] = {0}; // systematic error^2 in Y per layer + float sysErrZ2[7] = {0}; // systematic error^2 in Z per layer float maxChi2ClusterAttachment = -1.f; float maxChi2NDF = -1.f; float nSigmaCut = -1.f; @@ -61,37 +84,38 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper::max(); + bool dropTFUponFailure = false; + bool fataliseUponFailure = true; // granular management of the fatalisation in async mode - O2ParamDef(TrackerParamConfig, "ITSCATrackerParam"); -}; + // Selections on tracks sharing clusters + bool allowSharingFirstCluster = false; // allow first cluster sharing among tracks + float sharedClusterMaxDeltaPhi = 0.05f; // Maximum allowed delta phi at the cluster position + float sharedClusterMaxDeltaEta = 0.03f; // Maximum allowed delta eta at the cluster position + bool sharedClusterOppositeSign = false; // Require opposite sign of the tracklets -struct GpuRecoParamConfig : public o2::conf::ConfigurableParamHelper { - // GPU-specific parameters - size_t tmpCUBBufferSize = 1e5; // In average in pp events there are required 4096 bytes - size_t maxTrackletsPerCluster = 1e2; - size_t clustersPerLayerCapacity = 2.5e5; - size_t clustersPerROfCapacity = 1.5e3; - size_t trackletsCapacity = maxTrackletsPerCluster * clustersPerLayerCapacity; - size_t validatedTrackletsCapacity = 1e5; - size_t cellsLUTsize = validatedTrackletsCapacity; - size_t maxNeighboursSize = 1e4; - size_t neighboursLUTsize = maxNeighboursSize; - size_t maxLinesCapacity = 1e2; - size_t maxVerticesCapacity = 5e4; - size_t nTimeFramePartitions = 3; - int maxGPUMemoryGB = -1; - - O2ParamDef(GpuRecoParamConfig, "ITSGpuRecoParam"); + O2ParamDef(TrackerParamConfig, "ITSCATrackerParam"); }; -} // namespace its -} // namespace o2 +} // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h new file mode 100644 index 0000000000000..14c5d6a62e0ad --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h @@ -0,0 +1,109 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS_TRACKINGINTERFACE +#define O2_ITS_TRACKINGINTERFACE + +#include "Framework/DataProcessorSpec.h" + +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Tracker.h" +#include "ITStracking/TrackerTraits.h" +#include "ITStracking/Vertexer.h" +#include "ITStracking/VertexerTraits.h" +#include "ITStracking/BoundedAllocator.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "DataFormatsCalibration/MeanVertexObject.h" + +#include "GPUDataTypesIO.h" +#include "GPUO2ExternalUser.h" +#include "GPUChainITS.h" + +#include + +namespace o2::its +{ +class ITSTrackingInterface +{ + public: + static constexpr int NLayers{7}; + using VertexerN = Vertexer; + using VertexerTraitsN = VertexerTraits; + using TrackerN = Tracker; + using TrackerTraitsN = TrackerTraits; + using TimeFrameN = TimeFrame; + + ITSTrackingInterface(bool isMC, + bool doStag, + int trgType, + const bool overrBeamEst) + : mIsMC{isMC}, + mDoStaggering(doStag), + mUseTriggers{trgType}, + mOverrideBeamEstimation{overrBeamEst} {} + + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + void setMeanVertex(const o2::dataformats::MeanVertexObject* v) + { + if (v == nullptr) { + LOGP(error, "Mean Vertex Object is nullptr"); + return; + } else { + LOGP(info, "Mean Vertex set with x: {} y: {}", v->getX(), v->getY()); + } + mMeanVertex = v; + } + // Task callbacks + void initialise(); + void run(framework::ProcessingContext& pc); + void printSummary() const; + + virtual void updateTimeDependentParams(framework::ProcessingContext& pc); + virtual void finaliseCCDB(framework::ConcreteDataMatcher& matcher, void* obj); + + // Custom + void setTraitsFromProvider(VertexerTraitsN*, TrackerTraitsN*, TimeFrameN*); + void setTrackingMode(TrackingMode::Type mode = TrackingMode::Unset) { mMode = mode; } + + auto getTracker() const { return mTracker.get(); } + auto getVertexer() const { return mVertexer.get(); } + + TimeFrameN* mTimeFrame = nullptr; + + protected: + virtual void overrideParameters(std::vector& t, std::vector& v) {} + virtual void requestTopologyDictionary(framework::ProcessingContext& pc); + virtual void loadROF(gsl::span& trackROFspan, + gsl::span clusters, + gsl::span::iterator& pattIt, + int layer, + const dataformats::MCTruthContainer* mcLabels); + + private: + bool mIsMC = false; + bool mDoStaggering = false; + bool mRunVertexer = true; + bool mCosmicsProcessing = false; + int mUseTriggers = 0; + std::vector mFilter; + TrackingMode::Type mMode = TrackingMode::Unset; + bool mOverrideBeamEstimation = false; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + std::unique_ptr mTracker = nullptr; + std::unique_ptr mVertexer = nullptr; + const o2::dataformats::MeanVertexObject* mMeanVertex{}; + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; +}; + +} // namespace o2::its +#endif // O2_ITS_TRACKINGINTERFACE diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingTopology.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingTopology.h new file mode 100644 index 0000000000000..16f5e6f01e873 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingTopology.h @@ -0,0 +1,237 @@ +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef TRACKINGITSU_INCLUDE_TRACKINGTOPOLOGY_H_ +#define TRACKINGITSU_INCLUDE_TRACKINGTOPOLOGY_H_ + +#include +#include +#include +#include + +#ifndef GPUCA_GPUCODE +#include +#include +#include "Framework/Logger.h" +#endif + +#include "CommonDataFormat/RangeReference.h" +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "ITStracking/LayerMask.h" + +namespace o2::its +{ + +template +class TrackingTopology +{ + public: + static constexpr int MaxLinks = (NLayers * (NLayers - 1)) / 2; + static constexpr int MaxCells = (NLayers * (NLayers - 1) * (NLayers - 2)) / 6; + using Id = std::conditional_t::max(), uint8_t, uint16_t>; + using Mask = LayerMask; + using Range = o2::dataformats::RangeReference; + static_assert(NLayers < std::numeric_limits::max()); + static_assert(MaxLinks <= std::numeric_limits::max()); + static_assert(MaxCells <= std::numeric_limits::max()); + + // Describes from which layer to which layer the look-up happens + struct LayerLink { + Id fromLayer{0}; + Id toLayer{0}; + }; + static_assert(std::is_standard_layout_v); + static_assert(std::is_trivially_copyable_v); + static_assert(sizeof(LayerLink) == (2 * sizeof(Id))); + + // Describes from which LayerLink a tracklet is allowed to originate + // and with which LayerLink this can be combined additionally the hitMasked is cached + struct CellTopology { + Id firstLink{0}; + Id secondLink{0}; + Mask hitLayerMask{0}; + }; + static_assert(std::is_standard_layout_v); + static_assert(std::is_trivially_copyable_v); + static_assert(sizeof(CellTopology) == (2 * sizeof(Id)) + sizeof(Mask)); + + // GPU ready view of the underlying LUTs + struct View { + const LayerLink* links{nullptr}; + const CellTopology* cells{nullptr}; + const Range* cellsByFirstLinkIndex{nullptr}; + const Id* cellsByFirstLink{nullptr}; + Mask seedingLayerMask{0}; + Id nLinks{0}; + Id nCells{0}; + Id nCellsByFirstLink{0}; + + GPUhdi() const LayerLink& getLink(Id id) const { return links[id]; } + GPUhdi() const CellTopology& getCell(Id id) const { return cells[id]; } + GPUhdi() Range getCellsStartingWithLink(Id linkId) const { return cellsByFirstLinkIndex[linkId]; } + +#ifndef GPUCA_GPUCODE + std::string asString() const + { + std::string out = fmt::format("TrackingTopology: links={} cells={} seedingLayers={}", nLinks, nCells, seedingLayerMask.asString()); + out += "\n links:"; + for (Id linkId = 0; linkId < nLinks; ++linkId) { + const auto& t = links[linkId]; + out += fmt::format("\n {}: {} -> {}", linkId, t.fromLayer, t.toLayer); + } + out += "\n cells:"; + for (Id cellId = 0; cellId < nCells; ++cellId) { + const auto& c = cells[cellId]; + const auto& first = links[c.firstLink]; + const auto& second = links[c.secondLink]; + out += fmt::format("\n {}: {} -> {} -> {} hitMask={} links=({}, {})", cellId, first.fromLayer, first.toLayer, second.toLayer, c.hitLayerMask.asString(), c.firstLink, c.secondLink); + } + return out; + } + + void print() const + { + LOGP(info, "{}", asString()); + } +#endif + }; + + void init(int maxLayers, int maxHoles, Mask holeLayerMask, Mask seedingLayerMask = 0) + { + clear(); + mMaxLayers = o2::gpu::CAMath::Max(0, o2::gpu::CAMath::Min(maxLayers, NLayers)); + mMaxHoles = o2::gpu::CAMath::Max(maxHoles, 0); + mHoleLayerMask = holeLayerMask; + mSeedingLayerMask = seedingLayerMask.empty() ? Mask::span(0, mMaxLayers - 1) : (seedingLayerMask & Mask::span(0, mMaxLayers - 1)); +#ifndef GPUCA_GPUCODE + if (mSeedingLayerMask.count() < constants::ClustersPerCell) { + LOGP(fatal, "Tracking topology has {} seeding layers, but at least {} are required to build CA cells", mSeedingLayerMask.count(), constants::ClustersPerCell); + } +#endif + for (int fromLayer = 0; fromLayer < mMaxLayers; ++fromLayer) { + if (!mSeedingLayerMask.has(fromLayer)) { + continue; + } + for (int toLayer = fromLayer + 1; toLayer < mMaxLayers; ++toLayer) { + if (mSeedingLayerMask.has(toLayer) && isAllowedSeedingLink(fromLayer, toLayer)) { + mLinks[mNLinks++] = LayerLink{static_cast(fromLayer), static_cast(toLayer)}; + } + } + } + + for (Id firstId = 0; firstId < mNLinks; ++firstId) { + const auto& first = mLinks[firstId]; + for (Id secondId = 0; secondId < mNLinks; ++secondId) { + const auto& second = mLinks[secondId]; + if (first.toLayer != second.fromLayer) { + continue; + } + const Mask hitMask{first.fromLayer, first.toLayer, second.toLayer}; + if ((hitMask.holeMask() & mSeedingLayerMask).isAllowedHoleMask(mMaxHoles, mHoleLayerMask)) { + mCells[mNCells++] = CellTopology{firstId, secondId, hitMask}; + } + } + } + + fillCellsByLink(); + } + + View getView() const + { + return View{mLinks.data(), + mCells.data(), + mCellsByFirstLinkIndex.data(), + mCellsByFirstLink.data(), + mSeedingLayerMask, + mNLinks, + mNCells, + mNCellsByFirstLink}; + } + + View getDeviceView(const LayerLink* deviceLinks, + const CellTopology* deviceCells, + const Range* deviceCellsByFirstLinkIndex, + const Id* deviceCellsByFirstLink) const + { + return View{deviceLinks, + deviceCells, + deviceCellsByFirstLinkIndex, + deviceCellsByFirstLink, + mSeedingLayerMask, + mNLinks, + mNCells, + mNCellsByFirstLink}; + } + + const auto& getLinks() const noexcept { return mLinks; } + const auto& getCells() const noexcept { return mCells; } + const auto& getCellsByFirstLinkIndex() const noexcept { return mCellsByFirstLinkIndex; } + const auto& getCellsByFirstLink() const noexcept { return mCellsByFirstLink; } + Id getNLinks() const noexcept { return mNLinks; } + Id getNCells() const noexcept { return mNCells; } + Id getNCellsByFirstLink() const noexcept { return mNCellsByFirstLink; } + + private: + void clear() + { + mNLinks = 0; + mNCells = 0; + mNCellsByFirstLink = 0; + mLinks.fill({}); + mCells.fill({}); + mCellsByFirstLinkIndex.fill(Range{0, 0}); + mCellsByFirstLink.fill(0); + } + + void fillCellsByLink() + { + std::array counts{}; + for (Id cellId = 0; cellId < mNCells; ++cellId) { + ++counts[mCells[cellId].firstLink]; + } + + Id offset = 0; + for (Id linkId = 0; linkId < mNLinks; ++linkId) { + mCellsByFirstLinkIndex[linkId].setFirstEntry(offset); + mCellsByFirstLinkIndex[linkId].setEntries(counts[linkId]); + offset += counts[linkId]; + } + + std::array cursor{}; + for (Id cellId = 0; cellId < mNCells; ++cellId) { + const Id linkId = mCells[cellId].firstLink; + mCellsByFirstLink[mCellsByFirstLinkIndex[linkId].getFirstEntry() + cursor[linkId]++] = cellId; + } + mNCellsByFirstLink = offset; + } + + bool isAllowedSeedingLink(int fromLayer, int toLayer) const noexcept + { + return (Mask::skipped(fromLayer, toLayer) & mSeedingLayerMask).isAllowedHoleMask(mMaxHoles, mHoleLayerMask); + } + + int mMaxLayers{0}; + int mMaxHoles{0}; + Mask mHoleLayerMask{0}; + Mask mSeedingLayerMask{0}; + Id mNLinks{0}; + Id mNCells{0}; + Id mNCellsByFirstLink{0}; + std::array mLinks{}; + std::array mCells{}; + std::array mCellsByFirstLinkIndex{}; + std::array mCellsByFirstLink{}; +}; + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h index a7f7adbb8abad..829fe9fa984e4 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -13,138 +13,62 @@ /// \brief /// -#ifndef TRACKINGITSU_INCLUDE_TRACKLET_H_ -#define TRACKINGITSU_INCLUDE_TRACKLET_H_ +#ifndef TRACKINGITS_INCLUDE_TRACKLET_H_ +#define TRACKINGITS_INCLUDE_TRACKLET_H_ +#include "ITStracking/Constants.h" +#include "DataFormatsITS/TimeEstBC.h" #include "ITStracking/Cluster.h" -#include +#include "GPUCommonRtypes.h" #include "GPUCommonMath.h" #include "GPUCommonDef.h" +#include "GPUCommonLogger.h" -namespace o2 -{ -namespace its +namespace o2::its { +// tracklets are entirely determined by their two cluster idx struct Tracklet final { - GPUhdi() Tracklet(); - GPUhdi() Tracklet(const int, const int, const Cluster&, const Cluster&, int rof0, int rof1); - GPUhdi() Tracklet(const int, const int, float tanL, float phi, int rof0, int rof1); - GPUhdi() bool operator==(const Tracklet&) const; - GPUhdi() bool operator!=(const Tracklet&) const; - GPUhdi() unsigned char isEmpty() const + GPUhdDefault() Tracklet() = default; + GPUhdi() Tracklet(const int firstClusterOrderingIndex, const int secondClusterOrderingIndex, + const Cluster& firstCluster, const Cluster& secondCluster, const TimeEstBC& t) + : firstClusterIndex(firstClusterOrderingIndex), + secondClusterIndex(secondClusterOrderingIndex), + tanLambda((firstCluster.zCoordinate - secondCluster.zCoordinate) / (firstCluster.radius - secondCluster.radius)), + phi(o2::gpu::GPUCommonMath::ATan2(firstCluster.yCoordinate - secondCluster.yCoordinate, firstCluster.xCoordinate - secondCluster.xCoordinate)), + mTime(t) {} + + GPUhdi() Tracklet(const int idx0, const int idx1, float tanL, float phi, const TimeEstBC& t) + : firstClusterIndex(idx0), + secondClusterIndex(idx1), + tanLambda(tanL), + phi(phi), + mTime(t) {} + GPUhdi() bool operator<(const Tracklet& o) const noexcept { - return firstClusterIndex < 0 || secondClusterIndex < 0; + return (firstClusterIndex != o.firstClusterIndex) ? firstClusterIndex < o.firstClusterIndex : secondClusterIndex < o.secondClusterIndex; } - GPUhdi() void dump(); - GPUhdi() void dump() const; - GPUhdi() void dump(const int, const int); - GPUhdi() void dump(const int, const int) const; - GPUhdi() unsigned char operator<(const Tracklet&) const; - - int firstClusterIndex; - int secondClusterIndex; - float tanLambda; - float phi; - unsigned short rof[2]; -}; - -GPUhdi() Tracklet::Tracklet() : firstClusterIndex{-1}, secondClusterIndex{-1}, tanLambda{0.0f}, phi{0.0f} -{ - rof[0] = 0; - rof[1] = 0; -} - -GPUhdi() Tracklet::Tracklet(const int firstClusterOrderingIndex, const int secondClusterOrderingIndex, - const Cluster& firstCluster, const Cluster& secondCluster, int rof0 = -1, int rof1 = -1) - : firstClusterIndex{firstClusterOrderingIndex}, - secondClusterIndex{secondClusterOrderingIndex}, - tanLambda{(firstCluster.zCoordinate - secondCluster.zCoordinate) / - (firstCluster.radius - secondCluster.radius)}, - phi{o2::gpu::GPUCommonMath::ATan2(firstCluster.yCoordinate - secondCluster.yCoordinate, - firstCluster.xCoordinate - secondCluster.xCoordinate)}, - rof{static_cast(rof0), static_cast(rof1)} -{ - // Nothing to do -} - -GPUhdi() Tracklet::Tracklet(const int idx0, const int idx1, float tanL, float phi, int rof0, int rof1) - : firstClusterIndex{idx0}, - secondClusterIndex{idx1}, - tanLambda{tanL}, - phi{phi}, - rof{static_cast(rof0), static_cast(rof1)} -{ - // Nothing to do -} - -GPUhdi() bool Tracklet::operator==(const Tracklet& rhs) const -{ - return this->firstClusterIndex == rhs.firstClusterIndex && - this->secondClusterIndex == rhs.secondClusterIndex && - this->tanLambda == rhs.tanLambda && - this->phi == rhs.phi && - this->rof[0] == rhs.rof[0] && - this->rof[1] == rhs.rof[1]; -} - -GPUhdi() bool Tracklet::operator!=(const Tracklet& rhs) const -{ - return this->firstClusterIndex != rhs.firstClusterIndex || - this->secondClusterIndex != rhs.secondClusterIndex || - this->tanLambda != rhs.tanLambda || - this->phi != rhs.phi; -} - -GPUhdi() unsigned char Tracklet::operator<(const Tracklet& t) const -{ - if (isEmpty()) { - return false; + GPUhdi() bool operator==(const Tracklet& o) const noexcept + { + return firstClusterIndex == o.firstClusterIndex && secondClusterIndex == o.secondClusterIndex; } - return true; -} - -// GPUhdi() void Tracklet::dump() -// { -// printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu phi: %f tl: %f \n", firstClusterIndex, secondClusterIndex, rof[0], rof[1], phi, tanLambda); -// } - -// GPUhdi() void Tracklet::dump() const -// { -// printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu phi: %f tl: %f \n", firstClusterIndex, secondClusterIndex, rof[0], rof[1], phi, tanLambda); -// } - -// GPUhdi() void Tracklet::dump(const int offsetFirst, const int offsetSecond) -// { -// printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu phi: %f tl: %f \n", firstClusterIndex + offsetFirst, secondClusterIndex + offsetSecond, rof[0], rof[1], phi, tanLambda); -// } - -// GPUhdi() void Tracklet::dump(const int offsetFirst, const int offsetSecond) const -// { -// printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu phi: %f tl: %f \n", firstClusterIndex + offsetFirst, secondClusterIndex + offsetSecond, rof[0], rof[1], phi, tanLambda); -// } - -GPUhdi() void Tracklet::dump(const int offsetFirst, const int offsetSecond) -{ - printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu\n", firstClusterIndex + offsetFirst, secondClusterIndex + offsetSecond, rof[0], rof[1]); -} + GPUhdi() bool isCompatible(const Tracklet& o) const { return mTime.isCompatible(o.mTime); } + GPUhd() void print() const + { + LOGP(info, "TRKLT: fClIdx:{} sClIdx:{} ts:{}+/-{} TgL={} Phi={}", firstClusterIndex, secondClusterIndex, mTime.getTimeStamp(), mTime.getTimeStampError(), tanLambda, phi); + } + GPUhd() auto& getTimeStamp() noexcept { return mTime; } + GPUhd() const auto& getTimeStamp() const noexcept { return mTime; } -GPUhdi() void Tracklet::dump(const int offsetFirst, const int offsetSecond) const -{ - printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu\n", firstClusterIndex + offsetFirst, secondClusterIndex + offsetSecond, rof[0], rof[1]); -} + int firstClusterIndex{constants::UnusedIndex}; + int secondClusterIndex{constants::UnusedIndex}; + float tanLambda{constants::UnsetValue}; + float phi{constants::UnsetValue}; + TimeEstBC mTime; -GPUhdi() void Tracklet::dump() -{ - printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu\n", firstClusterIndex, secondClusterIndex, rof[0], rof[1]); -} - -GPUhdi() void Tracklet::dump() const -{ - printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu\n", firstClusterIndex, secondClusterIndex, rof[0], rof[1]); -} + ClassDefNV(Tracklet, 1); +}; -} // namespace its -} // namespace o2 +} // namespace o2::its -#endif /* TRACKINGITSU_INCLUDE_TRACKLET_H_ */ +#endif /* TRACKINGITS_INCLUDE_TRACKLET_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h index 6e245966c318f..c899dde24ed44 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -21,107 +21,116 @@ #include #include #include +#include +#include + +#include -#include "ITStracking/ROframe.h" #include "ITStracking/Constants.h" #include "ITStracking/Configuration.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/VertexerTraits.h" -#include "ReconstructionDataFormats/Vertex.h" - -#include "ITStracking/ClusterLines.h" -#include "ITStracking/Tracklet.h" -#include "ITStracking/Cluster.h" - -#include "GPUCommonLogger.h" +#include "ITStracking/BoundedAllocator.h" -class TTree; - -namespace o2 -{ -namespace its +namespace o2::its { -using TimeFrame = o2::its::TimeFrame; -using Vertex = o2::dataformats::Vertex>; +template class Vertexer { + using TimeFrameN = TimeFrame; + using VertexerTraitsN = VertexerTraits; + using LogFunc = std::function; + public: - Vertexer(VertexerTraits* traits); + Vertexer(VertexerTraitsN* traits); virtual ~Vertexer() = default; Vertexer(const Vertexer&) = delete; Vertexer& operator=(const Vertexer&) = delete; - void adoptTimeFrame(TimeFrame& tf); - VertexingParameters& getVertParameters() const; - void getGlobalConfiguration(); - - std::vector exportVertices(); - VertexerTraits* getTraits() const { return mTraits; }; + void adoptTimeFrame(TimeFrameN& tf); + auto& getVertParameters() const { return mTraits->getVertexingParameters(); } + void setParameters(const std::vector& vertParams) { mVertParams = vertParams; } + const auto& getParameters() const noexcept { return mVertParams; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } - float clustersToVertices(std::function = [](std::string s) { std::cout << s << std::endl; }); + float clustersToVertices(LogFunc = [](const std::string& s) { std::cout << s << '\n'; }); void filterMCTracklets(); - void validateTracklets(); + void printSummary() const; template - void findTracklets(T&&... args); + void findTracklets(T&&... args) + { + mTraits->computeTracklets(std::forward(args)...); + } + template + void validateTracklets(T&&... args) + { + mTraits->computeTrackletMatching(std::forward(args)...); + } + template + void findVertices(T&&... args) + { + mTraits->computeVertices(std::forward(args)...); + } - void findTrivialMCTracklets(); - void findVertices(); - void findHistVertices(); + void addTruthSeeds() { mTraits->addTruthSeedingVertices(); } template - void initialiseVertexer(T&&... args); + void initialiseVertexer(T&&... args) + { + mTraits->initialise(std::forward(args)...); + } template void initialiseTimeFrame(T&&... args); + void sortVertices(); + // Utils - void dumpTraits(); template - float evaluateTask(void (Vertexer::*)(T...), const char*, std::function logger, T&&... args); - void printEpilog(std::function logger, const float total); + float evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args); + + void printEpilog(LogFunc& logger, + unsigned int trackletN01, unsigned int trackletN12, + unsigned selectedN, unsigned int vertexN, unsigned int totalVertexN, + float initT, float trackletT, float selecT, float vertexT); + + void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } private: std::uint32_t mTimeFrameCounter = 0; - VertexerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class - TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class + VertexerTraitsN* mTraits = nullptr; /// Observer pointer, not owned by this class + TimeFrameN* mTimeFrame = nullptr; /// Observer pointer, not owned by this class + + std::vector mVertParams; + std::shared_ptr mMemoryPool; + + enum Steps { + Init = 0, + Trackleting, + Selection, + Finding, + TruthSeeding, + NSteps, + }; + Steps mCurStep{Init}; + static constexpr std::array StateNames{"Initialisation", "Tracklet finding", "Tracklet selection", "Vertex finding", "Truth seeding"}; + std::vector> mTimingStats; + void addTimingStatCurStep(int iteration, double timeMs); }; +template template -void Vertexer::initialiseVertexer(T&&... args) -{ - mTraits->initialise(std::forward(args)...); -} - -template -void Vertexer::findTracklets(T&&... args) -{ - mTraits->computeTracklets(std::forward(args)...); -} - -inline VertexingParameters& Vertexer::getVertParameters() const -{ - return mTraits->getVertexingParameters(); -} - -inline void Vertexer::dumpTraits() -{ - mTraits->dumpVertexerTraits(); -} - -inline void Vertexer::validateTracklets() -{ - mTraits->computeTrackletMatching(); -} - -template -float Vertexer::evaluateTask(void (Vertexer::*task)(T...), const char* taskName, std::function logger, - T&&... args) +float Vertexer::evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args) { float diff{0.f}; - if (constants::DoTimeBenchmarks) { + if (mVertParams[iteration].PrintMemory) { + mMemoryPool->resetPeakMemory(); + } + + if constexpr (constants::DoTimeBenchmarks) { auto start = std::chrono::high_resolution_clock::now(); (this->*task)(std::forward(args)...); auto end = std::chrono::high_resolution_clock::now(); @@ -130,19 +139,33 @@ float Vertexer::evaluateTask(void (Vertexer::*task)(T...), const char* taskName, diff = diff_t.count(); std::stringstream sstream; - if (taskName == nullptr) { + if (taskName.empty()) { sstream << diff << "\t"; } else { sstream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms"; } logger(sstream.str()); + + if (mVertParams[iteration].SaveTimeBenchmarks) { + std::string taskNameStr(taskName); + std::transform(taskNameStr.begin(), taskNameStr.end(), taskNameStr.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::replace(taskNameStr.begin(), taskNameStr.end(), ' ', '_'); + if (std::ofstream file{"its_time_benchmarks.txt", std::ios::app}) { + file << "vtx:" << iteration << '\t' << taskNameStr << '\t' << diff << '\n'; + } + addTimingStatCurStep(iteration, diff); + } } else { (this->*task)(std::forward(args)...); } + if (mVertParams[iteration].PrintMemory) { + LOGP(info, "iter:{}:{}: {}", iteration, StateNames[mCurStep], mMemoryPool->asString()); + } + return diff; } -} // namespace its -} // namespace o2 +} // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h index 4a86dbf29c1eb..daf8d708e1e23 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h @@ -17,9 +17,11 @@ #define O2_ITS_TRACKING_VERTEXER_TRAITS_H_ #include +#include #include #include +#include "ITStracking/BoundedAllocator.h" #include "ITStracking/Cluster.h" #include "ITStracking/ClusterLines.h" #include "ITStracking/Configuration.h" @@ -27,127 +29,88 @@ #include "ITStracking/IndexTableUtils.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/Tracklet.h" +#include "ITStracking/MathUtils.h" #include "GPUCommonDef.h" #include "GPUCommonMath.h" +#include + namespace o2 { class MCCompLabel; namespace its { -class ROframe; -using constants::its::LayersNumberVertexer; - -enum class TrackletMode { - Layer0Layer1 = 0, - Layer1Layer2 = 2 -}; +template class VertexerTraits { + using IndexTableUtilsN = IndexTableUtils; + using TimeFrameN = TimeFrame; + public: VertexerTraits() = default; virtual ~VertexerTraits() = default; - GPUhd() static constexpr int4 getEmptyBinsRect() - { - return int4{0, 0, 0, 0}; - } - GPUhd() const int4 getBinsRect(const Cluster&, const int, const float, float maxdeltaz, float maxdeltaphi); - GPUhd() const int2 getPhiBins(float phi, float deltaPhi); - - GPUhd() static const int4 getBinsRect(const Cluster&, const int, const float, float maxdeltaz, float maxdeltaphi, const IndexTableUtils&); - GPUhd() static const int2 getPhiBins(float phi, float deltaPhi, const IndexTableUtils&); - // virtual vertexer interface virtual void initialise(const TrackingParameters& trackingParams); - virtual void computeTracklets(); - virtual void computeTrackletMatching(); - virtual void computeVertices(); - virtual void adoptTimeFrame(TimeFrame* tf); - virtual void updateVertexingParameters(const VertexingParameters& vrtPar, const TimeFrameGPUParameters& gpuTfPar); - - void computeVerticesInRof(int, - gsl::span&, - std::vector&, - std::vector&, - std::array&, - std::vector&, - std::vector&, - TimeFrame*, - std::vector*); - - VertexingParameters getVertexingParameters() const { return mVrtParams; } - static const std::vector> selectClusters(const int* indexTable, - const std::array& selectedBinsRect, - const IndexTableUtils& utils); + virtual void computeTracklets(const int iteration); + virtual void computeTrackletMatching(const int iteration); + virtual void computeVertices(const int iteration); + virtual void adoptTimeFrame(TimeFrameN* tf) noexcept { mTimeFrame = tf; } + virtual void updateVertexingParameters(const std::vector& vrtPar); + + // truth tracking + void addTruthSeedingVertices(); // utils - VertexingParameters& getVertexingParameters() { return mVrtParams; } - void setIsGPU(const unsigned char isgpu) { mIsGPU = isgpu; }; - unsigned char getIsGPU() const { return mIsGPU; }; - void dumpVertexerTraits(); - void setNThreads(int n); - int getNThreads() const { return mNThreads; } + auto& getVertexingParameters() { return mVrtParams; } + auto getVertexingParameters() const { return mVrtParams; } + void setVertexingParameters(std::vector& vertParams) { mVrtParams = vertParams; } + void setNThreads(int n, std::shared_ptr& arena); + int getNThreads() { return mTaskArena->max_concurrency(); } + virtual bool isGPU() const noexcept { return false; } + virtual const char* getName() const noexcept { return "CPU"; } + virtual bool usesMemoryPool() const noexcept { return true; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } + + static VertexLabel computeMain(const bounded_vector& elements) + { + // we only care about the source&event of the tracks, not the trackId + auto composeVtxLabel = [](const o2::MCCompLabel& lbl) -> o2::MCCompLabel { + return {o2::MCCompLabel::maxTrackID(), lbl.getEventID(), lbl.getSourceID(), lbl.isFake()}; + }; + std::unordered_map frequency; + for (const auto& element : elements) { + ++frequency[composeVtxLabel(element)]; + } + o2::MCCompLabel elem{}; + size_t maxCount = 0; + for (const auto& [key, count] : frequency) { + if (count > maxCount) { + maxCount = count; + elem = key; + } + } + if (maxCount <= 1) { // need >50% + elem.setFakeFlag(); + } + return std::make_pair(elem, static_cast(maxCount) / static_cast(elements.size())); + } protected: - unsigned char mIsGPU; - int mNThreads = 1; - - VertexingParameters mVrtParams; - IndexTableUtils mIndexTableUtils; - std::vector mVertices; + std::vector mVrtParams; + IndexTableUtilsN mIndexTableUtils; // Frame related quantities - TimeFrame* mTimeFrame = nullptr; -}; + TimeFrameN* mTimeFrame = nullptr; // observer ptr + private: + bool skipROF(int iteration, int rof) const; -inline void VertexerTraits::initialise(const TrackingParameters& trackingParams) -{ - mTimeFrame->initialise(0, trackingParams, 3); - setIsGPU(false); -} - -GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi) -{ - return VertexerTraits::getPhiBins(phi, dPhi, mIndexTableUtils); -} - -GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi, const IndexTableUtils& utils) -{ - return int2{utils.getPhiBinIndex(math_utils::getNormalizedPhi(phi - dPhi)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phi + dPhi))}; -} - -GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, - const float directionZIntersection, float maxdeltaz, float maxdeltaphi, - const IndexTableUtils& utils) -{ - const float zRangeMin = directionZIntersection - 2 * maxdeltaz; - const float phiRangeMin = currentCluster.phi - maxdeltaphi; - const float zRangeMax = directionZIntersection + 2 * maxdeltaz; - const float phiRangeMax = currentCluster.phi + maxdeltaphi; - - if (zRangeMax < -utils.getLayerZ(layerIndex + 1) || - zRangeMin > utils.getLayerZ(layerIndex + 1) || zRangeMin > zRangeMax) { - return getEmptyBinsRect(); - } - - return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), - o2::gpu::GPUCommonMath::Min(utils.getNzBins() - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; -} - -GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, - const float directionZIntersection, float maxdeltaz, float maxdeltaphi) -{ - return VertexerTraits::getBinsRect(currentCluster, layerIndex, directionZIntersection, maxdeltaz, maxdeltaphi, mIndexTableUtils); -} - -inline void VertexerTraits::adoptTimeFrame(TimeFrame* tf) { mTimeFrame = tf; } + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; +}; } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/json.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/json.h deleted file mode 100644 index d1d246b329907..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/json.h +++ /dev/null @@ -1,16313 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/* - __ _____ _____ _____ - __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.1.2 -|_____|_____|_____|_|___| https://github.com/nlohmann/json - -Licensed under the MIT License . -Copyright (c) 2013-2018 Niels Lohmann . - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -#ifndef NLOHMANN_JSON_HPP -#define NLOHMANN_JSON_HPP - -#define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 1 -#define NLOHMANN_JSON_VERSION_PATCH 2 - -#include // all_of, find, for_each -#include // assert -#include // and, not, or -#include // nullptr_t, ptrdiff_t, size_t -#include // hash, less -#include // initializer_list -#include // istream, ostream -#include // iterator_traits, random_access_iterator_tag -#include // accumulate -#include // string, stoi, to_string -#include // declval, forward, move, pair, swap - -// #include -#ifndef NLOHMANN_JSON_FWD_HPP -#define NLOHMANN_JSON_FWD_HPP - -#include // int64_t, uint64_t -#include // map -#include // allocator -#include // string -#include // vector - -/*! -@brief namespace for Niels Lohmann -@see https://github.com/nlohmann -@since version 1.0.0 -*/ -namespace nlohmann -{ -/*! -@brief default JSONSerializer template argument - -This serializer ignores the template arguments and uses ADL -([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) -for serialization. -*/ -template -struct adl_serializer; - -template